Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Drop support for Python 2 #1482

Merged
merged 22 commits into from
Dec 27, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
ff09365
Remove absolute_import and print_function imports from __future__
Harmon758 Dec 26, 2020
bb07b95
Remove u prefix from strings
Harmon758 Dec 26, 2020
80b148f
Remove input import from six.moves
Harmon758 Dec 26, 2020
ac0d28a
Update HTTPConnection import
Harmon758 Dec 26, 2020
15b78c6
Update urllib.parse.parse_qs import
Harmon758 Dec 26, 2020
e36cf1f
Update urllib.parse.quote and urllib.parse.urlencode imports
Harmon758 Dec 26, 2020
bcc04e4
Remove no longer used variable in streaming tests
Harmon758 Dec 26, 2020
dc1a9c8
Replace six.BytesIO with io.BytesIO
Harmon758 Dec 26, 2020
e0f0065
Replace six.b with b prefix for bytes literals
Harmon758 Dec 26, 2020
c3d72c1
Replace six.text_type with str
Harmon758 Dec 26, 2020
4598534
Remove subclassing of object
Harmon758 Dec 26, 2020
4d31c1b
Remove unnecessary attributes for super
Harmon758 Dec 26, 2020
bbdf078
Replace six.reraise
Harmon758 Dec 26, 2020
e35bf64
Remove six requirement
Harmon758 Dec 26, 2020
5eccd6c
Remove Python 2.7 from GitHub Actions Test workflow
Harmon758 Dec 26, 2020
553d797
Update Python requirement to >= 3.5
Harmon758 Dec 26, 2020
9d85a70
Update setup classifiers to be only Python 3
Harmon758 Dec 26, 2020
3e5a1de
Specify support for Python 3.5 - 3.9 in README
Harmon758 Dec 26, 2020
4cd73a9
Remove cPickle import attempt
Harmon758 Dec 26, 2020
4f66f7b
Remove Python 2.7 from tox configuration
Harmon758 Dec 26, 2020
c1861ef
Use next built-in function in place of next method for iterators
Harmon758 Dec 26, 2020
4e6c780
Update documentation for sep parameter for ReadBuffer.read_line
Harmon758 Dec 26, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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 @@ -1424,7 +1422,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