From 71e3b922d0760e8520407439cee57b455e46b40e Mon Sep 17 00:00:00 2001 From: Tom Most Date: Sat, 16 Jan 2021 12:56:34 -0800 Subject: [PATCH 01/18] Drop support for Python 2.7 and 3.5 --- .github/workflows/ci.yaml | 5 +---- tox.ini | 6 +++--- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 98d11b9f..51c9c787 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -63,7 +63,7 @@ jobs: continue-on-error: ${{ matrix.experimental }} strategy: matrix: - python-version: ["2.7", "3.5", "3.6", "3.7", "3.8", "3.9", "pypy-2.7", "pypy-3.7"] + python-version: ["3.6", "3.7", "3.8", "3.9", "pypy-3.7"] twisted-version: ["lowest", "latest"] experimental: [false] @@ -97,13 +97,10 @@ jobs: shell: python run: | table = { - "2.7": "py27", - "3.5": "py35", "3.6": "py36", "3.7": "py37", "3.8": "py38", "3.9": "py39", - "pypy-2.7": "pypy", "pypy-3.7": "pypy3", } factor = table["${{ matrix.python-version }}"] diff --git a/tox.ini b/tox.ini index 0f87167f..f0a2d438 100644 --- a/tox.ini +++ b/tox.ini @@ -1,8 +1,8 @@ [tox] envlist = - {pypy,py27,py35,py36,py37}-twisted_lowest, - {pypy,pypy3,py27,py35,py36,py37,py38,py39}-twisted_latest, - {pypy3,py35,py36,py37,py38,py39}-twisted_trunk, + {py36,py37}-twisted_lowest, + {pypy3,py36,py37,py38,py39}-twisted_latest, + {pypy3,py36,py37,py38,py39}-twisted_trunk, towncrier, twine, check-manifest, flake8, docs [testenv] From 4fc36765af2e1f4dc343c6ea295b6af7f96a5a4d Mon Sep 17 00:00:00 2001 From: Tom Most Date: Sat, 16 Jan 2021 13:04:29 -0800 Subject: [PATCH 02/18] Update package metadata --- setup.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/setup.py b/setup.py index 528bed28..26ac8121 100644 --- a/setup.py +++ b/setup.py @@ -7,8 +7,6 @@ "Operating System :: OS Independent", "Framework :: Twisted", "Programming Language :: Python", - "Programming Language :: Python :: 2.7", - "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", @@ -28,7 +26,7 @@ package_dir={"": "src"}, setup_requires=["incremental"], use_incremental=True, - python_requires='>=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*', + python_requires='>=3.5', install_requires=[ "incremental", "requests >= 2.1.0", From b465d0fb3ca377c333e0e91c8d7a333d1cb70cd5 Mon Sep 17 00:00:00 2001 From: Tom Most Date: Sat, 16 Jan 2021 13:07:28 -0800 Subject: [PATCH 03/18] Remove six from treq.testing --- src/treq/testing.py | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/src/treq/testing.py b/src/treq/testing.py index c05cc2e5..360697e7 100644 --- a/src/treq/testing.py +++ b/src/treq/testing.py @@ -1,12 +1,7 @@ -# -*- coding: utf-8 -*- """ In-memory version of treq for testing. """ -from __future__ import absolute_import, division, print_function - -from six import text_type, PY3 - from contextlib import contextmanager from functools import wraps @@ -120,10 +115,7 @@ def check_already_called(r): # the tcpClients list. Alternately, it will try to establish an HTTPS # connection with the reactor's connectSSL method, and MemoryReactor # will place it into the sslClients list. We'll extract that. - if PY3: - scheme = URLPath.fromBytes(uri).scheme - else: - scheme = URLPath.fromString(uri).scheme + scheme = URLPath.fromBytes(uri).scheme host, port, factory, timeout, bindAddress = ( self._memoryReactor.tcpClients[-1]) @@ -197,8 +189,8 @@ def __init__(self, body): self.body = body msg = ("StubTreq currently only supports url-encodable types, bytes, " "or unicode as data.") - assert isinstance(body, (bytes, text_type)), msg - if isinstance(body, text_type): + assert isinstance(body, (bytes, str)), msg + if isinstance(body, str): self.body = body.encode('utf-8') self.length = len(body) @@ -328,7 +320,7 @@ def _maybeEncode(someStr): """ Encode `someStr` to ASCII if required. """ - if isinstance(someStr, text_type): + if isinstance(someStr, str): return someStr.encode('ascii') return someStr From 4e740dfd5637ae0c5bf0b0a4640a2dac3dabb29c Mon Sep 17 00:00:00 2001 From: Tom Most Date: Sat, 16 Jan 2021 13:08:40 -0800 Subject: [PATCH 04/18] Remove future imports from treq.response --- src/treq/response.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/treq/response.py b/src/treq/response.py index 3689c8a2..d13c3edb 100644 --- a/src/treq/response.py +++ b/src/treq/response.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import, division, print_function - from twisted.python.components import proxyForInterface from twisted.web.iweb import IResponse, UNKNOWN_LENGTH from twisted.python import reflect From 4ccd1388c5e73bbef90322a651ebd840181d9d4b Mon Sep 17 00:00:00 2001 From: Tom Most Date: Sat, 16 Jan 2021 13:10:43 -0800 Subject: [PATCH 05/18] Remove six from treq.multipart --- src/treq/multipart.py | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/src/treq/multipart.py b/src/treq/multipart.py index e61c1ac0..eaf0baaf 100644 --- a/src/treq/multipart.py +++ b/src/treq/multipart.py @@ -1,14 +1,10 @@ # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. -from __future__ import absolute_import, division, print_function - from uuid import uuid4 from io import BytesIO from contextlib import closing -from six import integer_types, text_type - from twisted.internet import defer, task from twisted.web.iweb import UNKNOWN_LENGTH, IBodyProducer @@ -60,7 +56,7 @@ def __init__(self, fields, boundary=None, cooperator=task): self.boundary = boundary or uuid4().hex - if isinstance(self.boundary, text_type): + if isinstance(self.boundary, str): self.boundary = self.boundary.encode('ascii') self.length = self._calculateLength() @@ -169,7 +165,7 @@ def _writeLoop(self, consumer): consumer.write(CRLF + self._getBoundary(final=True) + CRLF) def _writeField(self, name, value, consumer): - if isinstance(value, text_type): + if isinstance(value, str): self._writeString(name, value, consumer) elif isinstance(value, tuple): filename, content_type, producer = value @@ -218,8 +214,8 @@ def _escape(value): a newline in the file name parameter makes form-data request unreadable for majority of parsers. """ - if not isinstance(value, (bytes, text_type)): - value = text_type(value) + if not isinstance(value, (bytes, str)): + value = str(value) if isinstance(value, bytes): value = value.decode('utf-8') return value.replace(u"\r", u"").replace(u"\n", u"").replace(u'"', u'\\"') @@ -232,14 +228,14 @@ def _enforce_unicode(value): If someone needs to pass the binary string, use BytesIO and wrap it with `FileBodyProducer`. """ - if isinstance(value, text_type): + if isinstance(value, str): return value elif isinstance(value, bytes): # we got a byte string, and we have no ide what's the encoding of it # we can only assume that it's something cool try: - return text_type(value, "utf-8") + return str(value, "utf-8") except UnicodeDecodeError: raise ValueError( "Supplied raw bytes that are not ascii/utf-8." @@ -267,7 +263,7 @@ def _converted(fields): filename = _enforce_unicode(filename) if filename else None yield name, (filename, content_type, producer) - elif isinstance(value, (bytes, text_type)): + elif isinstance(value, (bytes, str)): yield name, _enforce_unicode(value) else: @@ -300,7 +296,7 @@ def write(self, value): if value is UNKNOWN_LENGTH: self.length = value - elif isinstance(value, integer_types): + elif isinstance(value, int): self.length += value else: self.length += len(value) @@ -347,7 +343,7 @@ def _sorted_by_type(fields): """ def key(p): key, val = p - if isinstance(val, (bytes, text_type)): + if isinstance(val, (bytes, str)): return (0, key) else: return (1, key) From 288536f8e23a1a24e91b0e068b197e9bbf46996a Mon Sep 17 00:00:00 2001 From: Tom Most Date: Sat, 16 Jan 2021 13:11:23 -0800 Subject: [PATCH 06/18] Remove future imports from treq.content --- src/treq/content.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/treq/content.py b/src/treq/content.py index c6ac1406..36219f54 100644 --- a/src/treq/content.py +++ b/src/treq/content.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import, division, print_function - import cgi import json From e4e74a3a7fd6ef5e8c639f82c1e0de739176a318 Mon Sep 17 00:00:00 2001 From: Tom Most Date: Sat, 16 Jan 2021 13:24:56 -0800 Subject: [PATCH 07/18] Remove six from treq.client --- src/treq/client.py | 33 +++++++++++++-------------------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/src/treq/client.py b/src/treq/client.py index 98b56892..d701c127 100644 --- a/src/treq/client.py +++ b/src/treq/client.py @@ -1,15 +1,10 @@ -from __future__ import absolute_import, division, print_function - +import io import mimetypes import uuid import warnings - -import io - -import six -from six.moves.collections_abc import Mapping -from six.moves.http_cookiejar import CookieJar -from six.moves.urllib.parse import quote_plus, urlencode as _urlencode +from collections.abc import Mapping +from http.cookiejar import CookieJar +from urllib.parse import quote_plus, urlencode as _urlencode from twisted.internet.interfaces import IProtocol from twisted.internet.defer import Deferred @@ -42,7 +37,10 @@ def urlencode(query, doseq): - return six.ensure_binary(_urlencode(query, doseq), encoding='ascii') + s = _urlencode(query, doseq) + if not isinstance(s, bytes): + s = s.encode("ascii") + return s class _BodyBufferingProtocol(proxyForInterface(IProtocol)): @@ -156,7 +154,7 @@ def request(self, method, url, **kwargs): parsed_url = url.encoded_url elif isinstance(url, EncodedURL): parsed_url = url - elif isinstance(url, six.text_type): + elif isinstance(url, str): # We use hyperlink in lazy mode so that users can pass arbitrary # bytes in the path and querystring. parsed_url = EncodedURL.from_text(url) @@ -250,7 +248,7 @@ def _request_headers(self, headers, stacklevel): if isinstance(headers, dict): h = Headers({}) for k, v in headers.items(): - if isinstance(v, (bytes, six.text_type)): + if isinstance(v, (bytes, str)): h.addRawHeader(k, v) elif isinstance(v, list): h.setRawHeaders(k, v) @@ -432,7 +430,7 @@ def _query_quote(v): a querystring (with space as ``+``). """ if not isinstance(v, (str, bytes)): - v = six.text_type(v) + v = str(v) if not isinstance(v, bytes): v = v.encode("utf-8") q = quote_plus(v) @@ -496,10 +494,5 @@ def _guess_content_type(filename): registerAdapter(_from_bytes, bytes, IBodyProducer) registerAdapter(_from_file, io.BytesIO, IBodyProducer) -if six.PY2: - registerAdapter(_from_file, six.StringIO, IBodyProducer) - # Suppress lint failure on Python 3. - registerAdapter(_from_file, file, IBodyProducer) # noqa: F821 -else: - # file()/open() equiv on Py3 - registerAdapter(_from_file, io.BufferedReader, IBodyProducer) +# file()/open() equiv on Py3 +registerAdapter(_from_file, io.BufferedReader, IBodyProducer) From 74fd0144dfc248d5cb33c79dba1ef2ba8192ed3b Mon Sep 17 00:00:00 2001 From: Tom Most Date: Sat, 16 Jan 2021 13:43:58 -0800 Subject: [PATCH 08/18] Remove six from treq.test.test_multipart --- src/treq/test/test_multipart.py | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/treq/test/test_multipart.py b/src/treq/test/test_multipart.py index a66c059c..5a079149 100644 --- a/src/treq/test/test_multipart.py +++ b/src/treq/test/test_multipart.py @@ -1,4 +1,3 @@ -# coding: utf-8 # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. @@ -10,17 +9,12 @@ from twisted.trial import unittest from zope.interface.verify import verifyObject -from six import PY3, text_type - from twisted.internet import task from twisted.web.client import FileBodyProducer from twisted.web.iweb import UNKNOWN_LENGTH, IBodyProducer from treq.multipart import MultiPartProducer, _LengthConsumer -if PY3: - long = int - class MultiPartProducerTestCase(unittest.TestCase): """ @@ -65,7 +59,7 @@ def getOutput(self, producer, with_producer=False): def newLines(self, value): - if isinstance(value, text_type): + if isinstance(value, str): return value.replace(u"\n", u"\r\n") else: return value.replace(b"\n", b"\r\n") @@ -622,15 +616,15 @@ class LengthConsumerTestCase(unittest.TestCase): def test_scalarsUpdateCounter(self): """ - When a long or an int are written, _LengthConsumer updates its internal + When an int is written, _LengthConsumer updates its internal counter. """ consumer = _LengthConsumer() self.assertEqual(consumer.length, 0) - consumer.write(long(1)) + consumer.write(1) self.assertEqual(consumer.length, 1) consumer.write(2147483647) - self.assertEqual(consumer.length, long(2147483648)) + self.assertEqual(consumer.length, 2147483648) def test_stringUpdatesCounter(self): """ From 38d88757c7047204d7e31421a76415309c479a50 Mon Sep 17 00:00:00 2001 From: Tom Most Date: Sat, 16 Jan 2021 13:45:43 -0800 Subject: [PATCH 09/18] Remove six from treq.test.test_testing --- src/treq/test/test_testing.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/treq/test/test_testing.py b/src/treq/test/test_testing.py index 01729b87..06b6a2d1 100644 --- a/src/treq/test/test_testing.py +++ b/src/treq/test/test_testing.py @@ -6,8 +6,6 @@ from mock import ANY -from six import text_type, binary_type, PY3 - from twisted.trial.unittest import TestCase from twisted.web.client import ResponseFailed from twisted.web.error import SchemeNotSupported @@ -163,9 +161,9 @@ def test_passing_in_strange_data_is_rejected(self): self.successResultOf(stub.request('method', 'http://url', data=[])) self.successResultOf(stub.request('method', 'http://url', data=())) self.successResultOf( - stub.request('method', 'http://url', data=binary_type(b""))) + stub.request('method', 'http://url', data=b"")) self.successResultOf( - stub.request('method', 'http://url', data=text_type(""))) + stub.request('method', 'http://url', data="")) def test_handles_failing_asynchronous_requests(self): """ @@ -306,11 +304,10 @@ def test_repr(self): """ :obj:`HasHeaders` returns a nice string repr. """ - if PY3: - reprOutput = "HasHeaders({b'a': [b'b']})" - else: - reprOutput = "HasHeaders({'a': ['b']})" - self.assertEqual(reprOutput, repr(HasHeaders({b'A': [b'b']}))) + self.assertEqual( + "HasHeaders({b'a': [b'b']})", + repr(HasHeaders({b"A": [b"b"]})), + ) class StringStubbingTests(TestCase): From 22bdb399a03b0eebaa93023b80d348f584f567d5 Mon Sep 17 00:00:00 2001 From: Tom Most Date: Sat, 16 Jan 2021 13:48:13 -0800 Subject: [PATCH 10/18] Remove six dependency --- setup.py | 1 - 1 file changed, 1 deletion(-) diff --git a/setup.py b/setup.py index 26ac8121..34f85cdb 100644 --- a/setup.py +++ b/setup.py @@ -31,7 +31,6 @@ "incremental", "requests >= 2.1.0", "hyperlink >= 21.0.0", - "six >= 1.13.0", "Twisted[tls] >= 18.7.0", "attrs", ], From 6b72f6c6d6eca5cd4e60b3819136b01c2c513438 Mon Sep 17 00:00:00 2001 From: Tom Most Date: Sat, 16 Jan 2021 13:50:33 -0800 Subject: [PATCH 11/18] Use stdlib mock --- docs/testing.rst | 6 ++++-- src/treq/test/test_client.py | 3 +-- src/treq/test/test_content.py | 3 +-- src/treq/test/test_testing.py | 2 +- src/treq/test/util.py | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/testing.rst b/docs/testing.rst index 01ddb0f2..2f292b51 100644 --- a/docs/testing.rst +++ b/docs/testing.rst @@ -33,13 +33,15 @@ Download: :download:`testing_seq.py `. Loosely matching the request ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -If you don't care about certain parts of the request, you can pass :data:`mock.ANY`, which compares equal to anything. +If you don't care about certain parts of the request, you can pass :data:`unittest.mock.ANY`, which compares equal to anything. This sequence matches a single GET request with any parameters or headers: .. code-block:: python + from unittest.mock import ANY + RequestSequence([ - ((b'get', mock.ANY, mock.ANY, b''), (200, {}, b'ok')) + ((b'get', ANY, ANY, b''), (200, {}, b'ok')) ]) diff --git a/src/treq/test/test_client.py b/src/treq/test/test_client.py index e4481c62..ee9f3fdb 100644 --- a/src/treq/test/test_client.py +++ b/src/treq/test/test_client.py @@ -1,8 +1,7 @@ -# -*- encoding: utf-8 -*- from collections import OrderedDict from io import BytesIO -import mock +from unittest import mock from hyperlink import DecodedURL, EncodedURL from twisted.internet.defer import Deferred, succeed, CancelledError diff --git a/src/treq/test/test_content.py b/src/treq/test/test_content.py index 60cd998b..9acb9d76 100644 --- a/src/treq/test/test_content.py +++ b/src/treq/test/test_content.py @@ -1,5 +1,4 @@ -# -*- coding: utf-8 -*- -import mock +from unittest import mock from twisted.python.failure import Failure diff --git a/src/treq/test/test_testing.py b/src/treq/test/test_testing.py index 06b6a2d1..eb804158 100644 --- a/src/treq/test/test_testing.py +++ b/src/treq/test/test_testing.py @@ -4,7 +4,7 @@ from functools import partial from inspect import getmembers, isfunction -from mock import ANY +from unittest.mock import ANY from twisted.trial.unittest import TestCase from twisted.web.client import ResponseFailed diff --git a/src/treq/test/util.py b/src/treq/test/util.py index 16f82c27..c03ca002 100644 --- a/src/treq/test/util.py +++ b/src/treq/test/util.py @@ -1,7 +1,7 @@ import os import platform -import mock +from unittest import mock from twisted.internet import reactor from twisted.internet.task import Clock From af0dbb5ce75cc2dedf6d28560aff235a355243ee Mon Sep 17 00:00:00 2001 From: Tom Most Date: Sat, 16 Jan 2021 13:51:37 -0800 Subject: [PATCH 12/18] Remove mock dependency --- setup.py | 1 - tox.ini | 1 - 2 files changed, 2 deletions(-) diff --git a/setup.py b/setup.py index 34f85cdb..0585733a 100644 --- a/setup.py +++ b/setup.py @@ -36,7 +36,6 @@ ], extras_require={ "dev": [ - "mock", "pep8", "pyflakes", "sphinx", diff --git a/tox.ini b/tox.ini index f0a2d438..4abd3119 100644 --- a/tox.ini +++ b/tox.ini @@ -9,7 +9,6 @@ envlist = extras = dev deps = coverage - mock twisted_lowest: Twisted==18.7.0 twisted_latest: Twisted From ccadefb64116def66b25c27fa0556615a8878f6f Mon Sep 17 00:00:00 2001 From: Tom Most Date: Sat, 16 Jan 2021 13:55:19 -0800 Subject: [PATCH 13/18] Add changelog entry --- changelog.d/318.removal.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/318.removal.rst diff --git a/changelog.d/318.removal.rst b/changelog.d/318.removal.rst new file mode 100644 index 00000000..b7229ad9 --- /dev/null +++ b/changelog.d/318.removal.rst @@ -0,0 +1 @@ +Support for Python 2.7 and 3.5 has been dropped. treq no longer depends on ``six`` or ``mock``. From ae892516784d91c64fc909c89fa6dec5bd107e0d Mon Sep 17 00:00:00 2001 From: Tom Most Date: Sat, 16 Jan 2021 13:57:25 -0800 Subject: [PATCH 14/18] Remove six from treq.test.local_httpbin.child --- src/treq/test/local_httpbin/child.py | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/src/treq/test/local_httpbin/child.py b/src/treq/test/local_httpbin/child.py index dcd1f797..4a6914e9 100644 --- a/src/treq/test/local_httpbin/child.py +++ b/src/treq/test/local_httpbin/child.py @@ -10,9 +10,7 @@ import httpbin -import six - -from twisted.internet.defer import Deferred, inlineCallbacks, returnValue +from twisted.internet.defer import Deferred, inlineCallbacks from twisted.internet.endpoints import TCP4ServerEndpoint, SSL4ServerEndpoint from twisted.internet.task import react from twisted.internet.ssl import (Certificate, @@ -158,11 +156,9 @@ def _serve_tls(reactor, host, port, site): :return: A :py:class:`Deferred` that fires with a :py:class:`_HTTPBinDescription` """ - cert_host = host.decode('ascii') if six.PY2 else host - ( ca_cert, private_key, certificate, - ) = _certificates_for_authority_and_server(cert_host) + ) = _certificates_for_authority_and_server(host) context_factory = CertificateOptions(privateKey=private_key, certificate=certificate) @@ -178,7 +174,7 @@ def _serve_tls(reactor, host, port, site): port=port.getHost().port, cacert=ca_cert.dumpPEM().decode('ascii')) - returnValue(description) + return description @inlineCallbacks @@ -202,7 +198,7 @@ def _serve_tcp(reactor, host, port, site): description = _HTTPBinDescription(host=host, port=port.getHost().port) - returnValue(description) + return description def _output_process_description(description, stdout=sys.stdout): @@ -214,15 +210,8 @@ def _output_process_description(description, stdout=sys.stdout): :param stdout: (optional) Standard out. """ - if six.PY2: - write = stdout.write - flush = stdout.flush - else: - write = stdout.buffer.write - flush = stdout.buffer.flush - - write(description.to_json_bytes() + b'\n') - flush() + stdout.buffer.write(description.to_json_bytes() + b'\n') + stdout.buffer.flush() def _forever_httpbin(reactor, argv, From 55c08d073a6cd1dc964ccd88ea49e7e59941d3c3 Mon Sep 17 00:00:00 2001 From: Tom Most Date: Sat, 16 Jan 2021 13:59:26 -0800 Subject: [PATCH 15/18] Remove six from treq.test.local_httpbin.test.test_child --- .../test/local_httpbin/test/test_child.py | 21 +++++++------------ 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/src/treq/test/local_httpbin/test/test_child.py b/src/treq/test/local_httpbin/test/test_child.py index 7e767e15..2a08d4e3 100644 --- a/src/treq/test/local_httpbin/test/test_child.py +++ b/src/treq/test/local_httpbin/test/test_child.py @@ -25,8 +25,6 @@ from service_identity.cryptography import verify_certificate_hostname -import six - from .. import child, shared @@ -263,14 +261,13 @@ def flush(self): self._state.flush_count += 1 -if not six.PY2: - @attr.s - class BufferedStandardOut(object): - """ - A standard out that whose ``buffer`` is a - :py:class:`FlushableBytesIO` instance. - """ - buffer = attr.ib() +@attr.s +class BufferedStandardOut(object): + """ + A standard out that whose ``buffer`` is a + :py:class:`FlushableBytesIO` instance. + """ + buffer = attr.ib() class OutputProcessDescriptionTests(SynchronousTestCase): @@ -280,9 +277,7 @@ class OutputProcessDescriptionTests(SynchronousTestCase): def setUp(self): self.stdout_state = FlushableBytesIOState() - self.stdout = FlushableBytesIO(self.stdout_state) - if not six.PY2: - self.stdout = BufferedStandardOut(self.stdout) + self.stdout = BufferedStandardOut(FlushableBytesIO(self.stdout_state)) def test_description_written(self): """ From b21531ae58a1718c9c7ca04e91b2e7ac06c98c36 Mon Sep 17 00:00:00 2001 From: Tom Most Date: Sun, 24 Jan 2021 12:24:04 -0800 Subject: [PATCH 16/18] Fix python_requires to 3.6+ --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 0585733a..3c3ca287 100644 --- a/setup.py +++ b/setup.py @@ -26,7 +26,7 @@ package_dir={"": "src"}, setup_requires=["incremental"], use_incremental=True, - python_requires='>=3.5', + python_requires='>=3.6', install_requires=[ "incremental", "requests >= 2.1.0", From a007f5d17e844823e0eaca50161fe00124104e1f Mon Sep 17 00:00:00 2001 From: Tom Most Date: Sun, 24 Jan 2021 12:46:06 -0800 Subject: [PATCH 17/18] Remove inheritance from object Per review feedback. --- src/treq/_agentspy.py | 4 ++-- src/treq/auth.py | 2 +- src/treq/client.py | 2 +- src/treq/multipart.py | 6 +++--- src/treq/test/local_httpbin/parent.py | 2 +- src/treq/test/local_httpbin/shared.py | 2 +- src/treq/test/test_api.py | 4 ++-- src/treq/test/test_multipart.py | 6 +++--- src/treq/test/test_response.py | 2 +- src/treq/testing.py | 12 ++++++------ 10 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/treq/_agentspy.py b/src/treq/_agentspy.py index 52fc3f5b..42475bab 100644 --- a/src/treq/_agentspy.py +++ b/src/treq/_agentspy.py @@ -10,7 +10,7 @@ @attr.s(frozen=True, order=False, slots=True) -class RequestRecord(object): +class RequestRecord: """ The details of a call to :meth:`_AgentSpy.request` @@ -30,7 +30,7 @@ class RequestRecord(object): @implementer(IAgent) @attr.s -class _AgentSpy(object): +class _AgentSpy: """ An agent that records HTTP requests diff --git a/src/treq/auth.py b/src/treq/auth.py index fad3df6c..6ed986dd 100644 --- a/src/treq/auth.py +++ b/src/treq/auth.py @@ -20,7 +20,7 @@ def __init__(self, config): @implementer(IAgent) -class _RequestHeaderSetterAgent(object): +class _RequestHeaderSetterAgent: """ Wrap an agent to set request headers diff --git a/src/treq/client.py b/src/treq/client.py index d701c127..c52b555f 100644 --- a/src/treq/client.py +++ b/src/treq/client.py @@ -94,7 +94,7 @@ def deliverBody(self, protocol): self._waiters.append(protocol) -class HTTPClient(object): +class HTTPClient: def __init__(self, agent, cookiejar=None, data_to_body_producer=IBodyProducer): self._agent = agent diff --git a/src/treq/multipart.py b/src/treq/multipart.py index eaf0baaf..4888e879 100644 --- a/src/treq/multipart.py +++ b/src/treq/multipart.py @@ -14,7 +14,7 @@ @implementer(IBodyProducer) -class MultiPartProducer(object): +class MultiPartProducer: """ :class:`MultiPartProducer` takes parameters for a HTTP request and produces bytes in multipart/form-data format defined in :rfc:`2388` and @@ -272,7 +272,7 @@ def _converted(fields): "or tuple (filename, content type, IBodyProducer)") -class _LengthConsumer(object): +class _LengthConsumer: """ `_LengthConsumer` is used to calculate the length of the multi-part request. The easiest way to do that is to consume all the fields, @@ -302,7 +302,7 @@ def write(self, value): self.length += len(value) -class _Header(object): +class _Header: """ `_Header` This class is a tiny wrapper that produces request headers. We can't use standard python header diff --git a/src/treq/test/local_httpbin/parent.py b/src/treq/test/local_httpbin/parent.py index bb940a2f..78dedaef 100644 --- a/src/treq/test/local_httpbin/parent.py +++ b/src/treq/test/local_httpbin/parent.py @@ -59,7 +59,7 @@ def connectionLost(self, reason): @attr.s -class _HTTPBinProcess(object): +class _HTTPBinProcess: """ Manage an ``httpbin`` server process. diff --git a/src/treq/test/local_httpbin/shared.py b/src/treq/test/local_httpbin/shared.py index f0572256..78016c45 100644 --- a/src/treq/test/local_httpbin/shared.py +++ b/src/treq/test/local_httpbin/shared.py @@ -6,7 +6,7 @@ @attr.s -class _HTTPBinDescription(object): +class _HTTPBinDescription: """ Describe an ``httpbin`` process. diff --git a/src/treq/test/test_api.py b/src/treq/test/test_api.py index 782a35f4..f7f9e093 100644 --- a/src/treq/test/test_api.py +++ b/src/treq/test/test_api.py @@ -15,7 +15,7 @@ from twisted.test.proto_helpers import MemoryReactorClock -class SyntacticAbominationHTTPConnectionPool(object): +class SyntacticAbominationHTTPConnectionPool: """ A HTTP connection pool that always fails to return a connection, but counts the number of requests made. @@ -80,7 +80,7 @@ def test_custom_agent(self): """ @implementer(IAgent) - class CounterAgent(object): + class CounterAgent: requests = 0 def request(self, method, uri, headers=None, bodyProducer=None): diff --git a/src/treq/test/test_multipart.py b/src/treq/test/test_multipart.py index 5a079149..fd170e3d 100644 --- a/src/treq/test/test_multipart.py +++ b/src/treq/test/test_multipart.py @@ -78,11 +78,11 @@ def test_unknownLength(self): passed as a parameter without either a C{seek} or C{tell} method, its C{length} attribute is set to C{UNKNOWN_LENGTH}. """ - class HasSeek(object): + class HasSeek: def seek(self, offset, whence): pass - class HasTell(object): + class HasTell: def tell(self): pass @@ -193,7 +193,7 @@ def test_failedReadWhileProducing(self): L{MultiPartProducer.startProducing} fires with a L{Failure} wrapping that exception. """ - class BrokenFile(object): + class BrokenFile: def read(self, count): raise IOError("Simulated bad thing") diff --git a/src/treq/test/test_response.py b/src/treq/test/test_response.py index e63bc587..77941e87 100644 --- a/src/treq/test/test_response.py +++ b/src/treq/test/test_response.py @@ -10,7 +10,7 @@ from treq.response import _Response -class FakeResponse(object): +class FakeResponse: def __init__(self, code, headers, body=()): self.code = code self.headers = headers diff --git a/src/treq/testing.py b/src/treq/testing.py index 360697e7..df633beb 100644 --- a/src/treq/testing.py +++ b/src/treq/testing.py @@ -37,7 +37,7 @@ @implementer(IAgentEndpointFactory) @attr.s -class _EndpointFactory(object): +class _EndpointFactory: """ An endpoint factory used by :class:`RequestTraversalAgent`. @@ -72,7 +72,7 @@ def endpointForURI(self, uri): @implementer(IAgent) -class RequestTraversalAgent(object): +class RequestTraversalAgent: """ :obj:`~twisted.web.iweb.IAgent` implementation that issues an in-memory request rather than going out to a real network socket. @@ -168,7 +168,7 @@ def flush(self): @implementer(IBodyProducer) -class _SynchronousProducer(object): +class _SynchronousProducer: """ A partial implementation of an :obj:`IBodyProducer` which produces its entire payload immediately. There is no way to access to an instance of @@ -215,7 +215,7 @@ def wrapper(*args, **kwargs): return wrapper -class StubTreq(object): +class StubTreq: """ A fake version of the treq module that can be used for testing that provides all the function calls exposed in :obj:`treq.__all__`. @@ -331,7 +331,7 @@ def _maybeEncodeHeaders(headers): for k, vs in headers.items()} -class HasHeaders(object): +class HasHeaders: """ Since Twisted adds headers to a request, such as the host and the content length, it's necessary to test whether request headers CONTAIN the expected @@ -361,7 +361,7 @@ def __ne__(self, other_headers): return not self.__eq__(other_headers) -class RequestSequence(object): +class RequestSequence: """ For an example usage, see :meth:`RequestSequence.consume`. From 68774be723d538a2b1124b57e140f9dc773aaf58 Mon Sep 17 00:00:00 2001 From: Tom Most Date: Sun, 24 Jan 2021 12:48:59 -0800 Subject: [PATCH 18/18] More review feedback --- src/treq/client.py | 2 +- src/treq/multipart.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/treq/client.py b/src/treq/client.py index c52b555f..7924d110 100644 --- a/src/treq/client.py +++ b/src/treq/client.py @@ -494,5 +494,5 @@ def _guess_content_type(filename): registerAdapter(_from_bytes, bytes, IBodyProducer) registerAdapter(_from_file, io.BytesIO, IBodyProducer) -# file()/open() equiv on Py3 +# file()/open() equiv registerAdapter(_from_file, io.BufferedReader, IBodyProducer) diff --git a/src/treq/multipart.py b/src/treq/multipart.py index 4888e879..bb5116e5 100644 --- a/src/treq/multipart.py +++ b/src/treq/multipart.py @@ -223,7 +223,7 @@ def _escape(value): def _enforce_unicode(value): """ - This function enforces the stings passed to be unicode, so we won't + This function enforces the strings passed to be unicode, so we won't need to guess what's the encoding of the binary strings passed in. If someone needs to pass the binary string, use BytesIO and wrap it with `FileBodyProducer`. @@ -232,10 +232,10 @@ def _enforce_unicode(value): return value elif isinstance(value, bytes): - # we got a byte string, and we have no ide what's the encoding of it + # we got a byte string, and we have no idea what's the encoding of it # we can only assume that it's something cool try: - return str(value, "utf-8") + return value.decode("utf-8") except UnicodeDecodeError: raise ValueError( "Supplied raw bytes that are not ascii/utf-8."