Skip to content

Commit

Permalink
Merge pull request #1482 from tweepy/python-2-support-removal
Browse files Browse the repository at this point in the history
Drop support for Python 2
  • Loading branch information
Harmon758 committed Dec 27, 2020
2 parents 9382fe2 + 4e6c780 commit ebcc04e
Show file tree
Hide file tree
Showing 23 changed files with 66 additions and 102 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [2.7, 3.5, 3.6, 3.7, 3.8, 3.9]
python-version: [3.5, 3.6, 3.7, 3.8, 3.9]

steps:
- uses: actions/checkout@v2
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Alternatively, install directly from the GitHub repository:

pip install git+https://github.com/tweepy/tweepy.git

Python 2.7, 3.5, 3.6, 3.7, 3.8, & 3.9 are supported.
Python 3.5 - 3.9 are supported.

Community
---------
Expand Down
4 changes: 2 additions & 2 deletions docs/code_snippet.rst
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ This snippet will follow every follower of the authenticated user.
Handling the rate limit using cursors
=====================================

Since cursors raise ``RateLimitError``\ s in their ``next()`` method,
Since cursors raise ``RateLimitError``\ s while iterating,
handling them can be done by wrapping the cursor in an iterator.

Running this snippet will print all users you follow that themselves follow
Expand All @@ -70,7 +70,7 @@ will wait for 15 minutes each time it hits the rate limit.
def limit_handled(cursor):
while True:
try:
yield cursor.next()
yield next(cursor)
except tweepy.RateLimitError:
time.sleep(15 * 60)
Expand Down
8 changes: 4 additions & 4 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@
master_doc = 'index'

# General information about the project.
project = u'tweepy'
copyright = u'2009-2020, Joshua Roesslein'
project = 'tweepy'
copyright = '2009-2020, Joshua Roesslein'

# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
Expand Down Expand Up @@ -179,8 +179,8 @@
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('index', 'tweepy.tex', u'tweepy Documentation',
u'Joshua Roesslein', 'manual'),
('index', 'tweepy.tex', 'tweepy Documentation',
'Joshua Roesslein', 'manual'),
]

# The name of an image file (relative to this directory) to place at the top of
Expand Down
2 changes: 0 additions & 2 deletions examples/oauth.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from __future__ import absolute_import, print_function

import tweepy

# == OAuth Authentication ==
Expand Down
2 changes: 0 additions & 2 deletions examples/streaming.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from __future__ import absolute_import, print_function

from tweepy import OAuthHandler, Stream, StreamListener

# Go to http://apps.twitter.com and create an app.
Expand Down
6 changes: 2 additions & 4 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@
install_requires=[
"requests[socks]>=2.11.1",
"requests_oauthlib>=0.7.0",
"six>=1.10.0",
],
tests_require=tests_require,
extras_require={
Expand All @@ -54,21 +53,20 @@
},
test_suite="nose.collector",
keywords="twitter library",
python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*",
python_requires=">=3.5",
classifiers=[
"Development Status :: 5 - Production/Stable",
"Topic :: Software Development :: Libraries",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
"Programming Language :: Python",
"Programming Language :: Python :: 2",
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3 :: Only",
],
zip_safe=True,
)
4 changes: 0 additions & 4 deletions tests/test_auth.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
from __future__ import absolute_import

import random
import unittest

from six.moves import input

from .config import *
from tweepy import API, OAuthHandler

Expand Down
4 changes: 2 additions & 2 deletions tests/test_cursors.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,10 @@ def testcursorsetstartcursor(self):
@tape.use_cassette('testcursornext.json')
def testcursornext(self):
"""
Test cursor.next() behavior, id being passed correctly.
Test next(cursor) behavior, id being passed correctly.
Regression test for issue #518
"""
cursor = Cursor(self.api.user_timeline, id='Twitter').items(5)
status = cursor.next()
status = next(cursor)

self.assertEqual(status.user.screen_name, 'Twitter')
4 changes: 2 additions & 2 deletions tests/test_resultset.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

from tweepy.models import ResultSet

class NoIdItem(object): pass
class NoIdItem: pass

class IdItem(object):
class IdItem:
def __init__(self, id):
self.id = id

