From 9428eeb197ae155971bcb8c1ec59aea4964e7bf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wilfredo=20Sa=CC=81nchez?= Date: Tue, 24 Mar 2020 16:40:12 -0700 Subject: [PATCH 01/10] Drop support for Python 2.7 --- .travis.yml | 21 ++------------------- CONTRIBUTING.rst | 4 ++-- pyproject.toml | 2 +- setup.py | 4 +--- src/klein/test/_trial.py | 23 ----------------------- src/klein/test/test_form.py | 8 +++----- src/klein/test/test_resource.py | 16 +--------------- tox.ini | 25 +++++++++++-------------- 8 files changed, 21 insertions(+), 82 deletions(-) diff --git a/.travis.yml b/.travis.yml index a50524f39..bfa9afbcd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,13 +28,6 @@ matrix: - python: 3.8 env: TOXENV=mypy - - python: 2.7 - env: TOXENV=coverage-py27-tw171,codecov - - python: 2.7 - env: TOXENV=coverage-py27-tw184,codecov - - python: 2.7 - env: TOXENV=coverage-py27-twcurrent,codecov - - python: 3.5 env: TOXENV=coverage-py35-tw171,codecov - python: 3.5 @@ -63,13 +56,6 @@ matrix: - python: 3.8 env: TOXENV=coverage-py38-twcurrent,codecov - - python: pypy - env: TOXENV=coverage-pypy2-tw171,codecov - - python: pypy - env: TOXENV=coverage-pypy2-tw184,codecov - - python: pypy - env: TOXENV=coverage-pypy2-twcurrent,codecov - - python: pypy3 env: TOXENV=coverage-pypy3-tw171,codecov - python: pypy3 @@ -79,19 +65,16 @@ matrix: # Test against Twisted trunk in case something in development breaks us. # This is allowed to fail below, since the bug may be in Twisted. - - python: 2.7 - env: TOXENV=coverage-py27-twtrunk,codecov - python: 3.8 env: TOXENV=coverage-py38-twtrunk,codecov - - python: 2.7 + - python: 3.8 env: TOXENV=docs - - python: 2.7 + - python: 3.8 env: TOXENV=docs-linkcheck allow_failures: # Tests against Twisted trunk are allow to fail, as they are not supported. - - env: TOXENV=coverage-py27-twtrunk,codecov - env: TOXENV=coverage-py38-twtrunk,codecov # This depends on external web sites, so it's allowed to fail. diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 8b0947124..4f25daf9f 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -13,12 +13,12 @@ Klein is an open source project that welcomes contributions of all kinds coming Getting started =============== -Here is a list of shell commands that will install the dependencies of Klein, run the test suite with Python 2.7 and the current version of Twisted, compile the documentation, and check for coding style issues with flake8. +Here is a list of shell commands that will install the dependencies of Klein, run the test suite with Python 3.8 and the current version of Twisted, compile the documentation, and check for coding style issues with flake8. .. code-block:: shell pip install --user tox - tox -e py27-twcurrent + tox -e py38-twcurrent tox -e docs tox -e flake8 diff --git a/pyproject.toml b/pyproject.toml index e7efe6aeb..85b5fb44e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,4 +7,4 @@ build-backend = "setuptools.build_meta" [tool.black] line-length = 80 -target-version = ["py27"] +target-version = ["py35"] diff --git a/setup.py b/setup.py index fe1bb57a4..ee139b86f 100644 --- a/setup.py +++ b/setup.py @@ -12,10 +12,10 @@ 'License :: OSI Approved :: MIT License', 'Operating System :: OS Independent', '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', 'Programming Language :: Python :: Implementation :: CPython', 'Programming Language :: Python :: Implementation :: PyPy', 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', @@ -27,13 +27,11 @@ use_incremental=True, install_requires=[ "attrs", - 'enum34 ; python_version<"3.4"', "hyperlink", "incremental", "six", "Tubes", "Twisted>=15.5", # 15.5 is the first version to support Py3.3+ - 'typing ; python_version<"3.5"', "Werkzeug", "zope.interface", ], diff --git a/src/klein/test/_trial.py b/src/klein/test/_trial.py index 612e25ec6..b79aee0df 100644 --- a/src/klein/test/_trial.py +++ b/src/klein/test/_trial.py @@ -4,10 +4,8 @@ Extensions to L{twisted.trial}. """ -import sys from typing import Any -from twisted import version as twistedVersion from twisted.trial.unittest import SynchronousTestCase from zope.interface import Interface @@ -23,27 +21,6 @@ class TestCase(SynchronousTestCase): Extensions to L{SynchronousTestCase}. """ - if (twistedVersion.major, twistedVersion.minor) < (16, 4): - - def assertRegex(self, text, regex, msg=None): - # type: (str, Any, str) -> None - """ - Fail the test if a C{regexp} search of C{text} fails. - - @param text: Text which is under test. - - @param regex: A regular expression object or a string containing a - regular expression suitable for use by re.search(). - - @param msg: Text used as the error message on failure. - """ - if sys.version_info[:2] > (2, 7): - super(TestCase, self).assertRegex(text, regex, msg) - else: - # Python 2.7 has unittest.assertRegexpMatches() which was - # renamed to unittest.assertRegex() in Python 3.2 - super(TestCase, self).assertRegexpMatches(text, regex, msg) - def assertProvides(self, interface, obj): # type: (Interface, Any) -> None """ diff --git a/src/klein/test/test_form.py b/src/klein/test/test_form.py index f1ea3ceee..0db299c2c 100644 --- a/src/klein/test/test_form.py +++ b/src/klein/test/test_form.py @@ -465,12 +465,10 @@ def test_customParameterValidation(self): self.assertEqual(len(errors), 1) errorText = cast(str, errors[0].text) self.assertIsNot(errorText, None) - self.assertTrue( - errorText.startswith("invalid literal for int() with base 10: ") + self.assertEqual( + errorText, + "invalid literal for int() with base 10: 'not a number'", ) - # Peculiar 2-step assert because pypy2 (invalidly) sticks a 'u' in - # there. - self.assertTrue(errorText.endswith("'not a number'")) def test_handlingJSON(self): # type: () -> None diff --git a/src/klein/test/test_resource.py b/src/klein/test/test_resource.py index d90dba50b..193f0c2a1 100644 --- a/src/klein/test/test_resource.py +++ b/src/klein/test/test_resource.py @@ -1098,16 +1098,7 @@ def test_failedDecodePathInfo(self): self.assertEqual(b"Non-UTF-8 encoding in URL.", rv) self.assertEqual(1, len(self.flushLoggedErrors(UnicodeDecodeError))) - def test_urlDecodeErrorReprPy2(self): - """ - URLDecodeError.__repr__ formats properly. - """ - self.assertEqual( - ")>", - repr(_URLDecodeError(ValueError)), - ) - - def test_urlDecodeErrorReprPy3(self): + def test_urlDecodeErrorRepr(self): """ URLDecodeError.__repr__ formats properly. """ @@ -1116,11 +1107,6 @@ def test_urlDecodeErrorReprPy3(self): repr(_URLDecodeError(ValueError)), ) - if _PY3: - test_urlDecodeErrorReprPy2.skip = "Only works on Py2" # type: ignore - else: - test_urlDecodeErrorReprPy3.skip = "Only works on Py3" # type: ignore - def test_subroutedBranch(self): subapp = Klein() diff --git a/tox.ini b/tox.ini index 15aaecaed..88a1ec512 100644 --- a/tox.ini +++ b/tox.ini @@ -2,7 +2,7 @@ envlist = flake8, black, mypy - coverage-py{27,35,36,37,38,py2,py3}-tw{171,184,current,trunk} + coverage-py{35,36,37,38,py3}-tw{171,184,current,trunk} coverage_report docs, docs-linkcheck packaging @@ -41,14 +41,6 @@ deps = Werkzeug==0.16.0 zope.interface==4.7.1 - {test,coverage}: treq==18.6.0 - {test,coverage}: hypothesis==4.56.0 - {test,coverage}: idna==2.8 - {test,coverage}-py{27,py2}: mock==3.0.5 - {test,coverage}-py{27,py2}: typing==3.7.4.1 - - coverage: {[testenv:coverage_report]deps} - setenv = PY_MODULE=klein @@ -64,17 +56,22 @@ setenv = description = run tests basepython = - py27: python2.7 py35: python3.5 py36: python3.6 py37: python3.7 py38: python3.8 py39: python3.9 - pypy2: pypy pypy3: pypy3 -deps = {[default]deps} +deps = + {[default]deps} + + {test,coverage}: treq==18.6.0 + {test,coverage}: hypothesis==4.56.0 + {test,coverage}: idna==2.8 + + coverage: {[testenv:coverage_report]deps} setenv = {[default]setenv} @@ -338,7 +335,7 @@ allow_untyped_defs = True [mypy-constantly] ignore_missing_imports = True -[mypy-hyperlink] +[mypy-hyperlink] ignore_missing_imports = True [mypy-incremental] @@ -376,7 +373,7 @@ ignore_missing_imports = True description = generate coverage report -depends = {test,coverage}-py{27,35,36,37,38,py2,py3}-tw{171,184,current,trunk} +depends = {test,coverage}-py{35,36,37,38,py3}-tw{171,184,current,trunk} basepython = {[default]basepython} From 52ef7fde27042c0cfcdecf5fa282f4ade2068e8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wilfredo=20Sa=CC=81nchez?= Date: Tue, 24 Mar 2020 16:40:33 -0700 Subject: [PATCH 02/10] Run black-reformat --- src/klein/_app.py | 2 +- src/klein/_dihttp.py | 6 ++---- src/klein/_form.py | 2 +- src/klein/_plating.py | 6 +++--- src/klein/_request_compat.py | 10 ++++----- src/klein/_resource.py | 2 +- src/klein/_session.py | 6 +++--- src/klein/storage/_memory.py | 2 +- src/klein/test/_strategies.py | 10 ++++----- src/klein/test/test_form.py | 31 +++++++++++++-------------- src/klein/test/test_headers.py | 8 +++---- src/klein/test/test_request.py | 8 +++---- src/klein/test/test_request_compat.py | 22 +++++++++---------- src/klein/test/test_requirer.py | 2 +- src/klein/test/test_resource.py | 8 +++---- src/klein/test/test_session.py | 2 +- 16 files changed, 61 insertions(+), 66 deletions(-) diff --git a/src/klein/_app.py b/src/klein/_app.py index b422a6bdf..19c691249 100644 --- a/src/klein/_app.py +++ b/src/klein/_app.py @@ -232,7 +232,7 @@ def branch_f(instance, request, *a, **kw): Rule( url.rstrip("/") + "/" + "", *args, - **branchKwargs + **branchKwargs, ) ) diff --git a/src/klein/_dihttp.py b/src/klein/_dihttp.py index ce01fad78..099f0aa1f 100644 --- a/src/klein/_dihttp.py +++ b/src/klein/_dihttp.py @@ -40,9 +40,7 @@ def urlFromRequest(request): url = DecodedURL.fromText(request.uri.decode("charmap")) url = url.replace( - scheme=u"https" if request.isSecure() else u"http", - host=host, - port=port, + scheme="https" if request.isSecure() else "http", host=host, port=port, ) return url @@ -124,7 +122,7 @@ class Response(object): ], default=attr.Factory(dict), ) - body = attr.ib(type=Any, default=u"") + body = attr.ib(type=Any, default="") def _applyToRequest(self, request): # type: (IRequest) -> Any diff --git a/src/klein/_form.py b/src/klein/_form.py index 5ef008bb2..4da66be0f 100644 --- a/src/klein/_form.py +++ b/src/klein/_form.py @@ -253,7 +253,7 @@ def hidden(cls, name, value, **kw): formInputType="hidden", noLabel=True, value=value, - **kw + **kw, ).maybeNamed(name) @classmethod diff --git a/src/klein/_plating.py b/src/klein/_plating.py index 1b8a8d792..82aaeac9d 100644 --- a/src/klein/_plating.py +++ b/src/klein/_plating.py @@ -245,17 +245,17 @@ def mymethod(instance, request, *args, **kw): json_data.update(data) for ignored in self._presentationSlots: json_data.pop(ignored, None) - text_type = u"json" + text_type = "json" ready = yield resolveDeferredObjects(json_data) result = dumps(ready) else: data[self.CONTENT] = loader.load() - text_type = u"html" + text_type = "html" result = self._elementify(instance, data) request.setHeader( b"content-type", ( - u"text/{format}; charset=utf-8".format( + "text/{format}; charset=utf-8".format( format=text_type ).encode("charmap") ), diff --git a/src/klein/_request_compat.py b/src/klein/_request_compat.py index f4502f536..f5d94609f 100644 --- a/src/klein/_request_compat.py +++ b/src/klein/_request_compat.py @@ -62,9 +62,9 @@ def uri(self): # This code borrows from t.w.server.Request._prePathURL. if request.isSecure(): - scheme = u"https" + scheme = "https" else: - scheme = u"http" + scheme = "http" netloc = nativeString(request.getRequestHostname()) @@ -74,13 +74,13 @@ def uri(self): else: default = 80 if port != default: - netloc += u":{}".format(port) + netloc += ":{}".format(port) path = nativeString(request.uri) - if path and path[0] == u"/": + if path and path[0] == "/": path = path[1:] - return DecodedURL.fromText(u"{}://{}/{}".format(scheme, netloc, path)) + return DecodedURL.fromText("{}://{}/{}".format(scheme, netloc, path)) @property def headers(self): diff --git a/src/klein/_resource.py b/src/klein/_resource.py index 1870fb5c2..585b40b0a 100644 --- a/src/klein/_resource.py +++ b/src/klein/_resource.py @@ -97,7 +97,7 @@ def _extractURLparts(request): if not path_info.startswith(b"/"): path_info = b"/" + path_info - url_scheme = u"https" if request.isSecure() else u"http" + url_scheme = "https" if request.isSecure() else "http" utf8Failures = [] try: diff --git a/src/klein/_session.py b/src/klein/_session.py index fbd120e2d..af482957c 100644 --- a/src/klein/_session.py +++ b/src/klein/_session.py @@ -166,8 +166,8 @@ def procureSession(self, request, forceInsecure=False): # manipulation could succeed (no CSRF token check could # ever succeed, for example). raise NoSuchSession( - u"Can't initialize a session on a " - u"{method} request.".format( + "Can't initialize a session on a " + "{method} request.".format( method=request.method.decode("ascii") ) ) @@ -175,7 +175,7 @@ def procureSession(self, request, forceInsecure=False): # We don't have a session ID at all, and we're not allowed # by policy to set a cookie on the client. raise NoSuchSession( - u"Cannot auto-initialize a session for this request." + "Cannot auto-initialize a session for this request." ) session = yield self._store.newSession(sentSecurely, mechanism) identifierInCookie = session.identifier diff --git a/src/klein/storage/_memory.py b/src/klein/storage/_memory.py index fccc281e8..c5915db96 100644 --- a/src/klein/storage/_memory.py +++ b/src/klein/storage/_memory.py @@ -154,7 +154,7 @@ def loadSession(self, identifier, isConfidential, authenticatedBy): else: return fail( NoSuchSession( - u"Session not found in memory store {id!r}".format( + "Session not found in memory store {id!r}".format( id=identifier ) ) diff --git a/src/klein/test/_strategies.py b/src/klein/test/_strategies.py index 356005032..f11107bac 100644 --- a/src/klein/test/_strategies.py +++ b/src/klein/test/_strategies.py @@ -75,7 +75,7 @@ def idna_characters(): # pragma: no cover break result.append(unichr(i)) - _idnaCharacters = u"".join(result) + _idnaCharacters = "".join(result) return _idnaCharacters @@ -115,7 +115,7 @@ def latin1_text(draw, min_size=0, max_size=None): @param max_size: The maximum number of characters in the text. Use C{None} for an unbounded size. """ - return u"".join( + return "".join( draw( lists( characters(max_codepoint=255), @@ -195,7 +195,7 @@ def hostname_labels(draw, allow_idn=True): # pragma: no cover text( min_size=1, max_size=63, - alphabet=unicode(ascii_letters + digits + u"-"), + alphabet=unicode(ascii_letters + digits + "-"), ) ), ) @@ -234,7 +234,7 @@ def hostnames( ), ) - name = u".".join(labels) + name = ".".join(labels) # Filter names that are not IDNA-encodable. # We try pretty hard not to generate bogus names in the first place... but @@ -306,7 +306,7 @@ def encoded_urls(draw): # pragma: no cover port = None return EncodedURL( - scheme=cast(Text, draw(sampled_from((u"http", u"https")))), + scheme=cast(Text, draw(sampled_from(("http", "https")))), host=host, port=port, path=path, diff --git a/src/klein/test/test_form.py b/src/klein/test/test_form.py index 0db299c2c..2ad9350b3 100644 --- a/src/klein/test/test_form.py +++ b/src/klein/test/test_form.py @@ -85,7 +85,7 @@ def handler(self, name, value): @requirer.require( router.route("/handle-submit", methods=["POST"]), name=Field.text(), - button=Field.submit(u"OK"), + button=Field.submit("OK"), ) def handlerWithSubmit(self, name, button): # type: (str, str) -> None @@ -122,7 +122,7 @@ def constrained(self, goldilocks): @requirer.require( router.route("/render", methods=["GET"]), - form=Form.rendererFor(handler, action=u"/handle"), + form=Form.rendererFor(handler, action="/handle"), ) def renderer(self, form): # type: (IRequest, Form) -> Form @@ -130,7 +130,7 @@ def renderer(self, form): @requirer.require( router.route("/render-submit", methods=["GET"]), - form=Form.rendererFor(handlerWithSubmit, action=u"/handle-submit"), + form=Form.rendererFor(handlerWithSubmit, action="/handle-submit"), ) def submitRenderer(self, form): # type: (IRequest, RenderableForm) -> RenderableForm @@ -138,7 +138,7 @@ def submitRenderer(self, form): @requirer.require( router.route("/render-custom", methods=["GET"]), - form=Form.rendererFor(handler, action=u"/handle"), + form=Form.rendererFor(handler, action="/handle"), ) def customFormRender(self, form): # type: (RenderableForm) -> Any @@ -150,7 +150,7 @@ def customFormRender(self, form): @requirer.require( router.route("/render-cascade", methods=["GET"]), - form=Form.rendererFor(handler, action=u"/handle"), + form=Form.rendererFor(handler, action="/handle"), ) def cascadeRenderer(self, form): # type: (RenderableForm) -> RenderableForm @@ -195,7 +195,7 @@ def emptyHandler(self): @requirer.require( router.route("/render-empty", methods=["GET"]), - form=Form.rendererFor(emptyHandler, action=u"/handle-empty"), + form=Form.rendererFor(emptyHandler, action="/handle-empty"), ) def emptyRenderer(self, form): # type: (RenderableForm) -> RenderableForm @@ -235,7 +235,7 @@ def test_textConverter(self): """ Convert a string of either type to text. """ - text = u"f\xf6o" + text = "f\xf6o" for string in (text, text.encode("utf-8")): result = textConverter(string) # type: ignore[type-var] self.assertIsInstance(result, Text) @@ -264,7 +264,7 @@ def test_handling(self): ) self.assertEqual(response.code, 200) self.assertEqual(self.successResultOf(content(response)), b"yay") - self.assertEqual(to.calls, [(u"hello", 1234)]) + self.assertEqual(to.calls, [("hello", 1234)]) def test_handlingPassword(self): # type: () -> None @@ -291,7 +291,7 @@ def test_handlingPassword(self): self.assertEqual( self.successResultOf(content(response)), b"password received" ) - self.assertEqual(to.calls, [(u"password", u"asdfjkl;")]) + self.assertEqual(to.calls, [("password", "asdfjkl;")]) def test_numberConstraints(self): # type: () -> None @@ -333,7 +333,7 @@ def test_numberConstraints(self): self.assertEqual(tooLow.code, 400) self.assertEqual(justRight.code, 200) self.assertEqual(self.successResultOf(content(justRight)), b"got it") - self.assertEqual(to.calls, [(u"constrained", 7)]) + self.assertEqual(to.calls, [("constrained", 7)]) def test_missingRequiredParameter(self): # type: () -> None @@ -406,7 +406,7 @@ def test_handlingGET(self): ) self.assertEqual(response.code, 200) self.assertEqual(self.successResultOf(content(response)), b"got") - self.assertEqual(calls, [(u"hello, big world", 4321)]) + self.assertEqual(calls, [("hello, big world", 4321)]) def test_validatingParameters(self): # type: () -> None @@ -466,8 +466,7 @@ def test_customParameterValidation(self): errorText = cast(str, errors[0].text) self.assertIsNot(errorText, None) self.assertEqual( - errorText, - "invalid literal for int() with base 10: 'not a number'", + errorText, "invalid literal for int() with base 10: 'not a number'", ) def test_handlingJSON(self): @@ -488,12 +487,12 @@ def test_handlingJSON(self): stub.post( "https://localhost/handle", json=dict(name="hello", value="1234", ignoreme="extraneous"), - headers={u"X-Test-Session": session.identifier}, + headers={"X-Test-Session": session.identifier}, ) ) self.assertEqual(response.code, 200) self.assertEqual(self.successResultOf(content(response)), b"yay") - self.assertEqual(to.calls, [(u"hello", 1234)]) + self.assertEqual(to.calls, [("hello", 1234)]) def test_missingOptionalParameterJSON(self): # type: () -> None @@ -527,7 +526,7 @@ def test_missingOptionalParameterJSON(self): self.assertEqual(response2.code, 200) self.assertEqual(self.successResultOf(content(response)), b"okay") self.assertEqual(self.successResultOf(content(response2)), b"okay") - self.assertEqual(to.calls, [(u"one", 7.0), (u"two", 2.0)]) + self.assertEqual(to.calls, [("one", 7.0), ("two", 2.0)]) def test_rendering(self): # type: () -> None diff --git a/src/klein/test/test_headers.py b/src/klein/test/test_headers.py index 33bf1d0db..55a8ac72b 100644 --- a/src/klein/test/test_headers.py +++ b/src/klein/test/test_headers.py @@ -430,7 +430,7 @@ def test_removeTextName(self): """ rawHeaders = ((b"a", b"1"), (b"b", b"2a"), (b"c", b"3"), (b"b", b"2b")) headers = self.headers(rawHeaders=rawHeaders) - headers.remove(name=u"b") + headers.remove(name="b") self.assertRawHeadersEqual( headers.rawHeaders, ((b"a", b"1"), (b"c", b"3")) @@ -473,7 +473,7 @@ def test_addValueTextName(self): """ rawHeaders = ((b"a", b"1"), (b"b", b"2a")) headers = self.headers(rawHeaders=rawHeaders) - headers.addValue(name=u"b", value=u"2b") + headers.addValue(name="b", value="2b") self.assertRawHeadersEqual( headers.rawHeaders, ((b"a", b"1"), (b"b", b"2a"), (b"b", b"2b")) @@ -488,7 +488,7 @@ def test_addValueBytesNameTextValue(self): headers = self.headers(rawHeaders=()) e = cast(TestCase, self).assertRaises( - TypeError, headers.addValue, b"a", u"1" + TypeError, headers.addValue, b"a", "1" ) cast(TestCase, self).assertRegex( str(e), "value u?'1' must be bytes to match name b?'a'" @@ -503,7 +503,7 @@ def test_addValueTextNameBytesValue(self): headers = self.headers(rawHeaders=()) e = cast(TestCase, self).assertRaises( - TypeError, headers.addValue, u"a", b"1" + TypeError, headers.addValue, "a", b"1" ) cast(TestCase, self).assertRegex( str(e), "value b?'1' must be text to match name u?'a'" diff --git a/src/klein/test/test_request.py b/src/klein/test/test_request.py index ca6cd20c2..e7f58bd32 100644 --- a/src/klein/test/test_request.py +++ b/src/klein/test/test_request.py @@ -26,8 +26,8 @@ class FrozenHTTPRequestTests(FrozenHTTPMessageTestsMixIn, TestCase): def requestFromBytes(data=b""): # type: (bytes) -> FrozenHTTPRequest return FrozenHTTPRequest( - method=u"GET", - uri=DecodedURL.fromText(u"https://twistedmatrix.com/"), + method="GET", + uri=DecodedURL.fromText("https://twistedmatrix.com/"), headers=FrozenHTTPHeaders(rawHeaders=()), body=data, ) @@ -54,8 +54,8 @@ def test_initInvalidBodyType(self): e = self.assertRaises( TypeError, FrozenHTTPRequest, - method=u"GET", - uri=DecodedURL.fromText(u"https://twistedmatrix.com/"), + method="GET", + uri=DecodedURL.fromText("https://twistedmatrix.com/"), headers=FrozenHTTPHeaders(rawHeaders=()), body=object(), ) diff --git a/src/klein/test/test_request_compat.py b/src/klein/test/test_request_compat.py index 14186f99c..f5167d7d1 100644 --- a/src/klein/test/test_request_compat.py +++ b/src/klein/test/test_request_compat.py @@ -85,12 +85,10 @@ def test_uri(self, url): assert uri.port is not None # Tells mypy it's not an Optional path = ( - uri.replace(scheme=u"", host=u"", port=None) - .asText() - .encode("ascii") + uri.replace(scheme="", host="", port=None).asText().encode("ascii") ) legacyRequest = self.legacyRequest( - isSecure=(uri.scheme == u"https"), + isSecure=(uri.scheme == "https"), host=uri.host.encode("ascii"), port=uri.port, path=path, @@ -104,14 +102,14 @@ def test_uri(self, url): def strURL(url): # type: (EncodedURL) -> Text return ( - u"URL(scheme={url.scheme!r}, " - u"userinfo={url.userinfo!r}, " - u"host={url.host!r}, " - u"port={url.port!r}, " - u"path={url.path!r}, " - u"query={url.query!r}, " - u"fragment={url.fragment!r}, " - u"rooted={url.rooted})" + "URL(scheme={url.scheme!r}, " + "userinfo={url.userinfo!r}, " + "host={url.host!r}, " + "port={url.port!r}, " + "path={url.path!r}, " + "query={url.query!r}, " + "fragment={url.fragment!r}, " + "rooted={url.rooted})" ).format(url=url) self.assertEqual( diff --git a/src/klein/test/test_requirer.py b/src/klein/test/test_requirer.py index e693dc532..e0d2d0d5b 100644 --- a/src/klein/test/test_requirer.py +++ b/src/klein/test/test_requirer.py @@ -41,7 +41,7 @@ def requiresURL(url): """ This is a route that requires a URL. """ - return url.child(u"hello/ world").asText() + return url.child("hello/ world").asText() class ISample(Interface): diff --git a/src/klein/test/test_resource.py b/src/klein/test/test_resource.py index 193f0c2a1..7e90823db 100644 --- a/src/klein/test/test_resource.py +++ b/src/klein/test/test_resource.py @@ -519,7 +519,7 @@ def test_renderUnicode(self): @app.route("/snowman") def snowman(request): - return u"\u2603" + return "\u2603" d = _render(self.kr, request) @@ -1070,8 +1070,8 @@ def root(request): self.assertFired(d) def test_ensure_utf8_bytes(self): - self.assertEqual(ensure_utf8_bytes(u"abc"), b"abc") - self.assertEqual(ensure_utf8_bytes(u"\u2202"), b"\xe2\x88\x82") + self.assertEqual(ensure_utf8_bytes("abc"), b"abc") + self.assertEqual(ensure_utf8_bytes("\u2202"), b"\xe2\x88\x82") self.assertEqual(ensure_utf8_bytes(b"\xe2\x88\x82"), b"\xe2\x88\x82") def test_decodesPath(self): @@ -1233,7 +1233,7 @@ def test_afUnixSocket(self): """ request = requestMock(b"/f\xc3\xb6\xc3\xb6") server_mock = Mock(Server) - server_mock.getRequestHostname = u"/var/run/twisted.socket" + server_mock.getRequestHostname = "/var/run/twisted.socket" request.host = server_mock ( url_scheme, diff --git a/src/klein/test/test_session.py b/src/klein/test/test_session.py index e68005d84..5d8adef7a 100644 --- a/src/klein/test/test_session.py +++ b/src/klein/test/test_session.py @@ -210,7 +210,7 @@ def test_unknownSessionHeader(self): sessions, exceptions, token, cookie, treq = simpleSessionRouter() response = self.successResultOf( - treq.get("https://unittest.example.com/", headers={token: u"bad"}) + treq.get("https://unittest.example.com/", headers={token: "bad"}) ) self.assertEqual(response.code, 200) self.assertEqual(len(sessions), 0) From 6de85ccb01cd235add6fc21856924d23f86cf367 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wilfredo=20Sa=CC=81nchez?= Date: Fri, 27 Mar 2020 21:17:08 -0700 Subject: [PATCH 03/10] Add python_requires --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index ee139b86f..26d88b97b 100644 --- a/setup.py +++ b/setup.py @@ -23,6 +23,7 @@ ], description="werkzeug + twisted.web", long_description=long_description, + python_requires=">=3.5", setup_requires=["incremental"], use_incremental=True, install_requires=[ From 96c731f2b2b69020fc25fba3ecb42bcde112dd35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wilfredo=20Sa=CC=81nchez?= Date: Mon, 6 Apr 2020 14:45:17 -0700 Subject: [PATCH 04/10] Update treq --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 246810518..425b6f30c 100644 --- a/tox.ini +++ b/tox.ini @@ -67,7 +67,7 @@ basepython = deps = {[default]deps} - {test,coverage}: treq==18.6.0 + {test,coverage}: treq==20.3.0 {test,coverage}: hypothesis==4.56.0 {test,coverage}: idna==2.8 From 46838b6d7a959f544eaa09541bfaae51df04433a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wilfredo=20Sa=CC=81nchez?= Date: Mon, 8 Jun 2020 10:00:01 -0700 Subject: [PATCH 05/10] black --- release.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release.py b/release.py index b87c01c17..5001b72ad 100644 --- a/release.py +++ b/release.py @@ -58,7 +58,7 @@ def currentVersion() -> Version: # code from its source tree. Boo. versionInfo: Dict[str, Any] = {} versonFile = Path(__file__).parent / "src" / "klein" / "_version.py" - exec (versonFile.read_text(), versionInfo) # noqa: E211 # black py2.7 + exec(versonFile.read_text(), versionInfo) # noqa: E211 # black py2.7 return versionInfo["__version__"] From c5771099fe966e90f3288527250158a3d98f57ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wilfredo=20Sa=CC=81nchez?= Date: Mon, 8 Jun 2020 10:38:39 -0700 Subject: [PATCH 06/10] include release.py --- MANIFEST.in | 1 + 1 file changed, 1 insertion(+) diff --git a/MANIFEST.in b/MANIFEST.in index 01a7f583e..9175e8900 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -7,6 +7,7 @@ include LICENSE include NEWS.rst include pyproject.toml include README.rst +include release.py include tox.ini recursive-include docs *.py recursive-include docs *.rst From 2b4916b911636a4f4311f016763869a76ea3ea25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wilfredo=20Sa=CC=81nchez?= Date: Mon, 8 Jun 2020 10:51:35 -0700 Subject: [PATCH 07/10] exclude docs/_build --- MANIFEST.in | 1 + 1 file changed, 1 insertion(+) diff --git a/MANIFEST.in b/MANIFEST.in index 9175e8900..37949acbc 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -12,3 +12,4 @@ include tox.ini recursive-include docs *.py recursive-include docs *.rst recursive-include docs *.txt +recursive-exclude docs/_build * From efbce686901d08eb562bdee48530c5dae7170988 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wilfredo=20Sa=CC=81nchez?= Date: Mon, 8 Jun 2020 11:04:44 -0700 Subject: [PATCH 08/10] Tox cleanup --- tox.ini | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tox.ini b/tox.ini index 76f7780d0..3195bae3b 100644 --- a/tox.ini +++ b/tox.ini @@ -2,7 +2,7 @@ envlist = flake8, black, mypy - coverage-py{35,36,37,38,py3}-tw{184,192,current,trunk} + coverage-py{35,36,37,38,39,py3}-tw{184,192,current,trunk} coverage_report docs, docs-linkcheck packaging @@ -45,7 +45,7 @@ deps = {test,coverage}: treq==20.3.0 {test,coverage}-py{27,py2}: hypothesis==4.57.1 - {test,coverage}-py{35,36,37,38,py3}: hypothesis==5.8.6 + {test,coverage}-py{35,36,37,38,39,py3}: hypothesis==5.8.6 {test,coverage}: idna==2.8 {test,coverage}-py{27,py2}: mock==3.0.5 # rq.filter: <4 {test,coverage}-py{27,py2}: typing==3.7.4.1 @@ -383,7 +383,9 @@ ignore_missing_imports = True description = generate coverage report -depends = {test,coverage}-py{35,36,37,38,py3}-tw{171,184,current,trunk} +depends = + coverage-py{35,36,37,38,39,py3}-tw{1,2}{0,1,2,3,4,5,6,7,8,9}{0,1,2,3,4,5,6,7,8,9} + coverage-py{35,36,37,38,39,py3}-tw{current,trunk} basepython = {[default]basepython} From 71e4f8977c0728c90feba6fb17eca2c377dbdc7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wilfredo=20Sa=CC=81nchez?= Date: Mon, 8 Jun 2020 11:22:07 -0700 Subject: [PATCH 09/10] Run packaging tests in CI --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index cc4580b03..4cdef8603 100644 --- a/.travis.yml +++ b/.travis.yml @@ -73,6 +73,9 @@ matrix: - python: 3.8 env: TOXENV=docs-linkcheck + - python: 3.8 + env: TOXENV=packaging + allow_failures: # Tests against Twisted trunk are allow to fail, as they are not supported. - env: TOXENV=coverage-py38-twtrunk,codecov From 28d61cf5711904981f8ece28a715e1a0e2ea2b8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wilfredo=20Sa=CC=81nchez?= Date: Mon, 8 Jun 2020 11:22:17 -0700 Subject: [PATCH 10/10] rq.filter --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 3195bae3b..323df0feb 100644 --- a/tox.ini +++ b/tox.ini @@ -44,7 +44,7 @@ deps = zope.interface==5.1.0 {test,coverage}: treq==20.3.0 - {test,coverage}-py{27,py2}: hypothesis==4.57.1 + {test,coverage}-py{27,py2}: hypothesis==4.57.1 # rq.filter: <5 {test,coverage}-py{35,36,37,38,39,py3}: hypothesis==5.8.6 {test,coverage}: idna==2.8 {test,coverage}-py{27,py2}: mock==3.0.5 # rq.filter: <4