Expand Down
32 changes: 12 additions & 20 deletions tests/test_streaming.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
from __future__ import absolute_import, print_function

import io
import unittest
from unittest.case import skip

from mock import MagicMock, patch
import six

from .config import create_auth
from .test_utils import mock_tweet
Expand All @@ -14,15 +12,9 @@
from tweepy.streaming import ReadBuffer, Stream, StreamListener


if six.PY3:
getresponse_location = 'http.client.HTTPConnection.getresponse'
else:
getresponse_location = 'httplib.HTTPConnection.getresponse'


class MockStreamListener(StreamListener):
def __init__(self, test_case):
super(MockStreamListener, self).__init__()
super().__init__()
self.test_case = test_case
self.status_count = 0
self.status_stop_count = 0
Expand Down Expand Up @@ -100,27 +92,27 @@ def test_filter_track(self):
def test_track_encoding(self):
s = Stream(None, None)
s._start = lambda is_async: None
s.filter(track=[u'Caf\xe9'])
s.filter(track=['Caf\xe9'])

# Should be UTF-8 encoded
self.assertEqual(u'Caf\xe9'.encode('utf8'), s.body['track'])
self.assertEqual('Caf\xe9'.encode('utf8'), s.body['track'])

def test_follow_encoding(self):
s = Stream(None, None)
s._start = lambda is_async: None
s.filter(follow=[u'Caf\xe9'])
s.filter(follow=['Caf\xe9'])

# Should be UTF-8 encoded
self.assertEqual(u'Caf\xe9'.encode('utf8'), s.body['follow'])
self.assertEqual('Caf\xe9'.encode('utf8'), s.body['follow'])


class TweepyStreamReadBufferTests(unittest.TestCase):

stream = six.b("""11\n{id:12345}\n\n24\n{id:23456, test:"blah"}\n""")
stream = b"""11\n{id:12345}\n\n24\n{id:23456, test:"blah"}\n"""

def test_read_tweet(self):
for length in [1, 2, 5, 10, 20, 50]:
buf = ReadBuffer(six.BytesIO(self.stream), length)
buf = ReadBuffer(io.BytesIO(self.stream), length)
self.assertEqual('11\n', buf.read_line())
self.assertEqual('{id:12345}\n', buf.read_len(11))
self.assertEqual('\n', buf.read_line())
Expand Down Expand Up @@ -151,7 +143,7 @@ def on_read(chunk_size):
return ""

# Create a fake stream
stream = six.BytesIO(six.b(''))
stream = io.BytesIO(b'')

# Mock it's read function so it can't be called too many times
mock_read = MagicMock(side_effect=on_read)
Expand All @@ -170,14 +162,14 @@ def on_read(chunk_size):
self.assertEqual(mock_read.call_count, 0)

def test_read_unicode_tweet(self):
stream = six.b('11\n{id:12345}\n\n23\n{id:23456, test:"\xe3\x81\x93"}\n\n')
stream = b'11\n{id:12345}\n\n23\n{id:23456, test:"\xe3\x81\x93"}\n\n'
for length in [1, 2, 5, 10, 20, 50]:
buf = ReadBuffer(six.BytesIO(stream), length)
buf = ReadBuffer(io.BytesIO(stream), length)
self.assertEqual('11\n', buf.read_line())
self.assertEqual('{id:12345}\n', buf.read_len(11))
self.assertEqual('\n', buf.read_line())
self.assertEqual('23\n', buf.read_line())
self.assertEqual(u'{id:23456, test:"\u3053"}\n', buf.read_len(23))
self.assertEqual('{id:23456, test:"\u3053"}\n', buf.read_len(23))


class TweepyStreamBackoffTests(unittest.TestCase):
Expand Down
2 changes: 1 addition & 1 deletion tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# and then run "tox" from this directory.

[tox]
envlist = py27, py35, py36, py37, py38, py39
envlist = py35, py36, py37, py38, py39

[testenv]
commands = python setup.py nosetests
Expand Down
2 changes: 1 addition & 1 deletion tweepy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,5 @@
api = API()

def debug(enable=True, level=1):
from six.moves.http_client import HTTPConnection
from http.client import HTTPConnection
HTTPConnection.debuglevel = level
6 changes: 2 additions & 4 deletions tweepy/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,13 @@
import mimetypes
import os

import six

from tweepy.binder import bind_api, pagination
from tweepy.error import TweepError
from tweepy.parsers import ModelParser, Parser
from tweepy.utils import list_to_csv


class API(object):
class API:
"""Twitter API"""

def __init__(self, auth_handler=None,
Expand Down Expand Up @@ -1412,7 +1410,7 @@ def _pack_image(filename, max_size, form_field='image', f=None, file_type=None):
elif file_type not in ['image/gif', 'image/jpeg', 'image/png']:
raise TweepError('Invalid file type for image: %s' % file_type)

if isinstance(filename, six.text_type):
if isinstance(filename, str):
filename = filename.encode('utf-8')

BOUNDARY = b'Tw3ePy'
Expand Down
9 changes: 4 additions & 5 deletions tweepy/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,9 @@
import logging

import requests
import six
from requests.auth import AuthBase
from requests_oauthlib import OAuth1, OAuth1Session
from six.moves.urllib.parse import parse_qs
from urllib.parse import parse_qs

from tweepy.api import API
from tweepy.error import TweepError
Expand All @@ -20,7 +19,7 @@
log = logging.getLogger(__name__)


class AuthHandler(object):
class AuthHandler:

def apply_auth(self, url, method, headers, parameters):
"""Apply authentication headers to request"""
Expand All @@ -37,10 +36,10 @@ class OAuthHandler(AuthHandler):
OAUTH_ROOT = '/oauth/'

def __init__(self, consumer_key, consumer_secret, callback=None):
if type(consumer_key) == six.text_type:
if type(consumer_key) == str:
consumer_key = consumer_key.encode('ascii')

if type(consumer_secret) == six.text_type:
if type(consumer_secret) == str:
consumer_secret = consumer_secret.encode('ascii')

self.consumer_key = consumer_key
Expand Down
7 changes: 3 additions & 4 deletions tweepy/binder.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@
import time

import requests
import six
from six.moves.urllib.parse import quote, urlencode
from urllib.parse import quote, urlencode

from tweepy.error import is_rate_limit_error_message, RateLimitError, TweepError
from tweepy.models import Model
Expand All @@ -22,7 +21,7 @@

def bind_api(**config):

class APIMethod(object):
class APIMethod:

api = config['api']
path = config['path']
Expand Down Expand Up @@ -189,7 +188,7 @@ def execute(self):
auth=auth,
proxies=self.api.proxy)
except Exception as e:
six.reraise(TweepError, TweepError('Failed to send request: %s' % e), sys.exc_info()[2])
raise TweepError('Failed to send request: %s' % e).with_traceback(sys.exc_info()[2])

rem_calls = resp.headers.get('x-rate-limit-remaining')

Expand Down
8 changes: 2 additions & 6 deletions tweepy/cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,11 @@
import datetime
import hashlib
import logging
import pickle
import threading
import time
import os

try:
import cPickle as pickle
except ImportError:
import pickle

try:
import fcntl
except ImportError:
Expand All @@ -24,7 +20,7 @@
log = logging.getLogger(__name__)


class Cache(object):
class Cache:
"""Cache interface"""

def __init__(self, timeout=60):
Expand Down
8 changes: 4 additions & 4 deletions tweepy/cursor.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from tweepy.parsers import ModelParser, RawParser


class Cursor(object):
class Cursor:
"""Pagination helper class"""

def __init__(self, method, *args, **kwargs):
Expand Down Expand Up @@ -39,7 +39,7 @@ def items(self, limit=0):
return i


class BaseIterator(object):
class BaseIterator:

def __init__(self, method, *args, **kwargs):
self.method = method
Expand Down Expand Up @@ -240,9 +240,9 @@ def next(self):
raise StopIteration
if self.current_page is None or self.page_index == len(self.current_page) - 1:
# Reached end of current page, get the next page...
self.current_page = self.page_iterator.next()
self.current_page = next(self.page_iterator)
while len(self.current_page) == 0:
self.current_page = self.page_iterator.next()
self.current_page = next(self.page_iterator)
self.page_index = -1
self.page_index += 1
self.num_tweets += 1
Expand Down

0 comments on commit ebcc04e

Please sign in to comment.