diff -Nru twisted-20.3.0/CONTRIBUTING twisted-22.1.0/CONTRIBUTING --- twisted-20.3.0/CONTRIBUTING 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/CONTRIBUTING 1970-01-01 00:00:00.000000000 +0000 @@ -1,31 +0,0 @@ -Contributing to Twisted -======================= - -As an open source project, Twisted welcomes contributions of many forms. - -Examples of contributions include: - -* Code patches -* Documentation improvements -* Bug reports and patch reviews - -Extensive contribution guidelines are available online at: - -https://twistedmatrix.com/trac/wiki/ContributingToTwistedLabs - -Twisted has a Code of Conduct, available at code_of_conduct.md. - -File a ticket at: - -https://twistedmatrix.com/trac/newticket - -Twisted uses Trac to keep track of bugs, feature requests, and associated -patches because GitHub doesn't provide adequate tooling for its community. - -Contributions are managed using GitHub's Pull Requests. -For a PR to be accepted it needs to have: - -* all Travis CI tests passing -* patch coverage of 100% as reported by codecov.io - -The Travis CI tests currently represent only a subset of the all the platforms Twisted supports, so the buildbot tests are still the actual gate for PR acceptance. diff -Nru twisted-20.3.0/CONTRIBUTING.md twisted-22.1.0/CONTRIBUTING.md --- twisted-20.3.0/CONTRIBUTING.md 1970-01-01 00:00:00.000000000 +0000 +++ twisted-22.1.0/CONTRIBUTING.md 2022-02-07 13:12:15.000000000 +0000 @@ -0,0 +1,27 @@ +Contributing to Twisted +======================= + +As an open source project, Twisted welcomes contributions of many forms. +This document summarizes the process. + +Twisted has a [Code of Conduct](./code_of_conduct.md). + +Examples of contributions include: + +* Code patches +* Documentation improvements +* Bug reports +* Pull request reviews + +Contributions are managed using GitHub's Pull Requests. +For a PR to be accepted: + +* It must have an associated Trac ticket ([file one here](https://twistedmatrix.com/trac/newticket)) +* All automated checks must pass +* The changeset must have 100% patch test coverage + +Twisted uses Trac to keep track of bugs, feature requests, and associated +patches because GitHub doesn't provide adequate tooling for its community. +You can log in to Trac with your GitHub account. + +Extensive contribution guidelines are [available online](https://twistedmatrix.com/trac/wiki/ContributingToTwistedLabs). diff -Nru twisted-20.3.0/.coveragerc twisted-22.1.0/.coveragerc --- twisted-20.3.0/.coveragerc 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/.coveragerc 2022-02-07 13:12:15.000000000 +0000 @@ -2,14 +2,19 @@ branch = True parallel = True source = twisted +omit = + .tox/*/tmp/_trial_temp/* [paths] source= src/twisted - build/*/lib/python*/site-packages/twisted - build/*/Lib/site-packages/twisted - build/pypy*/site-packages/twisted + */site-packages/twisted + *\site-packages\twisted [report] precision = 2 ignore_errors = True +exclude_lines = + if TYPE_CHECKING + \s*\.\.\.$ + raise NotImplementedError diff -Nru twisted-20.3.0/debian/changelog twisted-22.1.0/debian/changelog --- twisted-20.3.0/debian/changelog 2021-10-17 10:22:05.000000000 +0000 +++ twisted-22.1.0/debian/changelog 2022-02-23 19:53:17.000000000 +0000 @@ -1,16 +1,121 @@ -twisted (20.3.0-7ubuntu3) jammy; urgency=medium +twisted (22.1.0-2ubuntu2) jammy; urgency=medium - * No-change rebuild to add python3.10. + * Import Literal from typing instead of typing_extensions in + a few more places - -- Matthias Klose Sun, 17 Oct 2021 12:22:05 +0200 + -- Graham Inggs Wed, 23 Feb 2022 19:53:17 +0000 -twisted (20.3.0-7ubuntu1) impish; urgency=medium +twisted (22.1.0-2ubuntu1) jammy; urgency=medium - * Merge from Debian unstable, remaining changes: - + Fix NoneType encode error when multipart body does not include - content-disposition headers + * Import Literal from typing instead of typing_extensions, + see #978536 - -- Graham Inggs Tue, 10 Aug 2021 09:34:55 +0000 + -- Graham Inggs Wed, 23 Feb 2022 07:46:09 +0000 + +twisted (22.1.0-2) unstable; urgency=medium + + * Team upload. + * Removal of a private _PY3 constant breaks treq << 20.9.0. + + -- Andrej Shadura Thu, 17 Feb 2022 11:40:49 +0100 + +twisted (22.1.0-1) unstable; urgency=medium + + * Team upload + + [ Carsten Schoenert ] + * d/gbp.conf: Extend with some more defaults + * d/watch: Update to version 4 + * New upstream version 21.7.0 + * Rebuild patch queue from patch-queue branch + Updated/Rebased/Adjusted/Renamed patches: + 0003-sphinx-theme.patch -> + debian-hacks/Sphinx-Set-html_theme-to-twisteddefault.patch + 0004-localIntersphinx.patch -> + debian-hacks/Sphinx-Set-intersphinx_mapping-for-py3.patch + 0005-insecure-pythonpath.patch -> + debian-hacks/Security-Fix-vulnerable-example-of-PYTHONPATH.patch + 0006-fix-sphinx-import-path.patch -> + debian-hacks/Sphinx-Adjust-setup-of-sys.path.insert.patch + 0009-no-stderr-in-test_ckeygen.patch -> + tests/Tests-Fix-ckeygen-test-writing-to-stderr.patch + 0010-handle-setlocale-test-failure.patch -> + tests/Tests-Handle-setlocale-more-tolerant.patch + 0012-Skip-test-for-empty-cypher-string-openssl-does-not-t.patch -> + tests/Tests-Skip-test-for-empty-cypher-string.patch + 0013-Drop-test_givesMeaningfulErrorMessageIfNoCipherMatch.patch -> + tests/Tests-Drop-test_givesMeaningfulErrorMessageIfNoCipherMatc.patch + 0016-Try-exec-ing-ckeygen3-if-ckeygen-was-not-found.patch -> + debian-hacks/Try-exec-ing-ckeygen3-if-ckeygen-was-not-found.patch + + Removed patches (included upstream): + 0001-wxpython3.0.patch + 0002-combinedlog.patch + 0008-sort-option-keys.patch + 0010-spurious-failure-in-setup-unit-tests.patch + 0011-Ignore-fuction-name-in-SSL-error-code-in-tests-to-wo.patch + 0017-Add-digestmod-parameter-to-HMAC.__init__-invocations.patch + 0018-Make-the-twisted-tests-work-when-pyOpenSSL-deletes-N.patch + 0019-Replace-base64.-string-functions-to-fix-py3.9-suppor.patch + 0020-Fix-imap4-utf-7-codec-lookup-function-for-Python-3.9.patch + 0021-Merge-9652-wiml-mktime-Allow-mktime-to-raise-EOVERFL.patch + 0022-increase-size-of-FFDH-keys-for-conch-testing.patch + 0023-Merge-9801-rodrigc-cgi-Change-import-of-cgi.parse_qs.patch + 0024-fixed-corrupted-iqmp-value-in-test-RSA-key.patch + 0025-Skip-failing-twisted.web.test.test_http.QueryArgumen.patch + * d/control: Add new required build dependencies + Adding pydoctor and python3-typing-extensions as new dependency required + for the package build. + * d/rules: Drop dh_movefiles for python3-twisted-bin + The files which were moved within target aren't existing any more. + * autopkgtest: Adjust testing call + * Rebuild patch queue from patch-queue branch + Added patches: + documentation/docs-Don-t-depend-on-git-stuff.patch + documentation/docs-conf.py-Adjust-the-intersphinx-mapping.patch + documentation/docs-conf.py-Don-t-use-intersphinx-within-pydoctor_args.patch + privacy/Privacy-Don-t-sideload-Google-Analytics.patch + tests/Test-Ignore-test_failure.py-file.patch + tests/Testing-Ignore-test-around-git-tooling.patch + tests/Tests-Ignore-test_listingModulesAlreadyImport.patch + tests/Tests-Ignore-test_unicodeLogFileUTF8.patch + tests/Tests-Ignore-tests-with-some-version-checking.patch + + Adding some more required patches so the build and a later done + autopkgtest will succeed. + * Lintian: Remove override for python3-twisted + * d/control: Remove packages python3-twisted-{bin,dbg} + These packages arn't build any more, the source for previous created SO + files are now living within a own new upstream project. + * d/*control: Running wrap-and-sort -ast + * d/control: Update Standards-Version to 4.6.0 + No further changes needed. + * d/rules: Ignore things around previous apidocs folder + * d/control: Adjust and update Build-Depends + Drop python2-doc and python3-all-{dbg,dev}, adding an versioned + dependency on pydoctor >= 21.12.1. + * d/control: Update suggestion of python3-twisted + * d/rules: Move over to debhelper style + Using debhelper targets within d/rules improves the readability + enormously and decreases the amount the really needed content to an + minimum. + * metadata: Update to serve more content + Extend data to also include the fields for Bug-Database, Bug-Submit and + FAQ. + * d/control: Adding entry Rules-Requires-Root: no + * d/d/options: Drop config file + * d/copyright: Update to current year data + * d/python3-twisted.post{inst.rm}: Uniform indentation style + * d/rules: Adjust shebang to use python3 in twisted-doc + * Lintian: Adding an override for twisted-doc + + [ Andrej Shadura ] + * New upstream release. + * Update dependency versions. + * Refresh patches. + * Use dh-sequence-python3 instead of --with python3. + + -- Andrej Shadura Thu, 10 Feb 2022 14:48:43 +0100 twisted (20.3.0-7) unstable; urgency=medium @@ -34,20 +139,6 @@ -- Ole Streicher Sat, 24 Apr 2021 14:24:44 +0200 -twisted (20.3.0-4ubuntu1) hirsute; urgency=medium - - * Fix NoneType encode error when multipart body does not include - content-disposition headers (LP: #1915819) - - d/p/lp1915819-Fix-nonetype-encode-error.patch - - -- Victor Manuel Tapia King Wed, 17 Feb 2021 12:48:20 +0100 - -twisted (20.3.0-4build1) hirsute; urgency=medium - - * No change rebuild with fixed ownership. - - -- Dimitri John Ledkov Tue, 16 Feb 2021 15:22:33 +0000 - twisted (20.3.0-4) unstable; urgency=medium * Team upload. diff -Nru twisted-20.3.0/debian/control twisted-22.1.0/debian/control --- twisted-20.3.0/debian/control 2021-08-10 09:34:55.000000000 +0000 +++ twisted-22.1.0/debian/control 2022-02-23 06:07:07.000000000 +0000 @@ -3,94 +3,76 @@ Priority: optional Maintainer: Ubuntu Developers XSBC-Original-Maintainer: Debian Python Team -Uploaders: Matthias Klose -Build-Depends: debhelper-compat (= 13), - dh-python, - patch, - python2-doc, - python3-all-dev, - python3-all-dbg, - python3-hamcrest, - python3-zope.interface (>= 4.0.2), - python3-setuptools, - python3-incremental, - python3-constantly, - python3-automat (>= 0.6.0), - python3-hyperlink, - python3-doc -Build-Depends-Indep: python3-sphinx -Standards-Version: 4.5.0 +Uploaders: + Matthias Klose , +Build-Depends: + debhelper-compat (= 13), + dh-sequence-python3, + patch, + pydoctor (>= 21.12.1), + python3-all, + python3-attr (>= 19.2.0), + python3-automat (>= 0.8.0), + python3-constantly, + python3-doc, + python3-hamcrest, + python3-hyperlink, + python3-incremental (>= 21.3.0), + python3-setuptools, +# python3-typing-extensions, + python3-zope.interface (>= 4.4.2), +Build-Depends-Indep: + python3-sphinx , +Rules-Requires-Root: no +Standards-Version: 4.6.0 Homepage: https://twistedmatrix.com/ Vcs-Browser: https://salsa.debian.org/python-team/packages/twisted Vcs-Git: https://salsa.debian.org/python-team/packages/twisted.git Package: python3-twisted Architecture: all -Depends: ${python3:Depends}, - python3-twisted-bin (>= ${source:Version}), - python3-zope.interface (>= 4.0.2), - python3-automat (>= 0.6.0), - python3-attr (>= 17.1.0), - ${misc:Depends}, - python3-openssl, - python3-idna, - python3-service-identity (>= 18.1.0), - python3-hamcrest, - python3-cryptography (>= 2.5), - python3-bcrypt (>= 3.0.0), -Suggests: python3-tk, - python3-pampy, - python3-qt4, - python3-serial, - python3-wxgtk2.8 -Provides: ${python:Provides} -Conflicts: python3-twisted-experimental -Replaces: python3-twisted-experimental -Breaks: python3-h2 (<< 3.0.0) +Depends: + python3-bcrypt (>= 3.0.0), + python3-cryptography (>= 2.5), + python3-hamcrest, + python3-idna, + python3-openssl, + python3-service-identity (>= 18.1.0), + ${misc:Depends}, + ${python3:Depends}, +Suggests: + python3-pampy, + python3-serial, + python3-tk, + python3-wxgtk4.0, +Provides: + ${python:Provides}, +Conflicts: + python3-twisted-experimental, +Replaces: + python3-twisted-experimental, +Breaks: + python3-h2 (<< 3.0.0), + python3-treq (<< 20.9.0), Description: Event-based framework for internet applications It includes a web server, a telnet server, a multiplayer RPG engine, a generic client and server for remote object access, and APIs for creating new protocols. -Package: python3-twisted-bin -Architecture: any -Multi-Arch: same -Depends: ${python3:Depends}, - ${shlibs:Depends}, - ${misc:Depends} -Provides: ${python:Provides} -Suggests: python3-twisted-bin-dbg -Description: Event-based framework for internet applications - It includes a web server, a telnet server, a multiplayer RPG engine, a - generic client and server for remote object access, and APIs for creating - new protocols. - -Package: python3-twisted-bin-dbg -Section: debug -Architecture: any -Multi-Arch: same -Depends: python3-zope.interface-dbg, - python3-twisted-bin (= ${binary:Version}), - python3-dbg, ${shlibs:Depends}, - ${misc:Depends} -Description: Event-based framework for internet applications (debug extension) - It includes a web server, a telnet server, a multiplayer RPG engine, a - generic client and server for remote object access, and APIs for creating - new protocols. - . - This package contains the extension built for the Python debug interpreter. - Package: twisted-doc Section: doc Build-Profiles: Architecture: all Multi-Arch: foreign -Depends: ${misc:Depends}, - ${sphinxdoc:Depends} -Suggests: python3-twisted -Recommends: www-browser | postscript-viewer | pdf-viewer +Depends: + ${misc:Depends}, + ${sphinxdoc:Depends}, +Suggests: + python3-twisted, +Recommends: + www-browser | postscript-viewer | pdf-viewer, Description: Official documentation of Twisted - This contains various HOWTOs and overviews in various formats + This contains various HOWTOs and overviews in various formats. . Twisted is an event-based framework for internet applications. It includes a web server, a telnet server, a multiplayer RPG engine, a diff -Nru twisted-20.3.0/debian/copyright twisted-22.1.0/debian/copyright --- twisted-20.3.0/debian/copyright 2021-04-24 16:36:24.000000000 +0000 +++ twisted-22.1.0/debian/copyright 2022-02-17 10:40:49.000000000 +0000 @@ -3,7 +3,7 @@ Files: * Copyright: - 2001-2018 Twisted Matrix Laboratories + 2001-2021 Twisted Matrix Laboratories Allen Short Amber Hawkie Brown Andrew Bennetts diff -Nru twisted-20.3.0/debian/gbp.conf twisted-22.1.0/debian/gbp.conf --- twisted-20.3.0/debian/gbp.conf 2021-04-24 16:36:24.000000000 +0000 +++ twisted-22.1.0/debian/gbp.conf 2022-02-17 10:40:49.000000000 +0000 @@ -1,6 +1,12 @@ +# Configuration file for git-buildpackage and friends + [DEFAULT] +# use pristine-tar pristine-tar = True +# generate gz compressed orig tarball +compression = gz +debian-branch = debian/master +upstream-branch = upstream -[buildpackage] -export-dir = ../build-area/ - +[pq] +patch-numbers = False diff -Nru twisted-20.3.0/debian/patches/0001-wxpython3.0.patch twisted-22.1.0/debian/patches/0001-wxpython3.0.patch --- twisted-20.3.0/debian/patches/0001-wxpython3.0.patch 2021-04-24 16:36:24.000000000 +0000 +++ twisted-22.1.0/debian/patches/0001-wxpython3.0.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,36 +0,0 @@ -From: Matthias Klose -Date: Thu, 20 Oct 2016 04:34:06 +0000 -Subject: wxpython3.0 - -Fix imports from the wx package. ---- - src/twisted/internet/wxreactor.py | 6 +++--- - 1 file changed, 3 insertions(+), 3 deletions(-) - -diff --git a/src/twisted/internet/wxreactor.py b/src/twisted/internet/wxreactor.py -index ae24c59..b15e43a 100644 ---- a/src/twisted/internet/wxreactor.py -+++ b/src/twisted/internet/wxreactor.py -@@ -29,11 +29,11 @@ except ImportError: - from Queue import Empty, Queue - - try: -- from wx import PySimpleApp as wxPySimpleApp, CallAfter as wxCallAfter, \ -+ from wx import App as wxApp, CallAfter as wxCallAfter, \ - Timer as wxTimer - except ImportError: - # older version of wxPython: -- from wxPython.wx import wxPySimpleApp, wxCallAfter, wxTimer -+ from wxPython.wx import wxApp, wxCallAfter, wxTimer - - from twisted.python import log, runtime - from twisted.internet import _threadedselect -@@ -129,7 +129,7 @@ class WxReactor(_threadedselect.ThreadedSelectReactor): - if not hasattr(self, "wxapp"): - log.msg("registerWxApp() was not called on reactor, " - "registering my own wxApp instance.") -- self.registerWxApp(wxPySimpleApp()) -+ self.registerWxApp(wxApp(False)) - - # start select() thread: - self.interleave(self._runInMainThread, diff -Nru twisted-20.3.0/debian/patches/0002-combinedlog.patch twisted-22.1.0/debian/patches/0002-combinedlog.patch --- twisted-20.3.0/debian/patches/0002-combinedlog.patch 2021-04-24 16:36:24.000000000 +0000 +++ twisted-22.1.0/debian/patches/0002-combinedlog.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,103 +0,0 @@ -From: Matthias Klose -Date: Thu, 20 Oct 2016 04:34:06 +0000 -Subject: combinedlog - -Preserve backward-compatibility in the way the client IP of a request -is logged by the twisted.web HTTP server. - -Bug: https://twistedmatrix.com/trac/ticket/7730 -Bug-Debian https://bugs.debian.org/772629 ---- - src/twisted/web/http.py | 2 +- - src/twisted/web/test/test_web.py | 16 ++++++++-------- - 2 files changed, 9 insertions(+), 9 deletions(-) - -diff --git a/src/twisted/web/http.py b/src/twisted/web/http.py -index 2599470..6a27bca 100644 ---- a/src/twisted/web/http.py -+++ b/src/twisted/web/http.py -@@ -2631,7 +2631,7 @@ def combinedLogFormatter(timestamp, request): - referrer = _escape(request.getHeader(b"referer") or b"-") - agent = _escape(request.getHeader(b"user-agent") or b"-") - line = ( -- u'"%(ip)s" - - %(timestamp)s "%(method)s %(uri)s %(protocol)s" ' -+ u'%(ip)s - - %(timestamp)s "%(method)s %(uri)s %(protocol)s" ' - u'%(code)d %(length)s "%(referrer)s" "%(agent)s"' % dict( - ip=_escape(ip), - timestamp=timestamp, -diff --git a/src/twisted/web/test/test_web.py b/src/twisted/web/test/test_web.py -index 387e830..539d099 100644 ---- a/src/twisted/web/test/test_web.py -+++ b/src/twisted/web/test/test_web.py -@@ -1438,7 +1438,7 @@ class AccessLogTestsMixin(object): - - self.assertEqual( - # Client IP -- b'"1.2.3.4" ' -+ b'1.2.3.4 ' - # Some blanks we never fill in - b'- - ' - # The current time (circa 1234567890) -@@ -1531,7 +1531,7 @@ class CombinedLogFormatterTests(unittest.TestCase): - - line = http.combinedLogFormatter(timestamp, request) - self.assertEqual( -- u'"evil x-forwarded-for \\x80" - - [13/Feb/2009:23:31:30 +0000] ' -+ u'evil x-forwarded-for \\x80 - - [13/Feb/2009:23:31:30 +0000] ' - u'"POS\\x81 /dummy HTTP/1.0" 123 - "evil \\x83" "evil \\x84"', - line) - -@@ -1663,7 +1663,7 @@ class LogEscapingTests(unittest.TestCase): - self.site._logDateTime = "[%02d/%3s/%4d:%02d:%02d:%02d +0000]" % ( - 25, 'Oct', 2004, 12, 31, 59) - self.assertLogs( -- b'"1.2.3.4" - - [25/Oct/2004:12:31:59 +0000] ' -+ b'1.2.3.4 - - [25/Oct/2004:12:31:59 +0000] ' - b'"GET /dummy HTTP/1.0" 123 - "-" "-"\n') - - -@@ -1675,7 +1675,7 @@ class LogEscapingTests(unittest.TestCase): - 25, 'Oct', 2004, 12, 31, 59) - self.request.method = b'G"T' - self.assertLogs( -- b'"1.2.3.4" - - [25/Oct/2004:12:31:59 +0000] ' -+ b'1.2.3.4 - - [25/Oct/2004:12:31:59 +0000] ' - b'"G\\"T /dummy HTTP/1.0" 123 - "-" "-"\n') - - -@@ -1687,7 +1687,7 @@ class LogEscapingTests(unittest.TestCase): - 25, 'Oct', 2004, 12, 31, 59) - self.request.uri = b'/dummy"withquote' - self.assertLogs( -- b'"1.2.3.4" - - [25/Oct/2004:12:31:59 +0000] ' -+ b'1.2.3.4 - - [25/Oct/2004:12:31:59 +0000] ' - b'"GET /dummy\\"withquote HTTP/1.0" 123 - "-" "-"\n') - - -@@ -1699,7 +1699,7 @@ class LogEscapingTests(unittest.TestCase): - 25, 'Oct', 2004, 12, 31, 59) - self.request.clientproto = b'HT"P/1.0' - self.assertLogs( -- b'"1.2.3.4" - - [25/Oct/2004:12:31:59 +0000] ' -+ b'1.2.3.4 - - [25/Oct/2004:12:31:59 +0000] ' - b'"GET /dummy HT\\"P/1.0" 123 - "-" "-"\n') - - -@@ -1714,7 +1714,7 @@ class LogEscapingTests(unittest.TestCase): - b'referer', - b'http://malicious" ".website.invalid') - self.assertLogs( -- b'"1.2.3.4" - - [25/Oct/2004:12:31:59 +0000] ' -+ b'1.2.3.4 - - [25/Oct/2004:12:31:59 +0000] ' - b'"GET /dummy HTTP/1.0" 123 - ' - b'"http://malicious\\" \\".website.invalid" "-"\n') - -@@ -1729,7 +1729,7 @@ class LogEscapingTests(unittest.TestCase): - self.request.requestHeaders.addRawHeader(b'user-agent', - b'Malicious Web" Evil') - self.assertLogs( -- b'"1.2.3.4" - - [25/Oct/2004:12:31:59 +0000] ' -+ b'1.2.3.4 - - [25/Oct/2004:12:31:59 +0000] ' - b'"GET /dummy HTTP/1.0" 123 - "-" "Malicious Web\\" Evil"\n') - - diff -Nru twisted-20.3.0/debian/patches/0003-sphinx-theme.patch twisted-22.1.0/debian/patches/0003-sphinx-theme.patch --- twisted-20.3.0/debian/patches/0003-sphinx-theme.patch 2021-04-24 16:36:24.000000000 +0000 +++ twisted-22.1.0/debian/patches/0003-sphinx-theme.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,22 +0,0 @@ -From: Matthias Klose -Date: Thu, 20 Oct 2016 04:34:07 +0000 -Subject: sphinx-theme - -Set the sphinx theme. ---- - docs/conf.py | 2 ++ - 1 file changed, 2 insertions(+) - -diff --git a/docs/conf.py b/docs/conf.py -index 299b5b4..2d23d59 100644 ---- a/docs/conf.py -+++ b/docs/conf.py -@@ -120,6 +120,8 @@ on_rtd = os.environ.get('READTHEDOCS', None) == 'True' - if not on_rtd: - html_theme = 'twistedtrac' - -+html_theme = 'twisteddefault' -+ - # Theme options are theme-specific and customize the look and feel of a theme - # further. For a list of options available for each theme, see the - # documentation. diff -Nru twisted-20.3.0/debian/patches/0004-localIntersphinx.patch twisted-22.1.0/debian/patches/0004-localIntersphinx.patch --- twisted-20.3.0/debian/patches/0004-localIntersphinx.patch 2021-04-24 16:36:24.000000000 +0000 +++ twisted-22.1.0/debian/patches/0004-localIntersphinx.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,27 +0,0 @@ -From: Free Ekanayaka -Date: Fri, 21 Oct 2016 07:37:26 +0000 -Subject: localIntersphinx - -Use local copies of object.inv for building documentation. - -Forwarded: not-needed -Bug-Debian: https://bugs.debian.org/836169 ---- - docs/conf.py | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/docs/conf.py b/docs/conf.py -index 2d23d59..a806c64 100644 ---- a/docs/conf.py -+++ b/docs/conf.py -@@ -359,8 +359,8 @@ traclinks_base_url = 'https://twistedmatrix.com/trac' - # tuple of (, ). - # The inventory file may be None to use the default location at the given URI. - intersphinx_mapping = { -- 'py2': ('http://docs.python.org/2.7', None), -- 'py3': ('http://docs.python.org/3.3', None), -+ 'py2': ('/usr/share/doc/python-doc/html', None), -+ 'py3': ('/usr/share/doc/python3-doc/html', None), - } - # How long to cache remote inventories. Positive is a number of days, - # negative means infinite. The default is 5 days, which should be fine diff -Nru twisted-20.3.0/debian/patches/0005-insecure-pythonpath.patch twisted-22.1.0/debian/patches/0005-insecure-pythonpath.patch --- twisted-20.3.0/debian/patches/0005-insecure-pythonpath.patch 2021-04-24 16:36:24.000000000 +0000 +++ twisted-22.1.0/debian/patches/0005-insecure-pythonpath.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,26 +0,0 @@ -From: Free Ekanayaka -Date: Fri, 21 Oct 2016 09:21:44 +0000 -Subject: insecure-pythonpath - -Fix vulnerable example of PYTHONPATH. - -Forwarded: no -Bug-Debian: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=605190 ---- - docs/core/howto/quotes.rst | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git a/docs/core/howto/quotes.rst b/docs/core/howto/quotes.rst -index 163d67b..d171a07 100644 ---- a/docs/core/howto/quotes.rst -+++ b/docs/core/howto/quotes.rst -@@ -65,7 +65,8 @@ following: - #. Add the ``TwistedQuotes`` directory's *parent* to your Python - path. For example, if the TwistedQuotes directory's path is - ``/mystuff/TwistedQuotes`` or ``c:\mystuff\TwistedQuotes`` -- add ``/mystuff`` to your Python path. On UNIX this would be ``export PYTHONPATH=/mystuff:$PYTHONPATH`` , on Microsoft -+ add ``/mystuff`` to your Python path. On UNIX this would be ``export PYTHONPATH=/mystuff${PYTHONPATH:+:$PYTHONPATH} -+`` , on Microsoft - Windows change the ``PYTHONPATH`` variable through the - Systems Properties dialog by adding ``;c:\mystuff`` at the - end. diff -Nru twisted-20.3.0/debian/patches/0006-fix-sphinx-import-path.patch twisted-22.1.0/debian/patches/0006-fix-sphinx-import-path.patch --- twisted-20.3.0/debian/patches/0006-fix-sphinx-import-path.patch 2021-04-24 16:36:24.000000000 +0000 +++ twisted-22.1.0/debian/patches/0006-fix-sphinx-import-path.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,23 +0,0 @@ -From: Free Ekanayaka -Date: Fri, 4 Nov 2016 07:26:00 +0000 -Subject: fix-sphinx-import-path - -Adjust the import path in the Sphinx configuration file to -match the new source files location (src/). ---- - docs/conf.py | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/docs/conf.py b/docs/conf.py -index a806c64..f7f8f93 100644 ---- a/docs/conf.py -+++ b/docs/conf.py -@@ -19,7 +19,7 @@ import os - # add these directories to sys.path here. If the directory is relative to the - # documentation root, use os.path.abspath to make it absolute, like shown here. - sys.path.insert(0, os.path.abspath('./_extensions')) --sys.path.insert(0, os.path.abspath('..')) -+sys.path.insert(0, os.path.abspath('../src')) - - # -- General configuration ------------------------------------------------ - diff -Nru twisted-20.3.0/debian/patches/0008-sort-option-keys.patch twisted-22.1.0/debian/patches/0008-sort-option-keys.patch --- twisted-20.3.0/debian/patches/0008-sort-option-keys.patch 2021-04-24 16:36:24.000000000 +0000 +++ twisted-22.1.0/debian/patches/0008-sort-option-keys.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,25 +0,0 @@ -From: Free Ekanayaka -Date: Sat, 26 Nov 2016 10:40:11 +0000 -Subject: sort-option-keys - -Fix flaky twisted.test.test_main.MainTests.test_twisted which fails -if options are not in the same order. - -See https://twistedmatrix.com/trac/attachment/ticket/8923. ---- - src/twisted/python/usage.py | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/src/twisted/python/usage.py b/src/twisted/python/usage.py -index 8114861..a46541d 100644 ---- a/src/twisted/python/usage.py -+++ b/src/twisted/python/usage.py -@@ -388,7 +388,7 @@ class Options(dict): - dct = {} - reflect.addMethodNamesToDict(self.__class__, dct, "opt_") - -- for name in dct.keys(): -+ for name in sorted(dct.keys()): - method = getattr(self, 'opt_'+name) - - takesArg = not flagFunction(method, name) diff -Nru twisted-20.3.0/debian/patches/0009-no-stderr-in-test_ckeygen.patch twisted-22.1.0/debian/patches/0009-no-stderr-in-test_ckeygen.patch --- twisted-20.3.0/debian/patches/0009-no-stderr-in-test_ckeygen.patch 2021-04-24 16:36:24.000000000 +0000 +++ twisted-22.1.0/debian/patches/0009-no-stderr-in-test_ckeygen.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,35 +0,0 @@ -From: Free Ekanayaka -Date: Sat, 26 Nov 2016 11:37:52 +0000 -Subject: no-stderr-in-test_ckeygen - -Fix an test writing to stderr. See: - -https://twistedmatrix.com/trac/ticket/8924 ---- - src/twisted/conch/test/test_ckeygen.py | 6 +++++- - 1 file changed, 5 insertions(+), 1 deletion(-) - -diff --git a/src/twisted/conch/test/test_ckeygen.py b/src/twisted/conch/test/test_ckeygen.py -index a840085..41a0208 100644 ---- a/src/twisted/conch/test/test_ckeygen.py -+++ b/src/twisted/conch/test/test_ckeygen.py -@@ -7,6 +7,7 @@ Tests for L{twisted.conch.scripts.ckeygen}. - - import getpass - import sys -+import os - import subprocess - - from io import BytesIO, StringIO -@@ -94,7 +95,10 @@ class KeyGenTests(TestCase): - def test_runBadKeytype(self): - filename = self.mktemp() - with self.assertRaises(subprocess.CalledProcessError): -- subprocess.check_call(['ckeygen', '-t', 'foo', '-f', filename]) -+ with open(os.devnull, "rb") as devnull: -+ subprocess.check_call( -+ ['ckeygen', '-t', 'foo', '-f', filename], -+ stderr=devnull) - - - diff -Nru twisted-20.3.0/debian/patches/0010-handle-setlocale-test-failure.patch twisted-22.1.0/debian/patches/0010-handle-setlocale-test-failure.patch --- twisted-20.3.0/debian/patches/0010-handle-setlocale-test-failure.patch 2021-04-24 16:36:24.000000000 +0000 +++ twisted-22.1.0/debian/patches/0010-handle-setlocale-test-failure.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,27 +0,0 @@ -From: Free Ekanayaka -Date: Sat, 18 Feb 2017 13:50:23 +0000 -Subject: handle-setlocale-test-failure - -Gracefully handle setlocale failures during the test suite (e.g -when running in a container). ---- - src/twisted/conch/test/test_cftp.py | 6 ++++-- - 1 file changed, 4 insertions(+), 2 deletions(-) - -diff --git a/src/twisted/conch/test/test_cftp.py b/src/twisted/conch/test/test_cftp.py -index 58eb5cc..4b3f5f2 100644 ---- a/src/twisted/conch/test/test_cftp.py -+++ b/src/twisted/conch/test/test_cftp.py -@@ -192,8 +192,10 @@ class ListingTests(TestCase): - locale.setlocale(locale.LC_ALL, "es_AR.UTF8") - except locale.Error: - test_localeIndependent.skip = "The es_AR.UTF8 locale is not installed." -- finally: -- locale.setlocale(locale.LC_ALL, currentLocale) -+ else: -+ locale.setlocale(locale.LC_ALL, currentLocale) -+ except: -+ pass - - - def test_newSingleDigitDayOfMonth(self): diff -Nru twisted-20.3.0/debian/patches/0010-spurious-failure-in-setup-unit-tests.patch twisted-22.1.0/debian/patches/0010-spurious-failure-in-setup-unit-tests.patch --- twisted-20.3.0/debian/patches/0010-spurious-failure-in-setup-unit-tests.patch 2021-04-24 16:36:24.000000000 +0000 +++ twisted-22.1.0/debian/patches/0010-spurious-failure-in-setup-unit-tests.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,21 +0,0 @@ -From: Free Ekanayaka -Date: Mon, 28 Aug 2017 17:05:34 +0000 -Subject: spurious-failure-in-setup-unit-tests - ---- - src/twisted/python/test/test_setup.py | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/src/twisted/python/test/test_setup.py b/src/twisted/python/test/test_setup.py -index e55e29c..1ecca8d 100644 ---- a/src/twisted/python/test/test_setup.py -+++ b/src/twisted/python/test/test_setup.py -@@ -92,7 +92,7 @@ class OptionalDependenciesTests(TestCase): - The extras need to be parsed with pkg_resources.parse_requirements(), - which returns a generator. - """ -- extras = dict(im_an_extra_dependency="thing") -+ extras = dict(im_an_extra_dependency=["thing"]) - attrs = dict(extras_require=extras) - distribution = Distribution(attrs) - diff -Nru twisted-20.3.0/debian/patches/0011-Ignore-fuction-name-in-SSL-error-code-in-tests-to-wo.patch twisted-22.1.0/debian/patches/0011-Ignore-fuction-name-in-SSL-error-code-in-tests-to-wo.patch --- twisted-20.3.0/debian/patches/0011-Ignore-fuction-name-in-SSL-error-code-in-tests-to-wo.patch 2021-04-24 16:36:24.000000000 +0000 +++ twisted-22.1.0/debian/patches/0011-Ignore-fuction-name-in-SSL-error-code-in-tests-to-wo.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,27 +0,0 @@ -From: Balint Reczey -Date: Wed, 28 Nov 2018 18:54:49 +0100 -Subject: Ignore fuction name in SSL error code in tests to work across - OpenSSL versions - ---- - src/twisted/test/test_tcp.py | 7 ++++++- - 1 file changed, 6 insertions(+), 1 deletion(-) - -diff --git a/src/twisted/test/test_tcp.py b/src/twisted/test/test_tcp.py -index b8791bb..a3fd7d8 100644 ---- a/src/twisted/test/test_tcp.py -+++ b/src/twisted/test/test_tcp.py -@@ -1193,7 +1193,12 @@ class ProperlyCloseFilesMixin: - expectedErrorCode = self.getHandleErrorCode() - exception = self.assertRaises( - self.getHandleExceptionType(), client.handle.send, b'bytes') -- self.assertEqual(exception.args[0], expectedErrorCode) -+ try: -+ self.assertEqual(exception.args[0][0][0], expectedErrorCode[0][0]) -+ self.assertEqual(exception.args[0][0][2], expectedErrorCode[0][2]) -+ except TypeError: -+ self.assertEqual(exception.args[0], expectedErrorCode) -+ - clientDeferred.addCallback(clientDisconnected) - - def cleanup(passthrough): diff -Nru twisted-20.3.0/debian/patches/0012-Skip-test-for-empty-cypher-string-openssl-does-not-t.patch twisted-22.1.0/debian/patches/0012-Skip-test-for-empty-cypher-string-openssl-does-not-t.patch --- twisted-20.3.0/debian/patches/0012-Skip-test-for-empty-cypher-string-openssl-does-not-t.patch 2021-04-24 16:36:24.000000000 +0000 +++ twisted-22.1.0/debian/patches/0012-Skip-test-for-empty-cypher-string-openssl-does-not-t.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,22 +0,0 @@ -From: Balint Reczey -Date: Wed, 28 Nov 2018 20:39:26 +0100 -Subject: Skip test for empty cypher string, openssl does not throw error now - -See: https://github.com/openssl/openssl/issues/7725 ---- - src/twisted/test/test_sslverify.py | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/src/twisted/test/test_sslverify.py b/src/twisted/test/test_sslverify.py -index fb6b01b..d012d5b 100644 ---- a/src/twisted/test/test_sslverify.py -+++ b/src/twisted/test/test_sslverify.py -@@ -2706,7 +2706,7 @@ class ExpandCipherStringTests(unittest.TestCase): - if skipSSL: - skip = skipSSL - -- def test_doesNotStumbleOverEmptyList(self): -+ def _test_doesNotStumbleOverEmptyList(self): - """ - If the expanded cipher list is empty, an empty L{list} is returned. - """ diff -Nru twisted-20.3.0/debian/patches/0013-Drop-test_givesMeaningfulErrorMessageIfNoCipherMatch.patch twisted-22.1.0/debian/patches/0013-Drop-test_givesMeaningfulErrorMessageIfNoCipherMatch.patch --- twisted-20.3.0/debian/patches/0013-Drop-test_givesMeaningfulErrorMessageIfNoCipherMatch.patch 2021-04-24 16:36:24.000000000 +0000 +++ twisted-22.1.0/debian/patches/0013-Drop-test_givesMeaningfulErrorMessageIfNoCipherMatch.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,35 +0,0 @@ -From: Balint Reczey -Date: Wed, 28 Nov 2018 22:09:42 +0100 -Subject: Drop test_givesMeaningfulErrorMessageIfNoCipherMatches - -with OpenSSL 1.1.1 no ValueError is raised ---- - src/twisted/test/test_sslverify.py | 15 --------------- - 1 file changed, 15 deletions(-) - -diff --git a/src/twisted/test/test_sslverify.py b/src/twisted/test/test_sslverify.py -index d012d5b..fc3bebc 100644 ---- a/src/twisted/test/test_sslverify.py -+++ b/src/twisted/test/test_sslverify.py -@@ -898,21 +898,6 @@ class OpenSSLOptionsTests(OpenSSLOptionsTestsMixin, unittest.TestCase): - self.assertEqual(opts._cipherString.encode('ascii'), ctx._cipherList) - - -- def test_givesMeaningfulErrorMessageIfNoCipherMatches(self): -- """ -- If there is no valid cipher that matches the user's wishes, -- a L{ValueError} is raised. -- """ -- self.assertRaises( -- ValueError, -- sslverify.OpenSSLCertificateOptions, -- privateKey=self.sKey, -- certificate=self.sCert, -- acceptableCiphers= -- sslverify.OpenSSLAcceptableCiphers.fromOpenSSLCipherString('') -- ) -- -- - def test_honorsAcceptableCiphersArgument(self): - """ - If acceptable ciphers are passed, they are used. diff -Nru twisted-20.3.0/debian/patches/0015-Fix-tests-to-expect-new-web-request-logging-format.patch twisted-22.1.0/debian/patches/0015-Fix-tests-to-expect-new-web-request-logging-format.patch --- twisted-20.3.0/debian/patches/0015-Fix-tests-to-expect-new-web-request-logging-format.patch 2021-04-24 16:36:24.000000000 +0000 +++ twisted-22.1.0/debian/patches/0015-Fix-tests-to-expect-new-web-request-logging-format.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,30 +0,0 @@ -From: Balint Reczey -Date: Tue, 4 Dec 2018 19:18:14 +0100 -Subject: Fix tests to expect new web request logging format - ---- - src/twisted/web/test/test_web.py | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/src/twisted/web/test/test_web.py b/src/twisted/web/test/test_web.py -index 539d099..eba0b0b 100644 ---- a/src/twisted/web/test/test_web.py -+++ b/src/twisted/web/test/test_web.py -@@ -1549,7 +1549,7 @@ class CombinedLogFormatterTests(unittest.TestCase): - - line = http.combinedLogFormatter(timestamp, request) - self.assertEqual( -- u'"::1" - - [13/Feb/2009:23:31:30 +0000] ' -+ u'::1 - - [13/Feb/2009:23:31:30 +0000] ' - u'"GET /dummy HTTP/1.0" 123 - "-" "-"', - line) - -@@ -1573,7 +1573,7 @@ class CombinedLogFormatterTests(unittest.TestCase): - request.client = UnknowableAddress() - - line = http.combinedLogFormatter(timestamp, request) -- self.assertTrue(line.startswith(u'"-" ')) -+ self.assertTrue(line.startswith(u'- ')) - - - diff -Nru twisted-20.3.0/debian/patches/0016-Try-exec-ing-ckeygen3-if-ckeygen-was-not-found.patch twisted-22.1.0/debian/patches/0016-Try-exec-ing-ckeygen3-if-ckeygen-was-not-found.patch --- twisted-20.3.0/debian/patches/0016-Try-exec-ing-ckeygen3-if-ckeygen-was-not-found.patch 2021-04-24 16:36:24.000000000 +0000 +++ twisted-22.1.0/debian/patches/0016-Try-exec-ing-ckeygen3-if-ckeygen-was-not-found.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,49 +0,0 @@ -From: Sergio Durigan Junior -Date: Fri, 12 Feb 2021 16:40:24 -0500 -Subject: Try exec'ing ckeygen3 if ckeygen was not found - -The ckeygen binary is named ckeygen3 "now". - -Author: Sergio Durigan Junior -Forwarded: not-needed -Last-Updated: 2021-02-13 ---- - src/twisted/conch/test/test_ckeygen.py | 18 +++++++++++++----- - 1 file changed, 13 insertions(+), 5 deletions(-) - -diff --git a/src/twisted/conch/test/test_ckeygen.py b/src/twisted/conch/test/test_ckeygen.py -index f4d5505..6024238 100644 ---- a/src/twisted/conch/test/test_ckeygen.py -+++ b/src/twisted/conch/test/test_ckeygen.py -@@ -72,7 +72,11 @@ class KeyGenTests(TestCase): - args.extend(['-b', keySize]) - if privateKeySubtype is not None: - args.extend(['--private-key-subtype', privateKeySubtype]) -- subprocess.call(args) -+ try: -+ subprocess.call(args) -+ except FileNotFoundError: -+ args[0] = 'ckeygen3' -+ subprocess.call(args) - privKey = Key.fromFile(filename) - pubKey = Key.fromFile(filename + '.pub') - if keyType == 'ecdsa': -@@ -102,10 +106,14 @@ class KeyGenTests(TestCase): - filename = self.mktemp() - with self.assertRaises(subprocess.CalledProcessError): - with open(os.devnull, "rb") as devnull: -- subprocess.check_call( -- ['ckeygen', '-t', 'foo', '-f', filename], -- stderr=devnull) -- -+ try: -+ subprocess.check_call( -+ ['ckeygen', '-t', 'foo', '-f', filename], -+ stderr=devnull) -+ except FileNotFoundError: -+ subprocess.check_call( -+ ['ckeygen3', '-t', 'foo', '-f', filename], -+ stderr=devnull) - - - def test_enumrepresentation(self): diff -Nru twisted-20.3.0/debian/patches/0017-Add-digestmod-parameter-to-HMAC.__init__-invocations.patch twisted-22.1.0/debian/patches/0017-Add-digestmod-parameter-to-HMAC.__init__-invocations.patch --- twisted-20.3.0/debian/patches/0017-Add-digestmod-parameter-to-HMAC.__init__-invocations.patch 2021-04-24 16:36:24.000000000 +0000 +++ twisted-22.1.0/debian/patches/0017-Add-digestmod-parameter-to-HMAC.__init__-invocations.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,94 +0,0 @@ -From: Craig Rodrigues -Date: Sun, 12 Apr 2020 14:28:23 -0700 -Subject: Add digestmod parameter to HMAC.__init__() invocations - -This parameter is now required on Python 3.8+ - -Author: Craig Rodrigues -Origin: upstream, https://github.com/twisted/twisted/commit/f58c702dac599695bbe9a3e047f8550e11274cdc -Last-Updated: 2021-02-13 ---- - src/twisted/cred/credentials.py | 3 ++- - src/twisted/cred/test/test_cramauth.py | 11 ++++++++--- - src/twisted/mail/test/test_pop3.py | 4 +++- - 3 files changed, 13 insertions(+), 5 deletions(-) - -diff --git a/src/twisted/cred/credentials.py b/src/twisted/cred/credentials.py -index 5469e51..67c24cb 100644 ---- a/src/twisted/cred/credentials.py -+++ b/src/twisted/cred/credentials.py -@@ -441,7 +441,8 @@ class CramMD5Credentials(object): - - - def checkPassword(self, password): -- verify = hexlify(hmac.HMAC(password, self.challenge).digest()) -+ verify = hexlify(hmac.HMAC(password, self.challenge, -+ digestmod=md5).digest()) - return verify == self.response - - -diff --git a/src/twisted/cred/test/test_cramauth.py b/src/twisted/cred/test/test_cramauth.py -index 1ee0871..d21f2f6 100644 ---- a/src/twisted/cred/test/test_cramauth.py -+++ b/src/twisted/cred/test/test_cramauth.py -@@ -7,6 +7,8 @@ Tests for L{twisted.cred}'s implementation of CRAM-MD5. - - from __future__ import division, absolute_import - -+import hashlib -+ - from hmac import HMAC - from binascii import hexlify - -@@ -39,7 +41,8 @@ class CramMD5CredentialsTests(TestCase): - """ - c = CramMD5Credentials() - chal = c.getChallenge() -- c.response = hexlify(HMAC(b'secret', chal).digest()) -+ c.response = hexlify(HMAC(b'secret', chal, -+ digestmod=hashlib.md5).digest()) - self.assertTrue(c.checkPassword(b'secret')) - - -@@ -61,7 +64,8 @@ class CramMD5CredentialsTests(TestCase): - """ - c = CramMD5Credentials() - chal = c.getChallenge() -- c.response = hexlify(HMAC(b'thewrongsecret', chal).digest()) -+ c.response = hexlify(HMAC(b'thewrongsecret', chal, -+ digestmod=hashlib.md5).digest()) - self.assertFalse(c.checkPassword(b'secret')) - - -@@ -75,7 +79,8 @@ class CramMD5CredentialsTests(TestCase): - chal = c.getChallenge() - c.setResponse(b" ".join( - (b"squirrel", -- hexlify(HMAC(b'supersecret', chal).digest())))) -+ hexlify(HMAC(b'supersecret', chal, -+ digestmod=hashlib.md5).digest())))) - self.assertTrue(c.checkPassword(b'supersecret')) - self.assertEqual(c.username, b"squirrel") - -diff --git a/src/twisted/mail/test/test_pop3.py b/src/twisted/mail/test/test_pop3.py -index 4a59c3b..ea51348 100644 ---- a/src/twisted/mail/test/test_pop3.py -+++ b/src/twisted/mail/test/test_pop3.py -@@ -11,6 +11,7 @@ import hmac - import base64 - import itertools - -+from hashlib import md5 - from collections import OrderedDict - from io import BytesIO - -@@ -1097,7 +1098,8 @@ class SASLTests(unittest.TestCase): - p.lineReceived(b"AUTH CRAM-MD5") - chal = s.getvalue().splitlines()[-1][2:] - chal = base64.decodestring(chal) -- response = hmac.HMAC(b'testpassword', chal).hexdigest().encode("ascii") -+ response = hmac.HMAC(b'testpassword', chal, -+ digestmod=md5).hexdigest().encode("ascii") - - p.lineReceived( - base64.encodestring(b'testuser ' + response).rstrip(b'\n')) diff -Nru twisted-20.3.0/debian/patches/0018-Make-the-twisted-tests-work-when-pyOpenSSL-deletes-N.patch twisted-22.1.0/debian/patches/0018-Make-the-twisted-tests-work-when-pyOpenSSL-deletes-N.patch --- twisted-20.3.0/debian/patches/0018-Make-the-twisted-tests-work-when-pyOpenSSL-deletes-N.patch 2021-04-24 16:36:24.000000000 +0000 +++ twisted-22.1.0/debian/patches/0018-Make-the-twisted-tests-work-when-pyOpenSSL-deletes-N.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,29 +0,0 @@ -From: Alex Gaynor -Date: Mon, 3 Aug 2020 21:00:25 -0400 -Subject: Make the twisted tests work when pyOpenSSL deletes NPN - -Author: Alex Gaynor -Origin: upstream, https://github.com/twisted/twisted/commit/895a9a0c7141646847a8c798f695c92e543af035 -Last-Updated: 2021-02-13 ---- - src/twisted/test/test_sslverify.py | 7 +++++-- - 1 file changed, 5 insertions(+), 2 deletions(-) - -diff --git a/src/twisted/test/test_sslverify.py b/src/twisted/test/test_sslverify.py -index 71ba1dc..d264f67 100644 ---- a/src/twisted/test/test_sslverify.py -+++ b/src/twisted/test/test_sslverify.py -@@ -44,8 +44,11 @@ if requireModule("OpenSSL"): - try: - ctx = SSL.Context(SSL.SSLv23_METHOD) - ctx.set_npn_advertise_callback(lambda c: None) -- except NotImplementedError: -- skipNPN = "OpenSSL 1.0.1 or greater required for NPN support" -+ except (NotImplementedError, AttributeError): -+ skipNPN = ( -+ "NPN is deprecated (and OpenSSL 1.0.1 or greater required for NPN" -+ " support)" -+ ) - - try: - ctx = SSL.Context(SSL.SSLv23_METHOD) diff -Nru twisted-20.3.0/debian/patches/0019-Replace-base64.-string-functions-to-fix-py3.9-suppor.patch twisted-22.1.0/debian/patches/0019-Replace-base64.-string-functions-to-fix-py3.9-suppor.patch --- twisted-20.3.0/debian/patches/0019-Replace-base64.-string-functions-to-fix-py3.9-suppor.patch 2021-04-24 16:36:24.000000000 +0000 +++ twisted-22.1.0/debian/patches/0019-Replace-base64.-string-functions-to-fix-py3.9-suppor.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,201 +0,0 @@ -From: Craig Rodrigues -Date: Sat, 20 Jun 2020 09:27:11 -0700 -Subject: Replace base64.*string() functions to fix py3.9 support - -Replace base64.decodestring() and .encodestring() functions as they -were deprecated since Python 3.1 in favor of (equivalent) .decodebytes() -and .encodebytes(), and were eventually removed in Python 3.9. - -While at it, replace most of their uses with base64.b64encode() -and .b64decode() that are preferable to the former wrt ticket #6446, -and they do not introduce line breaks that the twisted code usually -discarded. - -Use .decodebytes() and .encodebytes() in DirDBM as it seems to rely -on the exact presence of newlines, and changing that would break -backwards compatibility. - -Submitted by: Michal Gorny - -Author: Michal Gorny -Origin: upstream, https://github.com/twisted/twisted/commit/69e2838d441dde8cf20fa1aabb78de97e2d18343 -Last-Updated: 2021-02-13 ---- - src/twisted/conch/newsfragments/6446.misc | 0 - src/twisted/conch/newsfragments/9831.misc | 0 - src/twisted/conch/scripts/tkconch.py | 9 +++++---- - src/twisted/conch/test/test_keys.py | 2 +- - src/twisted/mail/newsfragments/6446.misc | 0 - src/twisted/mail/newsfragments/9831.misc | 0 - src/twisted/mail/pop3.py | 4 ++-- - src/twisted/mail/test/test_pop3.py | 4 ++-- - src/twisted/persisted/dirdbm.py | 4 ++-- - src/twisted/persisted/newsfragments/9831.misc | 0 - src/twisted/web/http.py | 2 +- - src/twisted/web/newsfragments/6446.misc | 0 - src/twisted/web/newsfragments/9831.misc | 0 - src/twisted/web/test/test_http.py | 6 +++--- - 14 files changed, 16 insertions(+), 15 deletions(-) - create mode 100644 src/twisted/conch/newsfragments/6446.misc - create mode 100644 src/twisted/conch/newsfragments/9831.misc - create mode 100644 src/twisted/mail/newsfragments/6446.misc - create mode 100644 src/twisted/mail/newsfragments/9831.misc - create mode 100644 src/twisted/persisted/newsfragments/9831.misc - create mode 100644 src/twisted/web/newsfragments/6446.misc - create mode 100644 src/twisted/web/newsfragments/9831.misc - -diff --git a/src/twisted/conch/newsfragments/6446.misc b/src/twisted/conch/newsfragments/6446.misc -new file mode 100644 -index 0000000..e69de29 -diff --git a/src/twisted/conch/newsfragments/9831.misc b/src/twisted/conch/newsfragments/9831.misc -new file mode 100644 -index 0000000..e69de29 -diff --git a/src/twisted/conch/scripts/tkconch.py b/src/twisted/conch/scripts/tkconch.py -index 9c48e8a..16de7c9 100644 ---- a/src/twisted/conch/scripts/tkconch.py -+++ b/src/twisted/conch/scripts/tkconch.py -@@ -407,11 +407,12 @@ class SSHClientTransport(transport.SSHClientTransport): - frame.write( - "Warning: Permanently added '%s' (%s) to the list of " - "known hosts.\r\n" % -- (khHost, {b'ssh-dss':'DSA', b'ssh-rsa':'RSA'}[keyType])) -- with open(os.path.expanduser('~/.ssh/known_hosts'), 'a') as known_hosts: -- encodedKey = base64.encodestring(pubKey).replace(b'\n', b'') -+ (khHost, {b'ssh-dss': 'DSA', b'ssh-rsa': 'RSA'}[keyType])) -+ with open(os.path.expanduser( -+ '~/.ssh/known_hosts'), 'a') as known_hosts: -+ encodedKey = base64.b64encode(pubKey) - known_hosts.write('\n%s %s %s' % (khHost, keyType, encodedKey)) -- except: -+ except BaseException: - log.deferr() - raise error.ConchError - -diff --git a/src/twisted/conch/test/test_keys.py b/src/twisted/conch/test/test_keys.py -index ab35f89..d770c9d 100644 ---- a/src/twisted/conch/test/test_keys.py -+++ b/src/twisted/conch/test/test_keys.py -@@ -352,7 +352,7 @@ SUrCyZXsNh6VXwjs3gKQ - - self.assertRaises( - keys.BadKeyError, -- keys.Key.fromString, data=b'{' + base64.encodestring(sexp) + b'}', -+ keys.Key.fromString, data=b'{' + base64.b64encode(sexp) + b'}', - ) - - -diff --git a/src/twisted/mail/newsfragments/6446.misc b/src/twisted/mail/newsfragments/6446.misc -new file mode 100644 -index 0000000..e69de29 -diff --git a/src/twisted/mail/newsfragments/9831.misc b/src/twisted/mail/newsfragments/9831.misc -new file mode 100644 -index 0000000..e69de29 -diff --git a/src/twisted/mail/pop3.py b/src/twisted/mail/pop3.py -index ffe9714..057389e 100644 ---- a/src/twisted/mail/pop3.py -+++ b/src/twisted/mail/pop3.py -@@ -728,7 +728,7 @@ class POP3(basic.LineOnlyReceiver, policies.TimeoutMixin): - self._auth = auth() - chal = self._auth.getChallenge() - -- self.sendLine(b'+ ' + base64.encodestring(chal).rstrip(b'\n')) -+ self.sendLine(b'+ ' + base64.b64encode(chal)) - self.state = 'AUTH' - - -@@ -747,7 +747,7 @@ class POP3(basic.LineOnlyReceiver, policies.TimeoutMixin): - """ - self.state = "COMMAND" - try: -- parts = base64.decodestring(line).split(None, 1) -+ parts = base64.b64decode(line).split(None, 1) - except binascii.Error: - self.failResponse(b"Invalid BASE64 encoding") - else: -diff --git a/src/twisted/mail/test/test_pop3.py b/src/twisted/mail/test/test_pop3.py -index ea51348..36780d9 100644 ---- a/src/twisted/mail/test/test_pop3.py -+++ b/src/twisted/mail/test/test_pop3.py -@@ -1097,12 +1097,12 @@ class SASLTests(unittest.TestCase): - - p.lineReceived(b"AUTH CRAM-MD5") - chal = s.getvalue().splitlines()[-1][2:] -- chal = base64.decodestring(chal) -+ chal = base64.b64decode(chal) - response = hmac.HMAC(b'testpassword', chal, - digestmod=md5).hexdigest().encode("ascii") - - p.lineReceived( -- base64.encodestring(b'testuser ' + response).rstrip(b'\n')) -+ base64.b64encode(b'testuser ' + response)) - self.assertTrue(p.mbox) - self.assertTrue(s.getvalue().splitlines()[-1].find(b"+OK") >= 0) - p.connectionLost(failure.Failure(Exception("Test harness disconnect"))) -diff --git a/src/twisted/persisted/dirdbm.py b/src/twisted/persisted/dirdbm.py -index f97c526..cc62f67 100644 ---- a/src/twisted/persisted/dirdbm.py -+++ b/src/twisted/persisted/dirdbm.py -@@ -81,14 +81,14 @@ class DirDBM: - Encode a key so it can be used as a filename. - """ - # NOTE: '_' is NOT in the base64 alphabet! -- return base64.encodestring(k).replace(b'\n', b'_').replace(b"/", b"-") -+ return base64.encodebytes(k).replace(b'\n', b'_').replace(b"/", b"-") - - - def _decode(self, k): - """ - Decode a filename to get the key. - """ -- return base64.decodestring(k.replace(b'_', b'\n').replace(b"-", b"/")) -+ return base64.decodebytes(k.replace(b'_', b'\n').replace(b"-", b"/")) - - - def _readFile(self, path): -diff --git a/src/twisted/persisted/newsfragments/9831.misc b/src/twisted/persisted/newsfragments/9831.misc -new file mode 100644 -index 0000000..e69de29 -diff --git a/src/twisted/web/http.py b/src/twisted/web/http.py -index 4713139..ac29b92 100644 ---- a/src/twisted/web/http.py -+++ b/src/twisted/web/http.py -@@ -1541,7 +1541,7 @@ class Request: - bas, upw = authh.split() - if bas.lower() != b"basic": - raise ValueError() -- upw = base64.decodestring(upw) -+ upw = base64.b64decode(upw) - self.user, self.password = upw.split(b':', 1) - except (binascii.Error, ValueError): - self.user = self.password = b'' -diff --git a/src/twisted/web/newsfragments/6446.misc b/src/twisted/web/newsfragments/6446.misc -new file mode 100644 -index 0000000..e69de29 -diff --git a/src/twisted/web/newsfragments/9831.misc b/src/twisted/web/newsfragments/9831.misc -new file mode 100644 -index 0000000..e69de29 -diff --git a/src/twisted/web/test/test_http.py b/src/twisted/web/test/test_http.py -index a3067f7..8538c43 100644 ---- a/src/twisted/web/test/test_http.py -+++ b/src/twisted/web/test/test_http.py -@@ -1607,7 +1607,7 @@ class ParsingTests(unittest.TestCase): - requests.append(self) - - for u, p in [(b"foo", b"bar"), (b"hello", b"there:z")]: -- s = base64.encodestring(b":".join((u, p))).strip() -+ s = base64.b64encode(b":".join((u, p))) - f = b"GET / HTTP/1.0\nAuthorization: Basic " + s + b"\n\n" - self.runRequest(f, Request, 0) - req = requests.pop() -@@ -2239,9 +2239,9 @@ Hello, - - u = b"foo" - p = b"bar" -- s = base64.encodestring(b":".join((u, p))).strip() -+ s = base64.b64encode(b":".join((u, p))) - f = b"GET / HTTP/1.0\nAuthorization: Basic " + s + b"\n\n" -- self.patch(base64, 'decodestring', lambda x: []) -+ self.patch(base64, 'b64decode', lambda x: []) - self.runRequest(f, Request, 0) - req = requests.pop() - self.assertEqual((b'', b''), req.credentials) diff -Nru twisted-20.3.0/debian/patches/0020-Fix-imap4-utf-7-codec-lookup-function-for-Python-3.9.patch twisted-22.1.0/debian/patches/0020-Fix-imap4-utf-7-codec-lookup-function-for-Python-3.9.patch --- twisted-20.3.0/debian/patches/0020-Fix-imap4-utf-7-codec-lookup-function-for-Python-3.9.patch 2021-04-24 16:36:24.000000000 +0000 +++ twisted-22.1.0/debian/patches/0020-Fix-imap4-utf-7-codec-lookup-function-for-Python-3.9.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ -From: Craig Rodrigues -Date: Sat, 20 Jun 2020 03:10:22 -0700 -Subject: Fix imap4-utf-7 codec lookup function for Python 3.9 - -Python 3.9 normalizes the codec name into 'imap4_utf_7' rather than -'imap4-utf-7', and therefore the lookup function needs to account -for the former name. Transform the latter locally to preserve support -for all Python versions. - -Submitted by: Michal Gorny - -Author: Michal Gorny -Origin: upstream, https://github.com/twisted/twisted/commit/b05a5b57cd07af2fe6899b7aefa5a42737919419 -Last-Updated: 2021-02-13 ---- - src/twisted/mail/imap4.py | 13 ++++++++++++- - src/twisted/mail/newsfragments/9832.misc | 0 - 2 files changed, 12 insertions(+), 1 deletion(-) - create mode 100644 src/twisted/mail/newsfragments/9832.misc - -diff --git a/src/twisted/mail/imap4.py b/src/twisted/mail/imap4.py -index 7949ef5..08ac7cb 100644 ---- a/src/twisted/mail/imap4.py -+++ b/src/twisted/mail/imap4.py -@@ -6364,13 +6364,24 @@ class StreamWriter(codecs.StreamWriter): - return encoder(s) - - -+ - _codecInfo = codecs.CodecInfo(encoder, decoder, StreamReader, StreamWriter) - - -+ - def imap4_utf_7(name): -- if name == 'imap4-utf-7': -+ # In Python 3.9, codecs.lookup() was changed to normalize the codec name -+ # in the same way as encodings.normalize_encoding(). The docstring -+ # for encodings.normalize_encoding() describes how the codec name is -+ # normalized. We need to replace '-' with '_' to be compatible with -+ # older Python versions. -+ # See: https://bugs.python.org/issue37751 -+ # https://github.com/python/cpython/pull/17997 -+ if name.replace('-', '_') == 'imap4_utf_7': - return _codecInfo - -+ -+ - codecs.register(imap4_utf_7) - - __all__ = [ -diff --git a/src/twisted/mail/newsfragments/9832.misc b/src/twisted/mail/newsfragments/9832.misc -new file mode 100644 -index 0000000..e69de29 diff -Nru twisted-20.3.0/debian/patches/0021-Merge-9652-wiml-mktime-Allow-mktime-to-raise-EOVERFL.patch twisted-22.1.0/debian/patches/0021-Merge-9652-wiml-mktime-Allow-mktime-to-raise-EOVERFL.patch --- twisted-20.3.0/debian/patches/0021-Merge-9652-wiml-mktime-Allow-mktime-to-raise-EOVERFL.patch 2021-04-24 16:36:24.000000000 +0000 +++ twisted-22.1.0/debian/patches/0021-Merge-9652-wiml-mktime-Allow-mktime-to-raise-EOVERFL.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,84 +0,0 @@ -From: Wim Lewis -Date: Sun, 31 May 2020 19:44:29 -0700 -Subject: Merge 9652-wiml-mktime: Allow mktime() to raise EOVERFLOW if isdst=1 - and there's no DST. - -Author: wiml -Reviewer: hawkowl -Fixes: ticket:9652 - -The mktime() call in versions of glibc starting with 2.28 will return -EOVERFLOW if asked to interpret a tuple whose isdst field is 1 in -timezones that don't observe Daylight Savings Time. We now accept that -mktime() *either* ignores isdst in those timezones *or* raises -EOVERFLOW, both of which appear to be legal behavior. - -Author: Wim Lewis -Origin: upstream, https://github.com/twisted/twisted/commit/9537671a8b507e46ca02af08f4e69e5462b69664 -Last-Updated: 2021-02-13 ---- - src/twisted/newsfragments/9652.misc | 1 + - src/twisted/test/test_log.py | 29 +++++++++++++++++++++-------- - 2 files changed, 22 insertions(+), 8 deletions(-) - create mode 100644 src/twisted/newsfragments/9652.misc - -diff --git a/src/twisted/newsfragments/9652.misc b/src/twisted/newsfragments/9652.misc -new file mode 100644 -index 0000000..8b13789 ---- /dev/null -+++ b/src/twisted/newsfragments/9652.misc -@@ -0,0 +1 @@ -+ -diff --git a/src/twisted/test/test_log.py b/src/twisted/test/test_log.py -index 7852f65..281a3af 100644 ---- a/src/twisted/test/test_log.py -+++ b/src/twisted/test/test_log.py -@@ -454,17 +454,29 @@ class FileObserverTests(LogPublisherTestCaseMixin, - # The behavior of mktime depends on the current timezone setting. - # So only do this after changing the timezone. - -- # Compute a POSIX timestamp for a certain date and time that is -- # known to occur at a time when daylight saving time is in effect. -- localDaylightTuple = (2006, 6, 30, 0, 0, 0, 4, 181, 1) -- daylight = time.mktime(localDaylightTuple) -- - # Compute a POSIX timestamp for a certain date and time that is - # known to occur at a time when daylight saving time is not in - # effect. - localStandardTuple = (2007, 1, 31, 0, 0, 0, 2, 31, 0) - standard = time.mktime(localStandardTuple) - -+ # Compute a POSIX timestamp for a certain date and time that is -+ # known to occur at a time when daylight saving time is in effect. -+ localDaylightTuple = (2006, 6, 30, 0, 0, 0, 4, 181, 1) -+ try: -+ daylight = time.mktime(localDaylightTuple) -+ except OverflowError: -+ # mktime() may raise OverflowError if its tuple is -+ # inconsistent, although many implementations don't -+ # care. The implementation in glibc>=2.28 will raise -+ # if DST is indicated for a zone that doesn't have DST. -+ # We accept either behavior: ignoring the DST flag for those -+ # zones, or raising EOVERFLOW. -+ if daylightOffset == standardOffset: # DST-less zone? -+ daylight = standard -+ else: -+ raise -+ - self.assertEqual( - (self.flo.getTimezoneOffset(daylight), - self.flo.getTimezoneOffset(standard)), -@@ -505,9 +517,10 @@ class FileObserverTests(LogPublisherTestCaseMixin, - daylight saving time at all (so both summer and winter time test values - should have the same offset). - """ -- # Test a timezone that doesn't have DST. mktime() implementations -- # available for testing seem happy to produce results for this even -- # though it's not entirely valid. -+ # Test a timezone that doesn't have DST. Some mktime() -+ # implementations available for testing seem happy to produce -+ # results for this even though it's not entirely valid. Others -+ # such as glibc>=2.28 return EOVERFLOW. - self._getTimezoneOffsetTest("Africa/Johannesburg", -7200, -7200) - - diff -Nru twisted-20.3.0/debian/patches/0022-increase-size-of-FFDH-keys-for-conch-testing.patch twisted-22.1.0/debian/patches/0022-increase-size-of-FFDH-keys-for-conch-testing.patch --- twisted-20.3.0/debian/patches/0022-increase-size-of-FFDH-keys-for-conch-testing.patch 2021-04-24 16:36:24.000000000 +0000 +++ twisted-22.1.0/debian/patches/0022-increase-size-of-FFDH-keys-for-conch-testing.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,79 +0,0 @@ -From: Paul Kehrer -Date: Sat, 28 Nov 2020 22:56:28 -0600 -Subject: increase size of FFDH keys for conch testing - -Author: Paul Kehrer -Origin: upstream, https://github.com/twisted/twisted/commit/d3a97b7f9e536af0103940afe670817fd2ff1393 -Last-Updated: 2021-02-13 ---- - src/twisted/conch/test/test_transport.py | 36 +++++++++++++++++++++++--------- - src/twisted/newsfragments/10061.misc | 0 - 2 files changed, 26 insertions(+), 10 deletions(-) - create mode 100644 src/twisted/newsfragments/10061.misc - -diff --git a/src/twisted/conch/test/test_transport.py b/src/twisted/conch/test/test_transport.py -index f5025c1..129c3ff 100644 ---- a/src/twisted/conch/test/test_transport.py -+++ b/src/twisted/conch/test/test_transport.py -@@ -2267,6 +2267,21 @@ class ClientSSHTransportDHGroupExchangeBaseCase(ClientSSHTransportBaseCase): - Diffie-Hellman group exchange tests for SSHClientTransport. - """ - -+ """ -+ 1536-bit modulus from RFC 3526 -+ """ -+ P1536 = int( -+ "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" -+ "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" -+ "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" -+ "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" -+ "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" -+ "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" -+ "83655D23DCA3AD961C62F356208552BB9ED529077096966D" -+ "670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF", -+ 16, -+ ) -+ - def test_KEXINIT_groupexchange(self): - """ - KEXINIT packet with a group-exchange key exchange results -@@ -2286,17 +2301,18 @@ class ClientSSHTransportDHGroupExchangeBaseCase(ClientSSHTransportBaseCase): - KEX_DH_GEX_INIT message with the client's Diffie-Hellman public key. - """ - self.test_KEXINIT_groupexchange() -- self.proto.ssh_KEX_DH_GEX_GROUP( -- b'\x00\x00\x00\x03\x00\xfe\xf3\x00\x00\x00\x01\x02') -- self.assertEqual(self.proto.p, 65267) -+ self.proto.ssh_KEX_DH_GEX_GROUP(common.MP(self.P1536) + common.MP(2)) -+ self.assertEqual(self.proto.p, self.P1536) - self.assertEqual(self.proto.g, 2) - x = self.proto.dhSecretKey.private_numbers().x -- self.assertEqual(common.MP(x)[5:], b'\x99' * 2) -- self.assertEqual(self.proto.dhSecretKeyPublicMP, -- common.MP(pow(2, x, 65267))) -- self.assertEqual(self.packets[1:], [(transport.MSG_KEX_DH_GEX_INIT, -- self.proto.dhSecretKeyPublicMP)]) -- -+ self.assertEqual(common.MP(x)[5:], b"\x99" * 192) -+ self.assertEqual( -+ self.proto.dhSecretKeyPublicMP, common.MP(pow(2, x, self.P1536)) -+ ) -+ self.assertEqual( -+ self.packets[1:], -+ [(transport.MSG_KEX_DH_GEX_INIT, self.proto.dhSecretKeyPublicMP)], -+ ) - - def begin_KEX_DH_GEX_REPLY(self): - """ -@@ -2322,7 +2338,7 @@ class ClientSSHTransportDHGroupExchangeBaseCase(ClientSSHTransportBaseCase): - # Here is the wire format for advertised min, pref and max DH sizes. - h.update(b'\x00\x00\x04\x00\x00\x00\x08\x00\x00\x00\x20\x00') - # And the selected group parameters. -- h.update(b'\x00\x00\x00\x03\x00\xfe\xf3\x00\x00\x00\x01\x02') -+ h.update(common.MP(self.P1536) + common.MP(2)) - h.update(self.proto.dhSecretKeyPublicMP) - h.update(fMP) - h.update(sharedSecret) -diff --git a/src/twisted/newsfragments/10061.misc b/src/twisted/newsfragments/10061.misc -new file mode 100644 -index 0000000..e69de29 diff -Nru twisted-20.3.0/debian/patches/0023-Merge-9801-rodrigc-cgi-Change-import-of-cgi.parse_qs.patch twisted-22.1.0/debian/patches/0023-Merge-9801-rodrigc-cgi-Change-import-of-cgi.parse_qs.patch --- twisted-20.3.0/debian/patches/0023-Merge-9801-rodrigc-cgi-Change-import-of-cgi.parse_qs.patch 2021-04-24 16:36:24.000000000 +0000 +++ twisted-22.1.0/debian/patches/0023-Merge-9801-rodrigc-cgi-Change-import-of-cgi.parse_qs.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,260 +0,0 @@ -From: Craig Rodrigues -Date: Mon, 13 Apr 2020 01:22:23 -0700 -Subject: Merge 9801-rodrigc-cgi: Change import of cgi.parse_qs to - urllib.parse.parse_qs - -Author: rodrigc -Reviewer: hawkowl -Fixes: ticket:9801 - -Author: Craig Rodrigues -Origin: upstream, https://github.com/twisted/twisted/commit/adc0a9a494e52211778b158f6748e6b55809c55a -Last-Updated: 2021-02-13 ---- - src/twisted/web/client.py | 17 ++++++----- - src/twisted/web/http.py | 50 ++++++++++++++++----------------- - src/twisted/web/newsfragments/9801.misc | 0 - src/twisted/web/test/test_http.py | 41 ++++----------------------- - src/twisted/web/test/test_webclient.py | 5 +--- - 5 files changed, 39 insertions(+), 74 deletions(-) - create mode 100644 src/twisted/web/newsfragments/9801.misc - -diff --git a/src/twisted/web/client.py b/src/twisted/web/client.py -index 7e4642e..8209f5a 100644 ---- a/src/twisted/web/client.py -+++ b/src/twisted/web/client.py -@@ -12,15 +12,8 @@ import os - import collections - import warnings - --try: -- from urlparse import urlunparse, urljoin, urldefrag --except ImportError: -- from urllib.parse import urljoin, urldefrag -- from urllib.parse import urlunparse as _urlunparse -- -- def urlunparse(parts): -- result = _urlunparse(tuple([p.decode("charmap") for p in parts])) -- return result.encode("charmap") -+from urllib.parse import urljoin, urldefrag -+from urllib.parse import urlunparse as _urlunparse - - import zlib - from functools import wraps -@@ -51,6 +44,12 @@ from twisted.web._newclient import _ensureValidURI, _ensureValidMethod - - - -+def urlunparse(parts): -+ result = _urlunparse(tuple([p.decode("charmap") for p in parts])) -+ return result.encode("charmap") -+ -+ -+ - class PartialDownloadError(error.Error): - """ - Page was only partially downloaded, we got disconnected in middle. -diff --git a/src/twisted/web/http.py b/src/twisted/web/http.py -index ac29b92..78f4a36 100644 ---- a/src/twisted/web/http.py -+++ b/src/twisted/web/http.py -@@ -66,27 +66,11 @@ import time - import calendar - import warnings - import os --from io import BytesIO as StringIO -- --try: -- from urlparse import ( -- ParseResult as ParseResultBytes, urlparse as _urlparse) -- from urllib import unquote -- from cgi import parse_header as _parseHeader --except ImportError: -- from urllib.parse import ( -- ParseResultBytes, urlparse as _urlparse, unquote_to_bytes as unquote) -- -- def _parseHeader(line): -- # cgi.parse_header requires a str -- key, pdict = cgi.parse_header(line.decode('charmap')) -- -- # We want the key as bytes, and cgi.parse_multipart (which consumes -- # pdict) expects a dict of str keys but bytes values -- key = key.encode('charmap') -- pdict = {x:y.encode('charmap') for x, y in pdict.items()} -- return (key, pdict) -+import re -+from io import BytesIO - -+from urllib.parse import ( -+ ParseResultBytes, urlparse as _urlparse, unquote_to_bytes as unquote) - - from zope.interface import Attribute, Interface, implementer, provider - -@@ -163,6 +147,20 @@ monthname = [None, - weekdayname_lower = [name.lower() for name in weekdayname] - monthname_lower = [name and name.lower() for name in monthname] - -+ -+ -+def _parseHeader(line): -+ # cgi.parse_header requires a str -+ key, pdict = cgi.parse_header(line.decode('charmap')) -+ -+ # We want the key as bytes, and cgi.parse_multipart (which consumes -+ # pdict) expects a dict of str keys but bytes values -+ key = key.encode('charmap') -+ pdict = {x: y.encode('charmap') for x, y in pdict.items()} -+ return (key, pdict) -+ -+ -+ - def urlparse(url): - """ - Parse an URL into six components. -@@ -486,13 +484,15 @@ class _IDeprecatedHTTPChannelToRequestInterface(Interface): - - class StringTransport: - """ -- I am a StringIO wrapper that conforms for the transport API. I support -+ I am a BytesIO wrapper that conforms for the transport API. I support - the `writeSequence' method. - """ - def __init__(self): -- self.s = StringIO() -+ self.s = BytesIO() -+ - def writeSequence(self, seq): - self.s.write(b''.join(seq)) -+ - def __getattr__(self, attr): - return getattr(self.__dict__['s'], attr) - -@@ -513,7 +513,7 @@ class HTTPClient(basic.LineReceiver): - @type firstLine: C{bool} - - @ivar __buffer: The buffer that stores the response to the HTTP request. -- @type __buffer: A C{StringIO} object. -+ @type __buffer: A C{BytesIO} object. - - @ivar _header: Part or all of an HTTP request header. - @type _header: C{bytes} -@@ -579,7 +579,7 @@ class HTTPClient(basic.LineReceiver): - if self._header != b"": - # Only extract headers if there are any - self.extractHeader(self._header) -- self.__buffer = StringIO() -+ self.__buffer = BytesIO() - self.handleEndHeaders() - self.setRawMode() - return -@@ -665,7 +665,7 @@ def _getContentFile(length): - Get a writeable file-like object to which request content can be written. - """ - if length is not None and length < 100000: -- return StringIO() -+ return BytesIO() - return tempfile.TemporaryFile() - - -diff --git a/src/twisted/web/newsfragments/9801.misc b/src/twisted/web/newsfragments/9801.misc -new file mode 100644 -index 0000000..e69de29 -diff --git a/src/twisted/web/test/test_http.py b/src/twisted/web/test/test_http.py -index 8538c43..e32926a 100644 ---- a/src/twisted/web/test/test_http.py -+++ b/src/twisted/web/test/test_http.py -@@ -9,15 +9,11 @@ from __future__ import absolute_import, division - - import base64 - import calendar --import cgi - import random - - import hamcrest - --try: -- from urlparse import urlparse, urlunsplit, clear_cache --except ImportError: -- from urllib.parse import urlparse, urlunsplit, clear_cache -+from urllib.parse import urlparse, urlunsplit, clear_cache, parse_qs - - from io import BytesIO - from itertools import cycle -@@ -28,7 +24,7 @@ from zope.interface import ( - ) - from zope.interface.verify import verifyObject - --from twisted.python.compat import (_PY3, iterbytes, long, networkString, -+from twisted.python.compat import (iterbytes, long, networkString, - unicode, intToBytes) - from twisted.python.components import proxyForInterface - from twisted.python.failure import Failure -@@ -2019,33 +2015,6 @@ Content-Type: application/x-www-form-urlencoded - self.assertEqual(content, [networkString(query)]) - - -- def test_missingContentDisposition(self): -- """ -- If the C{Content-Disposition} header is missing, the request is denied -- as a bad request. -- """ -- req = b'''\ --POST / HTTP/1.0 --Content-Type: multipart/form-data; boundary=AaB03x --Content-Length: 103 -- ----AaB03x --Content-Type: text/plain --Content-Transfer-Encoding: quoted-printable -- --abasdfg ----AaB03x-- --''' -- channel = self.runRequest(req, http.Request, success=False) -- self.assertEqual( -- channel.transport.value(), -- b"HTTP/1.1 400 Bad Request\r\n\r\n") -- -- if _PY3: -- test_missingContentDisposition.skip = ( -- "cgi.parse_multipart is much more error-tolerant on Python 3.") -- -- - def test_multipartProcessingFailure(self): - """ - When the multipart processing fails the client gets a 400 Bad Request. -@@ -2373,15 +2342,15 @@ ok - class QueryArgumentsTests(unittest.TestCase): - def testParseqs(self): - self.assertEqual( -- cgi.parse_qs(b"a=b&d=c;+=f"), -+ parse_qs(b"a=b&d=c;+=f"), - http.parse_qs(b"a=b&d=c;+=f")) - self.assertRaises( - ValueError, http.parse_qs, b"blah", strict_parsing=True) - self.assertEqual( -- cgi.parse_qs(b"a=&b=c", keep_blank_values=1), -+ parse_qs(b"a=&b=c", keep_blank_values=1), - http.parse_qs(b"a=&b=c", keep_blank_values=1)) - self.assertEqual( -- cgi.parse_qs(b"a=&b=c"), -+ parse_qs(b"a=&b=c"), - http.parse_qs(b"a=&b=c")) - - -diff --git a/src/twisted/web/test/test_webclient.py b/src/twisted/web/test/test_webclient.py -index 680e027..6725949 100644 ---- a/src/twisted/web/test/test_webclient.py -+++ b/src/twisted/web/test/test_webclient.py -@@ -11,10 +11,7 @@ import io - import os - from errno import ENOSPC - --try: -- from urlparse import urlparse, urljoin --except ImportError: -- from urllib.parse import urlparse, urljoin -+from urllib.parse import urlparse, urljoin - - from twisted.python.compat import networkString, nativeString, intToBytes - from twisted.trial import unittest, util diff -Nru twisted-20.3.0/debian/patches/0024-fixed-corrupted-iqmp-value-in-test-RSA-key.patch twisted-22.1.0/debian/patches/0024-fixed-corrupted-iqmp-value-in-test-RSA-key.patch --- twisted-20.3.0/debian/patches/0024-fixed-corrupted-iqmp-value-in-test-RSA-key.patch 2021-04-24 16:36:24.000000000 +0000 +++ twisted-22.1.0/debian/patches/0024-fixed-corrupted-iqmp-value-in-test-RSA-key.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,73 +0,0 @@ -From: Alex Gaynor -Date: Sat, 18 Jul 2020 15:30:41 -0400 -Subject: fixed corrupted iqmp value in test RSA key - -Author: Alex Gaynor -Origin: upstream, https://github.com/twisted/twisted/commit/9318b60fca5752f351cadd978ef6aa2e743d38ab -Last-Updated: 2021-02-13 ---- - src/twisted/conch/newsfragments/9913.misc | 0 - src/twisted/conch/test/keydata.py | 46 +++++++++++++++++-------------- - 2 files changed, 25 insertions(+), 21 deletions(-) - create mode 100644 src/twisted/conch/newsfragments/9913.misc - -diff --git a/src/twisted/conch/newsfragments/9913.misc b/src/twisted/conch/newsfragments/9913.misc -new file mode 100644 -index 0000000..e69de29 -diff --git a/src/twisted/conch/test/keydata.py b/src/twisted/conch/test/keydata.py -index 7c59922..1772fe0 100644 ---- a/src/twisted/conch/test/keydata.py -+++ b/src/twisted/conch/test/keydata.py -@@ -228,27 +228,31 @@ rXp0IjScTxfLP+Cq5V6lJ94/pX8Ppoj1FdZfNxeS4NYFSRA7kvY= - # following it. It is not any standard key format and was probably a bug in - # OpenSSH at some point. - privateRSA_openssh_alternate = b"""-----BEGIN RSA PRIVATE KEY----- --MIIEqTCCBKMCAQACggEBANVqrHgj1tYb7CWhUMR3Y1CERQFVQhQqKuDQYO7U6aOtSvo5Bl6EVXVf --ADa/b6oqP4MmN8FpLlv98PPSfdaYzTpAeNXKqBjAEZMkCQyBTI/3nO0TFmqkBOlJd8PkVWSzeWie --LAjrrOgELSF3BaeO71MwDaXluz1q4gk2b/00031vRv+H2qkpJ6r/rfWF5j4auHodSrHqwFr3MN8f --wqTk7z+RSZZA1Rl3LTfDXuydpjpEpcKkKd3Vupw9RbPGLBhk1bo936t/zUKsp/EYC6BYFWILpCpu --Q8PkBJ81o0eORu0zpWW9vDspbgILV9906BO0NzV+g18gJmCm3K2Lxmx5mPcCAwEAAQKCAQAhTAhm --oijVtPuOD3IbhQkAufJON/AcV0vjUX+eI6fkOphVG+qLepgevNi6sfmJEhhgrOjMC04JWkBqui+Z --+LMkYIS5zmmVmvni/B9RTScV2ysnre+0aay+fRDrhkdwc7QAh5UVOzf55xTngLtoHhvm3btzY7ln --5rInf8/PMJvCmP3ZGDYvNi7xPYF6n+EDLUfbNFFiOd1P6ayoi9nW84TEF7lxnQYIQnhNu8Uq9MNY --zVUr7b4zXwTqe+YEJGPyLdc9G2zVnGNDL5KIjT5u2hg32A8lZ4kduUY0XsnOxIvtklozBw/fhgj5 --kunb6zgINsnNzQoBSFs5PnrKxoCp3NQ5AoGBANlwBtjivNR4kVCU1MEbiThsRmRaUaCaBz1IjwNR --zGsSjn0asWXncXU54DIFdY0YTK+TsUmxZl94YnrRDMrmTUOznPRrfeYMmNzPIWKO1S4S3gSu1yRu --gzGiFaJEPSKpYiYiubLtVAqdCIOnBw3/GRiO2Ksd2kicMWgRoWZt49gdAoGBAPtEF4ukNr4eNx2n --9mFsBMSq3Xg+B4weMwKuAxSHg3rlnn0IZ6jyqr8ScM9yqafHCx2I1SD9nGPKRzBVTovEz/R/FqSS --EnShCcLEbpyMM++l5ffgK61PXBGqGoQ3W/166sPNfLDI5B9UY7XHr9/0Caf8xyX8XOmR15LFmB5W --07EjAoGAUa/pkp+UC0qEZT6UszuSELV0uIzJ78kOATL6L2gSoQMmrs9RaBRMJpsopAIzCF/hp3CY --ATR5XlKOxM82vB9LVazrwVOEx+FhqErUov9ADYAfEqlQwCoYdZQMBpsWUKhL7EHNe+/3S8l1AmjE --mLiGiBhaQ+cCM5ciZJODDEUqfO0CgYBpZjfGQN0hxQTzsLg+R5R8dvwt6z85PJXDQwFRxEKX8+gW --pMbu7NRJEFA4BO47zdfQzMwyaZAHoBtan/4xzR46fnEeGZQaTk8M319S1dEXbuzXnLZVnduOIV+8 --JIi2/K+r8O+kLLDcn4awAxK4i+LdD8DuIz1KUP4vuClGWL+2JwKBgQCFSxt6mxIQN54frV7a/saW --/t81a7k04haXkiYJvb1wIAOnNb0tG6DSB0cr1N6oqAcHG7gEIKcnQTxsOTnpQc7nFx3RTFy8PdIm --Jv5q1v1Icq5G+nvD0xlgRB2lE6eA9WMp1HpdBgcWXfaLPctkOuKEWk2MBi0tnRzrg0x4PXlUzjAA -+MIIEqDCCBKICAQACggEBANVqrHgj1tYb7CWhUMR3Y1CERQFVQhQqKuDQYO7U6aOt -+Svo5Bl6EVXVfADa/b6oqP4MmN8FpLlv98PPSfdaYzTpAeNXKqBjAEZMkCQyBTI/3 -+nO0TFmqkBOlJd8PkVWSzeWieLAjrrOgELSF3BaeO71MwDaXluz1q4gk2b/00031v -+Rv+H2qkpJ6r/rfWF5j4auHodSrHqwFr3MN8fwqTk7z+RSZZA1Rl3LTfDXuydpjpE -+pcKkKd3Vupw9RbPGLBhk1bo936t/zUKsp/EYC6BYFWILpCpuQ8PkBJ81o0eORu0z -+pWW9vDspbgILV9906BO0NzV+g18gJmCm3K2Lxmx5mPcCAwEAAQKCAQAhTAhmoijV -+tPuOD3IbhQkAufJON/AcV0vjUX+eI6fkOphVG+qLepgevNi6sfmJEhhgrOjMC04J -+WkBqui+Z+LMkYIS5zmmVmvni/B9RTScV2ysnre+0aay+fRDrhkdwc7QAh5UVOzf5 -+5xTngLtoHhvm3btzY7ln5rInf8/PMJvCmP3ZGDYvNi7xPYF6n+EDLUfbNFFiOd1P -+6ayoi9nW84TEF7lxnQYIQnhNu8Uq9MNYzVUr7b4zXwTqe+YEJGPyLdc9G2zVnGND -+L5KIjT5u2hg32A8lZ4kduUY0XsnOxIvtklozBw/fhgj5kunb6zgINsnNzQoBSFs5 -+PnrKxoCp3NQ5AoGBANlwBtjivNR4kVCU1MEbiThsRmRaUaCaBz1IjwNRzGsSjn0a -+sWXncXU54DIFdY0YTK+TsUmxZl94YnrRDMrmTUOznPRrfeYMmNzPIWKO1S4S3gSu -+1yRugzGiFaJEPSKpYiYiubLtVAqdCIOnBw3/GRiO2Ksd2kicMWgRoWZt49gdAoGB -+APtEF4ukNr4eNx2n9mFsBMSq3Xg+B4weMwKuAxSHg3rlnn0IZ6jyqr8ScM9yqafH -+Cx2I1SD9nGPKRzBVTovEz/R/FqSSEnShCcLEbpyMM++l5ffgK61PXBGqGoQ3W/16 -+6sPNfLDI5B9UY7XHr9/0Caf8xyX8XOmR15LFmB5W07EjAoGAUa/pkp+UC0qEZT6U -+szuSELV0uIzJ78kOATL6L2gSoQMmrs9RaBRMJpsopAIzCF/hp3CYATR5XlKOxM82 -+vB9LVazrwVOEx+FhqErUov9ADYAfEqlQwCoYdZQMBpsWUKhL7EHNe+/3S8l1AmjE -+mLiGiBhaQ+cCM5ciZJODDEUqfO0CgYBpZjfGQN0hxQTzsLg+R5R8dvwt6z85PJXD -+QwFRxEKX8+gWpMbu7NRJEFA4BO47zdfQzMwyaZAHoBtan/4xzR46fnEeGZQaTk8M -+319S1dEXbuzXnLZVnduOIV+8JIi2/K+r8O+kLLDcn4awAxK4i+LdD8DuIz1KUP4v -+uClGWL+2JwKBgGYW+SA00FQlvGExrIL775w1Hn5KVQJolQ0Kk74ev+FA+pCnVHAx -+6Xj84Ga3Inea693V0jBGyuLXXkGbz7VINVGqJdze2zQpSCHb1nT8fuUvU/ecCXC5 -+5KB2pq16dCI0nE8Xyz/gquVepSfeP6V/D6aI9RXWXzcXkuDWBUkQO5L2MAA= - -----END RSA PRIVATE KEY-----""" - - # New format introduced in OpenSSH 6.5 diff -Nru twisted-20.3.0/debian/patches/0025-Skip-failing-twisted.web.test.test_http.QueryArgumen.patch twisted-22.1.0/debian/patches/0025-Skip-failing-twisted.web.test.test_http.QueryArgumen.patch --- twisted-20.3.0/debian/patches/0025-Skip-failing-twisted.web.test.test_http.QueryArgumen.patch 2021-04-24 16:36:24.000000000 +0000 +++ twisted-22.1.0/debian/patches/0025-Skip-failing-twisted.web.test.test_http.QueryArgumen.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,35 +0,0 @@ -From: Thomas Grainger -Date: Tue, 23 Feb 2021 21:34:51 +0000 -Subject: skip failing - twisted.web.test.test_http.QueryArgumentsTests.testParseqs - -https://github.com/twisted/twisted/pull/1523 ---- - src/twisted/web/test/test_http.py | 3 +++ - 1 file changed, 3 insertions(+) - -diff --git a/src/twisted/web/test/test_http.py b/src/twisted/web/test/test_http.py -index e32926a..ab1cac2 100644 ---- a/src/twisted/web/test/test_http.py -+++ b/src/twisted/web/test/test_http.py -@@ -10,10 +10,12 @@ from __future__ import absolute_import, division - import base64 - import calendar - import random -+import sys - - import hamcrest - - from urllib.parse import urlparse, urlunsplit, clear_cache, parse_qs -+from unittest import skipIf - - from io import BytesIO - from itertools import cycle -@@ -2340,6 +2342,7 @@ ok - - - class QueryArgumentsTests(unittest.TestCase): -+ @skipIf(sys.version_info >= (3, 6), "newer py3.6 parse_qs treat ; differently") - def testParseqs(self): - self.assertEqual( - parse_qs(b"a=b&d=c;+=f"), diff -Nru twisted-20.3.0/debian/patches/debian-hacks/Security-Fix-vulnerable-example-of-PYTHONPATH.patch twisted-22.1.0/debian/patches/debian-hacks/Security-Fix-vulnerable-example-of-PYTHONPATH.patch --- twisted-20.3.0/debian/patches/debian-hacks/Security-Fix-vulnerable-example-of-PYTHONPATH.patch 1970-01-01 00:00:00.000000000 +0000 +++ twisted-22.1.0/debian/patches/debian-hacks/Security-Fix-vulnerable-example-of-PYTHONPATH.patch 2022-02-17 10:40:49.000000000 +0000 @@ -0,0 +1,27 @@ +From: Free Ekanayaka +Date: Fri, 21 Oct 2016 09:21:44 +0000 +Subject: Security: Fix vulnerable example of PYTHONPATH + +Modify the PYTHONPATH variable within the HOWTO section so it's less +insecure while usage. + +Forwarded: no +Bug-Debian: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=605190 +--- + docs/core/howto/quotes.rst | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/docs/core/howto/quotes.rst b/docs/core/howto/quotes.rst +index 163d67b..d171a07 100644 +--- a/docs/core/howto/quotes.rst ++++ b/docs/core/howto/quotes.rst +@@ -65,7 +65,8 @@ following: + #. Add the ``TwistedQuotes`` directory's *parent* to your Python + path. For example, if the TwistedQuotes directory's path is + ``/mystuff/TwistedQuotes`` or ``c:\mystuff\TwistedQuotes`` +- add ``/mystuff`` to your Python path. On UNIX this would be ``export PYTHONPATH=/mystuff:$PYTHONPATH`` , on Microsoft ++ add ``/mystuff`` to your Python path. On UNIX this would be ``export PYTHONPATH=/mystuff${PYTHONPATH:+:$PYTHONPATH} ++`` , on Microsoft + Windows change the ``PYTHONPATH`` variable through the + Systems Properties dialog by adding ``;c:\mystuff`` at the + end. diff -Nru twisted-20.3.0/debian/patches/debian-hacks/Sphinx-Adjust-setup-of-sys.path.insert.patch twisted-22.1.0/debian/patches/debian-hacks/Sphinx-Adjust-setup-of-sys.path.insert.patch --- twisted-20.3.0/debian/patches/debian-hacks/Sphinx-Adjust-setup-of-sys.path.insert.patch 1970-01-01 00:00:00.000000000 +0000 +++ twisted-22.1.0/debian/patches/debian-hacks/Sphinx-Adjust-setup-of-sys.path.insert.patch 2022-02-17 10:40:49.000000000 +0000 @@ -0,0 +1,25 @@ +From: Free Ekanayaka +Date: Fri, 4 Nov 2016 07:26:00 +0000 +Subject: Sphinx: Adjust setup of sys.path.insert + +Adjust the import path in the Sphinx configuration file to match the new +source files location (src/). + +Forwarded: no-needed +--- + docs/conf.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/docs/conf.py b/docs/conf.py +index 064e73f..c86ec5b 100644 +--- a/docs/conf.py ++++ b/docs/conf.py +@@ -22,7 +22,7 @@ from pprint import pprint + # add these directories to sys.path here. If the directory is relative to the + # documentation root, use os.path.abspath to make it absolute, like shown here. + sys.path.insert(0, os.path.abspath("./_extensions")) +-sys.path.insert(0, os.path.abspath("..")) ++sys.path.insert(0, os.path.abspath("../src")) + + # -- General configuration ------------------------------------------------ + diff -Nru twisted-20.3.0/debian/patches/debian-hacks/Sphinx-Set-html_theme-to-twisteddefault.patch twisted-22.1.0/debian/patches/debian-hacks/Sphinx-Set-html_theme-to-twisteddefault.patch --- twisted-20.3.0/debian/patches/debian-hacks/Sphinx-Set-html_theme-to-twisteddefault.patch 1970-01-01 00:00:00.000000000 +0000 +++ twisted-22.1.0/debian/patches/debian-hacks/Sphinx-Set-html_theme-to-twisteddefault.patch 2022-02-17 10:40:49.000000000 +0000 @@ -0,0 +1,27 @@ +From: Matthias Klose +Date: Thu, 20 Oct 2016 04:34:07 +0000 +Subject: Sphinx: Set html_theme to twisteddefault + +Set the sphinx theme explicetly to twisteddefault. +--- + docs/conf.py | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/docs/conf.py b/docs/conf.py +index e183221..3122b10 100644 +--- a/docs/conf.py ++++ b/docs/conf.py +@@ -106,6 +106,13 @@ on_rtd = os.environ.get("READTHEDOCS", None) == "True" + if not on_rtd: + html_theme = "twistedtrac" + ++html_theme = 'twisteddefault' ++ ++# Theme options are theme-specific and customize the look and feel of a theme ++# further. For a list of options available for each theme, see the ++# documentation. ++#html_theme_options = {} ++ + # Add any paths that contain custom themes here, relative to this directory. + html_theme_path = ["_themes"] + diff -Nru twisted-20.3.0/debian/patches/debian-hacks/Sphinx-Set-intersphinx_mapping-for-py3.patch twisted-22.1.0/debian/patches/debian-hacks/Sphinx-Set-intersphinx_mapping-for-py3.patch --- twisted-20.3.0/debian/patches/debian-hacks/Sphinx-Set-intersphinx_mapping-for-py3.patch 1970-01-01 00:00:00.000000000 +0000 +++ twisted-22.1.0/debian/patches/debian-hacks/Sphinx-Set-intersphinx_mapping-for-py3.patch 2022-02-17 10:40:49.000000000 +0000 @@ -0,0 +1,25 @@ +From: Free Ekanayaka +Date: Fri, 21 Oct 2016 07:37:26 +0000 +Subject: Sphinx: Set intersphinx_mapping for 'py3' + +Use local copies of object.inv for building documentation. + +Forwarded: not-needed +Bug-Debian: https://bugs.debian.org/836169 +--- + docs/conf.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/docs/conf.py b/docs/conf.py +index 3122b10..064e73f 100644 +--- a/docs/conf.py ++++ b/docs/conf.py +@@ -259,7 +259,7 @@ traclinks_base_url = "https://twistedmatrix.com/trac" + # tuple of (, ). + # The inventory file may be None to use the default location at the given URI. + intersphinx_mapping = { +- "py3": ("https://docs.python.org/3", None), ++ 'py3': ('/usr/share/doc/python3-doc/html', None), + "zopeinterface": ("https://zopeinterface.readthedocs.io/en/latest", None), + } + # How long to cache remote inventories. Positive is a number of days, diff -Nru twisted-20.3.0/debian/patches/debian-hacks/Try-exec-ing-ckeygen3-if-ckeygen-was-not-found.patch twisted-22.1.0/debian/patches/debian-hacks/Try-exec-ing-ckeygen3-if-ckeygen-was-not-found.patch --- twisted-20.3.0/debian/patches/debian-hacks/Try-exec-ing-ckeygen3-if-ckeygen-was-not-found.patch 1970-01-01 00:00:00.000000000 +0000 +++ twisted-22.1.0/debian/patches/debian-hacks/Try-exec-ing-ckeygen3-if-ckeygen-was-not-found.patch 2022-02-17 10:40:49.000000000 +0000 @@ -0,0 +1,48 @@ +From: Sergio Durigan Junior +Date: Fri, 12 Feb 2021 16:40:24 -0500 +Subject: Try exec'ing ckeygen3 if ckeygen was not found + +The ckeygen binary is named ckeygen3 "now". + +Author: Sergio Durigan Junior +Forwarded: not-needed +Last-Updated: 2021-02-13 +--- + src/twisted/conch/test/test_ckeygen.py | 17 +++++++++++++---- + 1 file changed, 13 insertions(+), 4 deletions(-) + +diff --git a/src/twisted/conch/test/test_ckeygen.py b/src/twisted/conch/test/test_ckeygen.py +index 584f59e..44ec294 100644 +--- a/src/twisted/conch/test/test_ckeygen.py ++++ b/src/twisted/conch/test/test_ckeygen.py +@@ -77,7 +77,11 @@ class KeyGenTests(TestCase): + args.extend(["-b", keySize]) + if privateKeySubtype is not None: + args.extend(["--private-key-subtype", privateKeySubtype]) +- subprocess.call(args) ++ try: ++ subprocess.call(args) ++ except FileNotFoundError: ++ args[0] = 'ckeygen3' ++ subprocess.call(args) + privKey = Key.fromFile(filename) + pubKey = Key.fromFile(filename + ".pub") + if keyType == "ecdsa": +@@ -107,9 +111,14 @@ class KeyGenTests(TestCase): + filename = self.mktemp() + with self.assertRaises(subprocess.CalledProcessError): + with open(os.devnull, "rb") as devnull: +- subprocess.check_call( +- ['ckeygen', '-t', 'foo', '-f', filename], +- stderr=devnull) ++ try: ++ subprocess.check_call( ++ ['ckeygen', '-t', 'foo', '-f', filename], ++ stderr=devnull) ++ except FileNotFoundError: ++ subprocess.check_call( ++ ['ckeygen3', '-t', 'foo', '-f', filename], ++ stderr=devnull) + + def test_enumrepresentation(self): + """ diff -Nru twisted-20.3.0/debian/patches/documentation/docs-conf.py-Adjust-the-intersphinx-mapping.patch twisted-22.1.0/debian/patches/documentation/docs-conf.py-Adjust-the-intersphinx-mapping.patch --- twisted-20.3.0/debian/patches/documentation/docs-conf.py-Adjust-the-intersphinx-mapping.patch 1970-01-01 00:00:00.000000000 +0000 +++ twisted-22.1.0/debian/patches/documentation/docs-conf.py-Adjust-the-intersphinx-mapping.patch 2022-02-17 10:40:49.000000000 +0000 @@ -0,0 +1,22 @@ +From: Carsten Schoenert +Date: Sun, 16 Jan 2022 08:43:47 +0100 +Subject: docs/conf.py: Adjust the intersphinx mapping + +We can't use a mapping to resources outside the build. +--- + docs/conf.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/docs/conf.py b/docs/conf.py +index a2fbb36..35e3327 100644 +--- a/docs/conf.py ++++ b/docs/conf.py +@@ -260,7 +260,7 @@ traclinks_base_url = "https://twistedmatrix.com/trac" + # The inventory file may be None to use the default location at the given URI. + intersphinx_mapping = { + 'py3': ('/usr/share/doc/python3-doc/html', None), +- 'python': ('http://docs.python.org/3', '/usr/share/doc/python3/html/objects.inv'), ++# 'python': ('http://docs.python.org/3', '/usr/share/doc/python3/html/objects.inv'), + # "zopeinterface": ("https://zopeinterface.readthedocs.io/en/latest", None), + } + # How long to cache remote inventories. Positive is a number of days, diff -Nru twisted-20.3.0/debian/patches/documentation/docs-conf.py-Don-t-use-intersphinx-within-pydoctor_args.patch twisted-22.1.0/debian/patches/documentation/docs-conf.py-Don-t-use-intersphinx-within-pydoctor_args.patch --- twisted-20.3.0/debian/patches/documentation/docs-conf.py-Don-t-use-intersphinx-within-pydoctor_args.patch 1970-01-01 00:00:00.000000000 +0000 +++ twisted-22.1.0/debian/patches/documentation/docs-conf.py-Don-t-use-intersphinx-within-pydoctor_args.patch 2022-02-17 10:40:49.000000000 +0000 @@ -0,0 +1,55 @@ +From: Carsten Schoenert +Date: Sat, 15 Jan 2022 23:15:22 +0100 +Subject: docs/conf.py: Don't use --intersphinx within 'pydoctor_args' + +This option requires access to the internet which isn't allowed while +package build. +Further more the requests library doesn't allow access to local +available files so using the wanted objects.inv files will need more +effort to make this usable. Also currently only a few of this files are +included in packages. +--- + docs/conf.py | 23 ++++++++++++----------- + 1 file changed, 12 insertions(+), 11 deletions(-) + +diff --git a/docs/conf.py b/docs/conf.py +index f07e93f..a2fbb36 100644 +--- a/docs/conf.py ++++ b/docs/conf.py +@@ -236,16 +236,16 @@ pydoctor_args = [ + "--project-url=https://twistedmatrix.com/", + "--system-class=twisted.python._pydoctor.TwistedSystem", + "--docformat=epytext", +- "--intersphinx=https://docs.python.org/3/objects.inv", +- "--intersphinx=https://cryptography.io/en/latest/objects.inv", +- "--intersphinx=https://pyopenssl.readthedocs.io/en/stable/objects.inv", +- "--intersphinx=https://hyperlink.readthedocs.io/en/stable/objects.inv", +- "--intersphinx=https://twisted.org/constantly/docs/objects.inv", +- "--intersphinx=https://twisted.org/incremental/docs/objects.inv", +- "--intersphinx=https://python-hyper.org/projects/hyper-h2/en/stable/objects.inv", +- "--intersphinx=https://priority.readthedocs.io/en/stable/objects.inv", +- "--intersphinx=https://zopeinterface.readthedocs.io/en/latest/objects.inv", +- "--intersphinx=https://automat.readthedocs.io/en/latest/objects.inv", ++# "--intersphinx=https://docs.python.org/3/objects.inv", ++# "--intersphinx=https://cryptography.io/en/latest/objects.inv", ++# "--intersphinx=https://pyopenssl.readthedocs.io/en/stable/objects.inv", ++# "--intersphinx=https://hyperlink.readthedocs.io/en/stable/objects.inv", ++# "--intersphinx=https://twisted.org/constantly/docs/objects.inv", ++# "--intersphinx=https://twisted.org/incremental/docs/objects.inv", ++# "--intersphinx=https://python-hyper.org/projects/hyper-h2/en/stable/objects.inv", ++# "--intersphinx=https://priority.readthedocs.io/en/stable/objects.inv", ++# "--intersphinx=https://zopeinterface.readthedocs.io/en/latest/objects.inv", ++# "--intersphinx=https://automat.readthedocs.io/en/latest/objects.inv", + f"--project-base-dir={_source_root}", + "--html-output={outdir}/api", + str(_source_root / "twisted"), +@@ -260,7 +260,8 @@ traclinks_base_url = "https://twistedmatrix.com/trac" + # The inventory file may be None to use the default location at the given URI. + intersphinx_mapping = { + 'py3': ('/usr/share/doc/python3-doc/html', None), +- "zopeinterface": ("https://zopeinterface.readthedocs.io/en/latest", None), ++ 'python': ('http://docs.python.org/3', '/usr/share/doc/python3/html/objects.inv'), ++# "zopeinterface": ("https://zopeinterface.readthedocs.io/en/latest", None), + } + # How long to cache remote inventories. Positive is a number of days, + # negative means infinite. The default is 5 days, which should be fine diff -Nru twisted-20.3.0/debian/patches/documentation/docs-Don-t-depend-on-git-stuff.patch twisted-22.1.0/debian/patches/documentation/docs-Don-t-depend-on-git-stuff.patch --- twisted-20.3.0/debian/patches/documentation/docs-Don-t-depend-on-git-stuff.patch 1970-01-01 00:00:00.000000000 +0000 +++ twisted-22.1.0/debian/patches/documentation/docs-Don-t-depend-on-git-stuff.patch 2022-02-17 10:40:49.000000000 +0000 @@ -0,0 +1,72 @@ +From: Carsten Schoenert +Date: Sun, 2 Jan 2022 20:25:00 +0100 +Subject: docs: Don't depend on git stuff + +In preparation for the build of the documentation the system tries to +detect the Git status, we don't build within a Git tree and therefore +the build will fail. Ignoring all that Git thingy and use a static link +to the upstream source. +--- + docs/conf.py | 30 +++++++++++++++--------------- + 1 file changed, 15 insertions(+), 15 deletions(-) + +diff --git a/docs/conf.py b/docs/conf.py +index c86ec5b..f07e93f 100644 +--- a/docs/conf.py ++++ b/docs/conf.py +@@ -188,18 +188,18 @@ epub_copyright = "2020, Twisted Matrix Labs" + + + # -- Extension configuration ---------------------------------------------- +-_git_reference = subprocess.run( +- ["git", "rev-parse", "--abbrev-ref", "HEAD"], +- text=True, +- encoding="utf8", +- capture_output=True, +- check=True, +-).stdout ++#_git_reference = subprocess.run( ++# ["git", "rev-parse", "--abbrev-ref", "HEAD"], ++# text=True, ++# encoding="utf8", ++# capture_output=True, ++# check=True, ++#).stdout + + +-print(f"== Environment dump for {_git_reference} ===") +-pprint(dict(os.environ)) +-print("======") ++#print(f"== Environment dump for {_git_reference} ===") ++#pprint(dict(os.environ)) ++#print("======") + + # Base url for API docs + api_base_url = f"/documents/{release}/api/" +@@ -217,21 +217,21 @@ if on_rtd: + # Try to find URL fragment for the GitHub source page based on current + # branch or tag. + +-if _git_reference == "HEAD": ++#if _git_reference == "HEAD": + # It looks like the branch has no name. + # Fallback to commit ID. +- _git_reference = subprocess.getoutput("git rev-parse HEAD") ++# _git_reference = subprocess.getoutput("git rev-parse HEAD") + + if os.environ.get("READTHEDOCS", "") == "True": + rtd_version = os.environ.get("READTHEDOCS_VERSION", "") +- if "." in rtd_version: ++# if "." in rtd_version: + # It looks like we have a tag build. +- _git_reference = rtd_version ++# _git_reference = rtd_version + + _source_root = pathlib.Path(__file__).parent.parent / "src" + pydoctor_args = [ + "--quiet", +- f"--html-viewsource-base=https://github.com/twisted/twisted/tree/{_git_reference}/src", ++ f"--html-viewsource-base=https://github.com/twisted/twisted/tree/trunk", + "--project-name=Twisted", + "--project-url=https://twistedmatrix.com/", + "--system-class=twisted.python._pydoctor.TwistedSystem", diff -Nru twisted-20.3.0/debian/patches/import-Literal-from-typing.patch twisted-22.1.0/debian/patches/import-Literal-from-typing.patch --- twisted-20.3.0/debian/patches/import-Literal-from-typing.patch 1970-01-01 00:00:00.000000000 +0000 +++ twisted-22.1.0/debian/patches/import-Literal-from-typing.patch 2022-02-23 19:12:14.000000000 +0000 @@ -0,0 +1,100 @@ +Description: Import Literal from typing instead of typing_extensions +Author: Graham Inggs +Last-Update: 2022-02-23 + +--- a/setup.cfg ++++ b/setup.cfg +@@ -33,7 +33,6 @@ + Automat >= 0.8.0 + hyperlink >= 17.1.1 + attrs >= 19.2.0 +- typing_extensions >= 3.6.5 + twisted-iocpsupport >= 1.0.2, <2; platform_system == "Windows" + include_package_data = True + zip_safe = False +--- a/src/Twisted.egg-info/requires.txt ++++ b/src/Twisted.egg-info/requires.txt +@@ -4,7 +4,6 @@ + Automat>=0.8.0 + hyperlink>=17.1.1 + attrs>=19.2.0 +-typing_extensions>=3.6.5 + + [:platform_system == "Windows"] + twisted-iocpsupport<2,>=1.0.2 +--- a/src/twisted/protocols/haproxy/_v2parser.py ++++ b/src/twisted/protocols/haproxy/_v2parser.py +@@ -9,12 +9,11 @@ + + import binascii + import struct +-from typing import Callable, Tuple, Type, Union ++from typing import Callable, Literal, Tuple, Type, Union + + from zope.interface import implementer + + from constantly import ValueConstant, Values # type: ignore[import] +-from typing_extensions import Literal + + from twisted.internet import address + from twisted.python import compat +--- a/src/twisted/internet/tcp.py ++++ b/src/twisted/internet/tcp.py +@@ -14,12 +14,11 @@ + import socket + import struct + import sys +-from typing import Callable, ClassVar, List, Optional ++from typing import Callable, ClassVar, List, Optional, Protocol + + from zope.interface import Interface, implementer + + import attr +-import typing_extensions + + from twisted.internet.interfaces import ( + IHalfCloseableProtocol, +@@ -936,7 +935,7 @@ + """ + + +-class _HasClose(typing_extensions.Protocol): ++class _HasClose(Protocol): + def close(self) -> object: + ... + +--- a/src/twisted/internet/defer.py ++++ b/src/twisted/internet/defer.py +@@ -26,6 +26,7 @@ + Generic, + Iterable, + List, ++ Literal, + Mapping, + NoReturn, + Optional, +@@ -39,7 +40,6 @@ + + import attr + from incremental import Version +-from typing_extensions import Literal + + from twisted.internet.interfaces import IDelayedCall, IReactorTime + from twisted.logger import Logger +--- a/src/twisted/internet/address.py ++++ b/src/twisted/internet/address.py +@@ -7,13 +7,12 @@ + + + import os +-from typing import Optional, Union ++from typing import Literal, Optional, Union + from warnings import warn + + from zope.interface import implementer + + import attr +-from typing_extensions import Literal + + from twisted.internet.interfaces import IAddress + from twisted.python.filepath import _asFilesystemBytes, _coerceToFilesystemEncoding diff -Nru twisted-20.3.0/debian/patches/lp1915819-Fix-nonetype-encode-error.patch twisted-22.1.0/debian/patches/lp1915819-Fix-nonetype-encode-error.patch --- twisted-20.3.0/debian/patches/lp1915819-Fix-nonetype-encode-error.patch 2021-02-17 11:48:20.000000000 +0000 +++ twisted-22.1.0/debian/patches/lp1915819-Fix-nonetype-encode-error.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,67 +0,0 @@ -From 31049624933f5af7d5caf51d1e00f6b0b770e9bc Mon Sep 17 00:00:00 2001 -From: Victor Tapia -Date: Wed, 10 Feb 2021 17:08:17 +0100 -Subject: [PATCH] Fix NoneType encode error when multipart body part does not - include headers - -Bug-Ubuntu: https://launchpad.net/bugs/1915819 -Origin: upstream, https://github.com/twisted/twisted/commit/bf02deda3117e82ed844f56b989e692a84daca95 ---- - src/twisted/web/http.py | 1 + - src/twisted/web/newsfragments/10084.bugfix | 1 + - src/twisted/web/test/test_http.py | 27 ++++++++++++++++++++++ - 3 files changed, 29 insertions(+) - create mode 100644 src/twisted/web/newsfragments/10084.bugfix - ---- a/src/twisted/web/http.py -+++ b/src/twisted/web/http.py -@@ -928,7 +928,8 @@ - x.encode('iso-8859-1'): \ - [z.encode('utf8', "surrogateescape") - if isinstance(z, str) else z for z in y] -- for x, y in cgiArgs.items()}) -+ for x, y in cgiArgs.items() -+ if isinstance(x, str)}) - - else: - self.args.update(cgiArgs) ---- /dev/null -+++ b/src/twisted/web/newsfragments/10084.bugfix -@@ -0,0 +1 @@ -+Fixed an error where twisted.web.http.requestReceived() tries to encode a NoneType returned by cgi.parse_multipart when a multipart body does not contain a "content-disposition" definition. ---- a/src/twisted/web/test/test_http.py -+++ b/src/twisted/web/test/test_http.py -@@ -2040,6 +2040,33 @@ - b"HTTP/1.1 400 Bad Request\r\n\r\n") - - -+ def test_multipartEmptyHeaderProcessingFailure(self): -+ """ -+ When the multipart does not contain a header is should be skipped -+ """ -+ processed = [] -+ -+ class MyRequest(http.Request): -+ def process(self): -+ processed.append(self) -+ self.write(b"done") -+ self.finish() -+ -+ # The parsing failure is encoding a NoneType key when name is not -+ # defined in Content-Disposition -+ req = b"""\ -+POST / HTTP/1.0 -+Content-Type: multipart/form-data; boundary=AaBb1313 -+Content-Length: 14 -+ -+--AaBb1313 -+ -+--AaBb1313-- -+""" -+ channel = self.runRequest(req, MyRequest, success=False) -+ self.assertEqual(channel.transport.value(), b"HTTP/1.0 200 OK\r\n\r\ndone") -+ self.assertEqual(processed[0].args, {}) -+ - def test_multipartFormData(self): - """ - If the request has a Content-Type of C{multipart/form-data}, and the diff -Nru twisted-20.3.0/debian/patches/series twisted-22.1.0/debian/patches/series --- twisted-20.3.0/debian/patches/series 2021-08-10 09:34:55.000000000 +0000 +++ twisted-22.1.0/debian/patches/series 2022-02-23 18:56:56.000000000 +0000 @@ -1,25 +1,18 @@ -0001-wxpython3.0.patch -0002-combinedlog.patch -0003-sphinx-theme.patch -0004-localIntersphinx.patch -0005-insecure-pythonpath.patch -0006-fix-sphinx-import-path.patch -0008-sort-option-keys.patch -0009-no-stderr-in-test_ckeygen.patch -0010-handle-setlocale-test-failure.patch -0010-spurious-failure-in-setup-unit-tests.patch -#0011-Ignore-fuction-name-in-SSL-error-code-in-tests-to-wo.patch -0012-Skip-test-for-empty-cypher-string-openssl-does-not-t.patch -0013-Drop-test_givesMeaningfulErrorMessageIfNoCipherMatch.patch -0015-Fix-tests-to-expect-new-web-request-logging-format.patch -0016-Try-exec-ing-ckeygen3-if-ckeygen-was-not-found.patch -0017-Add-digestmod-parameter-to-HMAC.__init__-invocations.patch -0018-Make-the-twisted-tests-work-when-pyOpenSSL-deletes-N.patch -0019-Replace-base64.-string-functions-to-fix-py3.9-suppor.patch -0020-Fix-imap4-utf-7-codec-lookup-function-for-Python-3.9.patch -0021-Merge-9652-wiml-mktime-Allow-mktime-to-raise-EOVERFL.patch -0022-increase-size-of-FFDH-keys-for-conch-testing.patch -0023-Merge-9801-rodrigc-cgi-Change-import-of-cgi.parse_qs.patch -0024-fixed-corrupted-iqmp-value-in-test-RSA-key.patch -0025-Skip-failing-twisted.web.test.test_http.QueryArgumen.patch -lp1915819-Fix-nonetype-encode-error.patch +debian-hacks/Sphinx-Set-html_theme-to-twisteddefault.patch +debian-hacks/Sphinx-Set-intersphinx_mapping-for-py3.patch +debian-hacks/Security-Fix-vulnerable-example-of-PYTHONPATH.patch +debian-hacks/Sphinx-Adjust-setup-of-sys.path.insert.patch +tests/Tests-Fix-ckeygen-test-writing-to-stderr.patch +tests/Tests-Handle-setlocale-more-tolerant.patch +tests/Tests-Skip-test-for-empty-cypher-string.patch +tests/Tests-Drop-test_givesMeaningfulErrorMessageIfNoCipherMatc.patch +debian-hacks/Try-exec-ing-ckeygen3-if-ckeygen-was-not-found.patch +documentation/docs-Don-t-depend-on-git-stuff.patch +documentation/docs-conf.py-Don-t-use-intersphinx-within-pydoctor_args.patch +documentation/docs-conf.py-Adjust-the-intersphinx-mapping.patch +tests/Testing-Ignore-test-around-git-tooling.patch +tests/Test-Ignore-test_failure.py-file.patch +tests/Tests-Ignore-test_listingModulesAlreadyImport.patch +tests/Tests-Ignore-tests-with-some-version-checking.patch +tests/Tests-Ignore-test_unicodeLogFileUTF8.patch +import-Literal-from-typing.patch diff -Nru twisted-20.3.0/debian/patches/tests/Test-Ignore-test_failure.py-file.patch twisted-22.1.0/debian/patches/tests/Test-Ignore-test_failure.py-file.patch --- twisted-20.3.0/debian/patches/tests/Test-Ignore-test_failure.py-file.patch 1970-01-01 00:00:00.000000000 +0000 +++ twisted-22.1.0/debian/patches/tests/Test-Ignore-test_failure.py-file.patch 2022-02-17 10:40:49.000000000 +0000 @@ -0,0 +1,1931 @@ +From: Carsten Schoenert +Date: Mon, 17 Jan 2022 08:07:48 +0100 +Subject: Test: Ignore test_failure.py file + +This test file requires the library cython_test_exception_raiser which isn't +packaged yet. +--- + src/twisted/test/ignore_test_failure.py | 953 ++++++++++++++++++++++++++++++++ + src/twisted/test/test_failure.py | 953 -------------------------------- + 2 files changed, 953 insertions(+), 953 deletions(-) + create mode 100644 src/twisted/test/ignore_test_failure.py + delete mode 100644 src/twisted/test/test_failure.py + +diff --git a/src/twisted/test/ignore_test_failure.py b/src/twisted/test/ignore_test_failure.py +new file mode 100644 +index 0000000..46f4b63 +--- /dev/null ++++ b/src/twisted/test/ignore_test_failure.py +@@ -0,0 +1,953 @@ ++# Copyright (c) Twisted Matrix Laboratories. ++# See LICENSE for details. ++ ++""" ++Test cases for the L{twisted.python.failure} module. ++""" ++ ++ ++import linecache ++import pdb ++import re ++import sys ++import traceback ++from io import StringIO ++from traceback import FrameSummary ++from unittest import skipIf ++ ++from cython_test_exception_raiser import raiser # type: ignore[import] ++ ++from twisted.python import failure, reflect ++from twisted.trial.unittest import SynchronousTestCase ++ ++ ++def getDivisionFailure(*args, **kwargs): ++ """ ++ Make a C{Failure} of a divide-by-zero error. ++ ++ @param args: Any C{*args} are passed to Failure's constructor. ++ @param kwargs: Any C{**kwargs} are passed to Failure's constructor. ++ """ ++ try: ++ 1 / 0 ++ except BaseException: ++ f = failure.Failure(*args, **kwargs) ++ return f ++ ++ ++class FailureTests(SynchronousTestCase): ++ """ ++ Tests for L{failure.Failure}. ++ """ ++ ++ def test_failAndTrap(self): ++ """ ++ Trapping a L{Failure}. ++ """ ++ try: ++ raise NotImplementedError("test") ++ except BaseException: ++ f = failure.Failure() ++ error = f.trap(SystemExit, RuntimeError) ++ self.assertEqual(error, RuntimeError) ++ self.assertEqual(f.type, NotImplementedError) ++ ++ def test_trapRaisesWrappedException(self): ++ """ ++ If the wrapped C{Exception} is not a subclass of one of the ++ expected types, L{failure.Failure.trap} raises the wrapped ++ C{Exception}. ++ """ ++ exception = ValueError() ++ try: ++ raise exception ++ except BaseException: ++ f = failure.Failure() ++ ++ untrapped = self.assertRaises(ValueError, f.trap, OverflowError) ++ self.assertIs(exception, untrapped) ++ ++ def test_failureValueFromFailure(self): ++ """ ++ A L{failure.Failure} constructed from another ++ L{failure.Failure} instance, has its C{value} property set to ++ the value of that L{failure.Failure} instance. ++ """ ++ exception = ValueError() ++ f1 = failure.Failure(exception) ++ f2 = failure.Failure(f1) ++ self.assertIs(f2.value, exception) ++ ++ def test_failureValueFromFoundFailure(self): ++ """ ++ A L{failure.Failure} constructed without a C{exc_value} ++ argument, will search for an "original" C{Failure}, and if ++ found, its value will be used as the value for the new ++ C{Failure}. ++ """ ++ exception = ValueError() ++ f1 = failure.Failure(exception) ++ try: ++ f1.trap(OverflowError) ++ except BaseException: ++ f2 = failure.Failure() ++ ++ self.assertIs(f2.value, exception) ++ ++ def assertStartsWith(self, s, prefix): ++ """ ++ Assert that C{s} starts with a particular C{prefix}. ++ ++ @param s: The input string. ++ @type s: C{str} ++ @param prefix: The string that C{s} should start with. ++ @type prefix: C{str} ++ """ ++ self.assertTrue(s.startswith(prefix), f"{prefix!r} is not the start of {s!r}") ++ ++ def assertEndsWith(self, s, suffix): ++ """ ++ Assert that C{s} end with a particular C{suffix}. ++ ++ @param s: The input string. ++ @type s: C{str} ++ @param suffix: The string that C{s} should end with. ++ @type suffix: C{str} ++ """ ++ self.assertTrue(s.endswith(suffix), f"{suffix!r} is not the end of {s!r}") ++ ++ def assertTracebackFormat(self, tb, prefix, suffix): ++ """ ++ Assert that the C{tb} traceback contains a particular C{prefix} and ++ C{suffix}. ++ ++ @param tb: The traceback string. ++ @type tb: C{str} ++ @param prefix: The string that C{tb} should start with. ++ @type prefix: C{str} ++ @param suffix: The string that C{tb} should end with. ++ @type suffix: C{str} ++ """ ++ self.assertStartsWith(tb, prefix) ++ self.assertEndsWith(tb, suffix) ++ ++ def assertDetailedTraceback(self, captureVars=False, cleanFailure=False): ++ """ ++ Assert that L{printDetailedTraceback} produces and prints a detailed ++ traceback. ++ ++ The detailed traceback consists of a header:: ++ ++ *--- Failure #20 --- ++ ++ The body contains the stacktrace:: ++ ++ /twisted/trial/_synctest.py:1180: _run(...) ++ /twisted/python/util.py:1076: runWithWarningsSuppressed(...) ++ --- --- ++ /twisted/test/test_failure.py:39: getDivisionFailure(...) ++ ++ If C{captureVars} is enabled the body also includes a list of ++ globals and locals:: ++ ++ [ Locals ] ++ exampleLocalVar : 'xyz' ++ ... ++ ( Globals ) ++ ... ++ ++ Or when C{captureVars} is disabled:: ++ ++ [Capture of Locals and Globals disabled (use captureVars=True)] ++ ++ When C{cleanFailure} is enabled references to other objects are removed ++ and replaced with strings. ++ ++ And finally the footer with the L{Failure}'s value:: ++ ++ exceptions.ZeroDivisionError: float division ++ *--- End of Failure #20 --- ++ ++ @param captureVars: Enables L{Failure.captureVars}. ++ @type captureVars: C{bool} ++ @param cleanFailure: Enables L{Failure.cleanFailure}. ++ @type cleanFailure: C{bool} ++ """ ++ if captureVars: ++ exampleLocalVar = "xyz" ++ # Silence the linter as this variable is checked via ++ # the traceback. ++ exampleLocalVar ++ ++ f = getDivisionFailure(captureVars=captureVars) ++ out = StringIO() ++ if cleanFailure: ++ f.cleanFailure() ++ f.printDetailedTraceback(out) ++ ++ tb = out.getvalue() ++ start = "*--- Failure #%d%s---\n" % ( ++ f.count, ++ (f.pickled and " (pickled) ") or " ", ++ ) ++ end = "{}: {}\n*--- End of Failure #{} ---\n".format( ++ reflect.qual(f.type), ++ reflect.safe_str(f.value), ++ f.count, ++ ) ++ self.assertTracebackFormat(tb, start, end) ++ ++ # Variables are printed on lines with 2 leading spaces. ++ linesWithVars = [line for line in tb.splitlines() if line.startswith(" ")] ++ ++ if captureVars: ++ self.assertNotEqual([], linesWithVars) ++ if cleanFailure: ++ line = " exampleLocalVar : \"'xyz'\"" ++ else: ++ line = " exampleLocalVar : 'xyz'" ++ self.assertIn(line, linesWithVars) ++ else: ++ self.assertEqual([], linesWithVars) ++ self.assertIn( ++ " [Capture of Locals and Globals disabled (use " "captureVars=True)]\n", ++ tb, ++ ) ++ ++ def assertBriefTraceback(self, captureVars=False): ++ """ ++ Assert that L{printBriefTraceback} produces and prints a brief ++ traceback. ++ ++ The brief traceback consists of a header:: ++ ++ Traceback: : float division ++ ++ The body with the stacktrace:: ++ ++ /twisted/trial/_synctest.py:1180:_run ++ /twisted/python/util.py:1076:runWithWarningsSuppressed ++ ++ And the footer:: ++ ++ --- --- ++ /twisted/test/test_failure.py:39:getDivisionFailure ++ ++ @param captureVars: Enables L{Failure.captureVars}. ++ @type captureVars: C{bool} ++ """ ++ if captureVars: ++ exampleLocalVar = "abcde" ++ # Silence the linter as this variable is checked via ++ # the traceback. ++ exampleLocalVar ++ ++ f = getDivisionFailure() ++ out = StringIO() ++ f.printBriefTraceback(out) ++ tb = out.getvalue() ++ stack = "" ++ for method, filename, lineno, localVars, globalVars in f.frames: ++ stack += f"{filename}:{lineno}:{method}\n" ++ ++ zde = repr(ZeroDivisionError) ++ self.assertTracebackFormat( ++ tb, ++ f"Traceback: {zde}: ", ++ f"{failure.EXCEPTION_CAUGHT_HERE}\n{stack}", ++ ) ++ ++ if captureVars: ++ self.assertIsNone(re.search("exampleLocalVar.*abcde", tb)) ++ ++ def assertDefaultTraceback(self, captureVars=False): ++ """ ++ Assert that L{printTraceback} produces and prints a default traceback. ++ ++ The default traceback consists of a header:: ++ ++ Traceback (most recent call last): ++ ++ The body with traceback:: ++ ++ File "/twisted/trial/_synctest.py", line 1180, in _run ++ runWithWarningsSuppressed(suppress, method) ++ ++ And the footer:: ++ ++ --- --- ++ File "twisted/test/test_failure.py", line 39, in getDivisionFailure ++ 1 / 0 ++ exceptions.ZeroDivisionError: float division ++ ++ @param captureVars: Enables L{Failure.captureVars}. ++ @type captureVars: C{bool} ++ """ ++ if captureVars: ++ exampleLocalVar = "xyzzy" ++ # Silence the linter as this variable is checked via ++ # the traceback. ++ exampleLocalVar ++ ++ f = getDivisionFailure(captureVars=captureVars) ++ out = StringIO() ++ f.printTraceback(out) ++ tb = out.getvalue() ++ stack = "" ++ for method, filename, lineno, localVars, globalVars in f.frames: ++ stack += f' File "{filename}", line {lineno}, in {method}\n' ++ stack += f" {linecache.getline(filename, lineno).strip()}\n" ++ ++ self.assertTracebackFormat( ++ tb, ++ "Traceback (most recent call last):", ++ "%s\n%s%s: %s\n" ++ % ( ++ failure.EXCEPTION_CAUGHT_HERE, ++ stack, ++ reflect.qual(f.type), ++ reflect.safe_str(f.value), ++ ), ++ ) ++ ++ if captureVars: ++ self.assertIsNone(re.search("exampleLocalVar.*xyzzy", tb)) ++ ++ def test_printDetailedTraceback(self): ++ """ ++ L{printDetailedTraceback} returns a detailed traceback including the ++ L{Failure}'s count. ++ """ ++ self.assertDetailedTraceback() ++ ++ def test_printBriefTraceback(self): ++ """ ++ L{printBriefTraceback} returns a brief traceback. ++ """ ++ self.assertBriefTraceback() ++ ++ def test_printTraceback(self): ++ """ ++ L{printTraceback} returns a traceback. ++ """ ++ self.assertDefaultTraceback() ++ ++ def test_printDetailedTracebackCapturedVars(self): ++ """ ++ L{printDetailedTraceback} captures the locals and globals for its ++ stack frames and adds them to the traceback, when called on a ++ L{Failure} constructed with C{captureVars=True}. ++ """ ++ self.assertDetailedTraceback(captureVars=True) ++ ++ def test_printBriefTracebackCapturedVars(self): ++ """ ++ L{printBriefTraceback} returns a brief traceback when called on a ++ L{Failure} constructed with C{captureVars=True}. ++ ++ Local variables on the stack can not be seen in the resulting ++ traceback. ++ """ ++ self.assertBriefTraceback(captureVars=True) ++ ++ def test_printTracebackCapturedVars(self): ++ """ ++ L{printTraceback} returns a traceback when called on a L{Failure} ++ constructed with C{captureVars=True}. ++ ++ Local variables on the stack can not be seen in the resulting ++ traceback. ++ """ ++ self.assertDefaultTraceback(captureVars=True) ++ ++ def test_printDetailedTracebackCapturedVarsCleaned(self): ++ """ ++ C{printDetailedTraceback} includes information about local variables on ++ the stack after C{cleanFailure} has been called. ++ """ ++ self.assertDetailedTraceback(captureVars=True, cleanFailure=True) ++ ++ def test_invalidFormatFramesDetail(self): ++ """ ++ L{failure.format_frames} raises a L{ValueError} if the supplied ++ C{detail} level is unknown. ++ """ ++ self.assertRaises( ++ ValueError, failure.format_frames, None, None, detail="noisia" ++ ) ++ ++ def test_ExplictPass(self): ++ e = RuntimeError() ++ f = failure.Failure(e) ++ f.trap(RuntimeError) ++ self.assertEqual(f.value, e) ++ ++ def _getInnermostFrameLine(self, f): ++ try: ++ f.raiseException() ++ except ZeroDivisionError: ++ tb = traceback.extract_tb(sys.exc_info()[2]) ++ return tb[-1][-1] ++ else: ++ raise Exception("f.raiseException() didn't raise ZeroDivisionError!?") ++ ++ def test_RaiseExceptionWithTB(self): ++ f = getDivisionFailure() ++ innerline = self._getInnermostFrameLine(f) ++ self.assertEqual(innerline, "1 / 0") ++ ++ def test_stringExceptionConstruction(self): ++ """ ++ Constructing a C{Failure} with a string as its exception value raises ++ a C{TypeError}, as this is no longer supported as of Python 2.6. ++ """ ++ exc = self.assertRaises(TypeError, failure.Failure, "ono!") ++ self.assertIn("Strings are not supported by Failure", str(exc)) ++ ++ def test_ConstructionFails(self): ++ """ ++ Creating a Failure with no arguments causes it to try to discover the ++ current interpreter exception state. If no such state exists, creating ++ the Failure should raise a synchronous exception. ++ """ ++ self.assertRaises(failure.NoCurrentExceptionError, failure.Failure) ++ ++ def test_getTracebackObject(self): ++ """ ++ If the C{Failure} has not been cleaned, then C{getTracebackObject} ++ returns the traceback object that captured in its constructor. ++ """ ++ f = getDivisionFailure() ++ self.assertEqual(f.getTracebackObject(), f.tb) ++ ++ def test_getTracebackObjectFromCaptureVars(self): ++ """ ++ C{captureVars=True} has no effect on the result of ++ C{getTracebackObject}. ++ """ ++ try: ++ 1 / 0 ++ except ZeroDivisionError: ++ noVarsFailure = failure.Failure() ++ varsFailure = failure.Failure(captureVars=True) ++ self.assertEqual(noVarsFailure.getTracebackObject(), varsFailure.tb) ++ ++ def test_getTracebackObjectFromClean(self): ++ """ ++ If the Failure has been cleaned, then C{getTracebackObject} returns an ++ object that looks the same to L{traceback.extract_tb}. ++ """ ++ f = getDivisionFailure() ++ expected = traceback.extract_tb(f.getTracebackObject()) ++ f.cleanFailure() ++ observed = traceback.extract_tb(f.getTracebackObject()) ++ self.assertIsNotNone(expected) ++ self.assertEqual(expected, observed) ++ ++ def test_getTracebackObjectFromCaptureVarsAndClean(self): ++ """ ++ If the Failure was created with captureVars, then C{getTracebackObject} ++ returns an object that looks the same to L{traceback.extract_tb}. ++ """ ++ f = getDivisionFailure(captureVars=True) ++ expected = traceback.extract_tb(f.getTracebackObject()) ++ f.cleanFailure() ++ observed = traceback.extract_tb(f.getTracebackObject()) ++ self.assertEqual(expected, observed) ++ ++ def test_getTracebackObjectWithoutTraceback(self): ++ """ ++ L{failure.Failure}s need not be constructed with traceback objects. If ++ a C{Failure} has no traceback information at all, C{getTracebackObject} ++ just returns None. ++ ++ None is a good value, because traceback.extract_tb(None) -> []. ++ """ ++ f = failure.Failure(Exception("some error")) ++ self.assertIsNone(f.getTracebackObject()) ++ ++ def test_tracebackFromExceptionInPython3(self): ++ """ ++ If a L{failure.Failure} is constructed with an exception but no ++ traceback in Python 3, the traceback will be extracted from the ++ exception's C{__traceback__} attribute. ++ """ ++ try: ++ 1 / 0 ++ except BaseException: ++ klass, exception, tb = sys.exc_info() ++ f = failure.Failure(exception) ++ self.assertIs(f.tb, tb) ++ ++ def test_cleanFailureRemovesTracebackInPython3(self): ++ """ ++ L{failure.Failure.cleanFailure} sets the C{__traceback__} attribute of ++ the exception to L{None} in Python 3. ++ """ ++ f = getDivisionFailure() ++ self.assertIsNotNone(f.tb) ++ self.assertIs(f.value.__traceback__, f.tb) ++ f.cleanFailure() ++ self.assertIsNone(f.value.__traceback__) ++ ++ def test_repr(self): ++ """ ++ The C{repr} of a L{failure.Failure} shows the type and string ++ representation of the underlying exception. ++ """ ++ f = getDivisionFailure() ++ typeName = reflect.fullyQualifiedName(ZeroDivisionError) ++ self.assertEqual( ++ repr(f), ++ "" % (typeName,), ++ ) ++ ++ ++class BrokenStr(Exception): ++ """ ++ An exception class the instances of which cannot be presented as strings ++ via L{str}. ++ """ ++ ++ def __str__(self) -> str: ++ # Could raise something else, but there's no point as yet. ++ raise self ++ ++ ++class BrokenExceptionMetaclass(type): ++ """ ++ A metaclass for an exception type which cannot be presented as a string via ++ L{str}. ++ """ ++ ++ def __str__(self) -> str: ++ raise ValueError("You cannot make a string out of me.") ++ ++ ++class BrokenExceptionType(Exception, metaclass=BrokenExceptionMetaclass): ++ ++ """ ++ The aforementioned exception type which cannot be presented as a string via ++ L{str}. ++ """ ++ ++ ++class GetTracebackTests(SynchronousTestCase): ++ """ ++ Tests for L{Failure.getTraceback}. ++ """ ++ ++ def _brokenValueTest(self, detail): ++ """ ++ Construct a L{Failure} with an exception that raises an exception from ++ its C{__str__} method and then call C{getTraceback} with the specified ++ detail and verify that it returns a string. ++ """ ++ x = BrokenStr() ++ f = failure.Failure(x) ++ traceback = f.getTraceback(detail=detail) ++ self.assertIsInstance(traceback, str) ++ ++ def test_brokenValueBriefDetail(self): ++ """ ++ A L{Failure} might wrap an exception with a C{__str__} method which ++ raises an exception. In this case, calling C{getTraceback} on the ++ failure with the C{"brief"} detail does not raise an exception. ++ """ ++ self._brokenValueTest("brief") ++ ++ def test_brokenValueDefaultDetail(self): ++ """ ++ Like test_brokenValueBriefDetail, but for the C{"default"} detail case. ++ """ ++ self._brokenValueTest("default") ++ ++ def test_brokenValueVerboseDetail(self): ++ """ ++ Like test_brokenValueBriefDetail, but for the C{"default"} detail case. ++ """ ++ self._brokenValueTest("verbose") ++ ++ def _brokenTypeTest(self, detail): ++ """ ++ Construct a L{Failure} with an exception type that raises an exception ++ from its C{__str__} method and then call C{getTraceback} with the ++ specified detail and verify that it returns a string. ++ """ ++ f = failure.Failure(BrokenExceptionType()) ++ traceback = f.getTraceback(detail=detail) ++ self.assertIsInstance(traceback, str) ++ ++ def test_brokenTypeBriefDetail(self): ++ """ ++ A L{Failure} might wrap an exception the type object of which has a ++ C{__str__} method which raises an exception. In this case, calling ++ C{getTraceback} on the failure with the C{"brief"} detail does not raise ++ an exception. ++ """ ++ self._brokenTypeTest("brief") ++ ++ def test_brokenTypeDefaultDetail(self): ++ """ ++ Like test_brokenTypeBriefDetail, but for the C{"default"} detail case. ++ """ ++ self._brokenTypeTest("default") ++ ++ def test_brokenTypeVerboseDetail(self): ++ """ ++ Like test_brokenTypeBriefDetail, but for the C{"verbose"} detail case. ++ """ ++ self._brokenTypeTest("verbose") ++ ++ ++class FindFailureTests(SynchronousTestCase): ++ """ ++ Tests for functionality related to L{Failure._findFailure}. ++ """ ++ ++ def test_findNoFailureInExceptionHandler(self): ++ """ ++ Within an exception handler, _findFailure should return ++ L{None} in case no Failure is associated with the current ++ exception. ++ """ ++ try: ++ 1 / 0 ++ except BaseException: ++ self.assertIsNone(failure.Failure._findFailure()) ++ else: ++ self.fail("No exception raised from 1/0!?") ++ ++ def test_findNoFailure(self): ++ """ ++ Outside of an exception handler, _findFailure should return None. ++ """ ++ self.assertIsNone(sys.exc_info()[-1]) # environment sanity check ++ self.assertIsNone(failure.Failure._findFailure()) ++ ++ def test_findFailure(self): ++ """ ++ Within an exception handler, it should be possible to find the ++ original Failure that caused the current exception (if it was ++ caused by raiseException). ++ """ ++ f = getDivisionFailure() ++ f.cleanFailure() ++ try: ++ f.raiseException() ++ except BaseException: ++ self.assertEqual(failure.Failure._findFailure(), f) ++ else: ++ self.fail("No exception raised from raiseException!?") ++ ++ def test_failureConstructionFindsOriginalFailure(self): ++ """ ++ When a Failure is constructed in the context of an exception ++ handler that is handling an exception raised by ++ raiseException, the new Failure should be chained to that ++ original Failure. ++ Means the new failure should still show the same origin frame, ++ but with different complete stack trace (as not thrown at same place). ++ """ ++ f = getDivisionFailure() ++ f.cleanFailure() ++ try: ++ f.raiseException() ++ except BaseException: ++ newF = failure.Failure() ++ tb = f.getTraceback().splitlines() ++ new_tb = newF.getTraceback().splitlines() ++ self.assertNotEqual(tb, new_tb) ++ self.assertEqual(tb[-3:], new_tb[-3:]) ++ else: ++ self.fail("No exception raised from raiseException!?") ++ ++ @skipIf(raiser is None, "raiser extension not available") ++ def test_failureConstructionWithMungedStackSucceeds(self): ++ """ ++ Pyrex and Cython are known to insert fake stack frames so as to give ++ more Python-like tracebacks. These stack frames with empty code objects ++ should not break extraction of the exception. ++ """ ++ try: ++ raiser.raiseException() ++ except raiser.RaiserException: ++ f = failure.Failure() ++ self.assertTrue(f.check(raiser.RaiserException)) ++ else: ++ self.fail("No exception raised from extension?!") ++ ++ ++# On Python 3.5, extract_tb returns "FrameSummary" objects, which are almost ++# like the old tuples. This being different does not affect the actual tests ++# as we are testing that the input works, and that extract_tb returns something ++# reasonable. ++def _tb(fn, lineno, name, text): ++ return FrameSummary(fn, lineno, name) ++ ++ ++class FormattableTracebackTests(SynchronousTestCase): ++ """ ++ Whitebox tests that show that L{failure._Traceback} constructs objects that ++ can be used by L{traceback.extract_tb}. ++ ++ If the objects can be used by L{traceback.extract_tb}, then they can be ++ formatted using L{traceback.format_tb} and friends. ++ """ ++ ++ def test_singleFrame(self): ++ """ ++ A C{_Traceback} object constructed with a single frame should be able ++ to be passed to L{traceback.extract_tb}, and we should get a singleton ++ list containing a (filename, lineno, methodname, line) tuple. ++ """ ++ tb = failure._Traceback([], [["method", "filename.py", 123, {}, {}]]) ++ # Note that we don't need to test that extract_tb correctly extracts ++ # the line's contents. In this case, since filename.py doesn't exist, ++ # it will just use None. ++ self.assertEqual( ++ traceback.extract_tb(tb), [_tb("filename.py", 123, "method", None)] ++ ) ++ ++ def test_manyFrames(self): ++ """ ++ A C{_Traceback} object constructed with multiple frames should be able ++ to be passed to L{traceback.extract_tb}, and we should get a list ++ containing a tuple for each frame. ++ """ ++ tb = failure._Traceback( ++ [ ++ ["caller1", "filename.py", 7, {}, {}], ++ ["caller2", "filename.py", 8, {}, {}], ++ ], ++ [ ++ ["method1", "filename.py", 123, {}, {}], ++ ["method2", "filename.py", 235, {}, {}], ++ ], ++ ) ++ self.assertEqual( ++ traceback.extract_tb(tb), ++ [ ++ _tb("filename.py", 123, "method1", None), ++ _tb("filename.py", 235, "method2", None), ++ ], ++ ) ++ ++ # We should also be able to extract_stack on it ++ self.assertEqual( ++ traceback.extract_stack(tb.tb_frame), ++ [ ++ _tb("filename.py", 7, "caller1", None), ++ _tb("filename.py", 8, "caller2", None), ++ _tb("filename.py", 123, "method1", None), ++ ], ++ ) ++ ++ ++class FrameAttributesTests(SynchronousTestCase): ++ """ ++ _Frame objects should possess some basic attributes that qualify them as ++ fake python Frame objects. ++ """ ++ ++ def test_fakeFrameAttributes(self): ++ """ ++ L{_Frame} instances have the C{f_globals} and C{f_locals} attributes ++ bound to C{dict} instance. They also have the C{f_code} attribute ++ bound to something like a code object. ++ """ ++ frame = failure._Frame(("dummyname", "dummyfilename", None, None, None), None) ++ self.assertIsInstance(frame.f_globals, dict) ++ self.assertIsInstance(frame.f_locals, dict) ++ self.assertIsInstance(frame.f_code, failure._Code) ++ ++ ++class DebugModeTests(SynchronousTestCase): ++ """ ++ Failure's debug mode should allow jumping into the debugger. ++ """ ++ ++ def setUp(self): ++ """ ++ Override pdb.post_mortem so we can make sure it's called. ++ """ ++ # Make sure any changes we make are reversed: ++ post_mortem = pdb.post_mortem ++ origInit = failure.Failure.__init__ ++ ++ def restore(): ++ pdb.post_mortem = post_mortem ++ failure.Failure.__init__ = origInit ++ ++ self.addCleanup(restore) ++ ++ self.result = [] ++ pdb.post_mortem = self.result.append ++ failure.startDebugMode() ++ ++ def test_regularFailure(self): ++ """ ++ If startDebugMode() is called, calling Failure() will first call ++ pdb.post_mortem with the traceback. ++ """ ++ try: ++ 1 / 0 ++ except BaseException: ++ typ, exc, tb = sys.exc_info() ++ f = failure.Failure() ++ self.assertEqual(self.result, [tb]) ++ self.assertFalse(f.captureVars) ++ ++ def test_captureVars(self): ++ """ ++ If startDebugMode() is called, passing captureVars to Failure() will ++ not blow up. ++ """ ++ try: ++ 1 / 0 ++ except BaseException: ++ typ, exc, tb = sys.exc_info() ++ f = failure.Failure(captureVars=True) ++ self.assertEqual(self.result, [tb]) ++ self.assertTrue(f.captureVars) ++ ++ ++class ExtendedGeneratorTests(SynchronousTestCase): ++ """ ++ Tests C{failure.Failure} support for generator features added in Python 2.5 ++ """ ++ ++ def _throwIntoGenerator(self, f, g): ++ try: ++ f.throwExceptionIntoGenerator(g) ++ except StopIteration: ++ pass ++ else: ++ self.fail("throwExceptionIntoGenerator should have raised " "StopIteration") ++ ++ def test_throwExceptionIntoGenerator(self): ++ """ ++ It should be possible to throw the exception that a Failure ++ represents into a generator. ++ """ ++ stuff = [] ++ ++ def generator(): ++ try: ++ yield ++ except BaseException: ++ stuff.append(sys.exc_info()) ++ else: ++ self.fail("Yield should have yielded exception.") ++ ++ g = generator() ++ f = getDivisionFailure() ++ next(g) ++ self._throwIntoGenerator(f, g) ++ ++ self.assertEqual(stuff[0][0], ZeroDivisionError) ++ self.assertIsInstance(stuff[0][1], ZeroDivisionError) ++ ++ self.assertEqual(traceback.extract_tb(stuff[0][2])[-1][-1], "1 / 0") ++ ++ def test_findFailureInGenerator(self): ++ """ ++ Within an exception handler, it should be possible to find the ++ original Failure that caused the current exception (if it was ++ caused by throwExceptionIntoGenerator). ++ """ ++ f = getDivisionFailure() ++ f.cleanFailure() ++ foundFailures = [] ++ ++ def generator(): ++ try: ++ yield ++ except BaseException: ++ foundFailures.append(failure.Failure._findFailure()) ++ else: ++ self.fail("No exception sent to generator") ++ ++ g = generator() ++ next(g) ++ self._throwIntoGenerator(f, g) ++ ++ self.assertEqual(foundFailures, [f]) ++ ++ def test_failureConstructionFindsOriginalFailure(self): ++ """ ++ When a Failure is constructed in the context of an exception ++ handler that is handling an exception raised by ++ throwExceptionIntoGenerator, the new Failure should be chained to that ++ original Failure. ++ """ ++ f = getDivisionFailure() ++ f.cleanFailure() ++ original_failure_str = f.getTraceback() ++ ++ newFailures = [] ++ ++ def generator(): ++ try: ++ yield ++ except BaseException: ++ newFailures.append(failure.Failure()) ++ else: ++ self.fail("No exception sent to generator") ++ ++ g = generator() ++ next(g) ++ self._throwIntoGenerator(f, g) ++ ++ self.assertEqual(len(newFailures), 1) ++ ++ # The original failure should not be changed. ++ self.assertEqual(original_failure_str, f.getTraceback()) ++ ++ # The new failure should be different and contain stack info for ++ # our generator. ++ self.assertNotEqual(newFailures[0].getTraceback(), f.getTraceback()) ++ self.assertIn("generator", newFailures[0].getTraceback()) ++ self.assertNotIn("generator", f.getTraceback()) ++ ++ def test_ambiguousFailureInGenerator(self): ++ """ ++ When a generator reraises a different exception, ++ L{Failure._findFailure} inside the generator should find the reraised ++ exception rather than original one. ++ """ ++ ++ def generator(): ++ try: ++ try: ++ yield ++ except BaseException: ++ [][1] ++ except BaseException: ++ self.assertIsInstance(failure.Failure().value, IndexError) ++ ++ g = generator() ++ next(g) ++ f = getDivisionFailure() ++ self._throwIntoGenerator(f, g) ++ ++ def test_ambiguousFailureFromGenerator(self): ++ """ ++ When a generator reraises a different exception, ++ L{Failure._findFailure} above the generator should find the reraised ++ exception rather than original one. ++ """ ++ ++ def generator(): ++ try: ++ yield ++ except BaseException: ++ [][1] ++ ++ g = generator() ++ next(g) ++ f = getDivisionFailure() ++ try: ++ self._throwIntoGenerator(f, g) ++ except BaseException: ++ self.assertIsInstance(failure.Failure().value, IndexError) +diff --git a/src/twisted/test/test_failure.py b/src/twisted/test/test_failure.py +deleted file mode 100644 +index 46f4b63..0000000 +--- a/src/twisted/test/test_failure.py ++++ /dev/null +@@ -1,953 +0,0 @@ +-# Copyright (c) Twisted Matrix Laboratories. +-# See LICENSE for details. +- +-""" +-Test cases for the L{twisted.python.failure} module. +-""" +- +- +-import linecache +-import pdb +-import re +-import sys +-import traceback +-from io import StringIO +-from traceback import FrameSummary +-from unittest import skipIf +- +-from cython_test_exception_raiser import raiser # type: ignore[import] +- +-from twisted.python import failure, reflect +-from twisted.trial.unittest import SynchronousTestCase +- +- +-def getDivisionFailure(*args, **kwargs): +- """ +- Make a C{Failure} of a divide-by-zero error. +- +- @param args: Any C{*args} are passed to Failure's constructor. +- @param kwargs: Any C{**kwargs} are passed to Failure's constructor. +- """ +- try: +- 1 / 0 +- except BaseException: +- f = failure.Failure(*args, **kwargs) +- return f +- +- +-class FailureTests(SynchronousTestCase): +- """ +- Tests for L{failure.Failure}. +- """ +- +- def test_failAndTrap(self): +- """ +- Trapping a L{Failure}. +- """ +- try: +- raise NotImplementedError("test") +- except BaseException: +- f = failure.Failure() +- error = f.trap(SystemExit, RuntimeError) +- self.assertEqual(error, RuntimeError) +- self.assertEqual(f.type, NotImplementedError) +- +- def test_trapRaisesWrappedException(self): +- """ +- If the wrapped C{Exception} is not a subclass of one of the +- expected types, L{failure.Failure.trap} raises the wrapped +- C{Exception}. +- """ +- exception = ValueError() +- try: +- raise exception +- except BaseException: +- f = failure.Failure() +- +- untrapped = self.assertRaises(ValueError, f.trap, OverflowError) +- self.assertIs(exception, untrapped) +- +- def test_failureValueFromFailure(self): +- """ +- A L{failure.Failure} constructed from another +- L{failure.Failure} instance, has its C{value} property set to +- the value of that L{failure.Failure} instance. +- """ +- exception = ValueError() +- f1 = failure.Failure(exception) +- f2 = failure.Failure(f1) +- self.assertIs(f2.value, exception) +- +- def test_failureValueFromFoundFailure(self): +- """ +- A L{failure.Failure} constructed without a C{exc_value} +- argument, will search for an "original" C{Failure}, and if +- found, its value will be used as the value for the new +- C{Failure}. +- """ +- exception = ValueError() +- f1 = failure.Failure(exception) +- try: +- f1.trap(OverflowError) +- except BaseException: +- f2 = failure.Failure() +- +- self.assertIs(f2.value, exception) +- +- def assertStartsWith(self, s, prefix): +- """ +- Assert that C{s} starts with a particular C{prefix}. +- +- @param s: The input string. +- @type s: C{str} +- @param prefix: The string that C{s} should start with. +- @type prefix: C{str} +- """ +- self.assertTrue(s.startswith(prefix), f"{prefix!r} is not the start of {s!r}") +- +- def assertEndsWith(self, s, suffix): +- """ +- Assert that C{s} end with a particular C{suffix}. +- +- @param s: The input string. +- @type s: C{str} +- @param suffix: The string that C{s} should end with. +- @type suffix: C{str} +- """ +- self.assertTrue(s.endswith(suffix), f"{suffix!r} is not the end of {s!r}") +- +- def assertTracebackFormat(self, tb, prefix, suffix): +- """ +- Assert that the C{tb} traceback contains a particular C{prefix} and +- C{suffix}. +- +- @param tb: The traceback string. +- @type tb: C{str} +- @param prefix: The string that C{tb} should start with. +- @type prefix: C{str} +- @param suffix: The string that C{tb} should end with. +- @type suffix: C{str} +- """ +- self.assertStartsWith(tb, prefix) +- self.assertEndsWith(tb, suffix) +- +- def assertDetailedTraceback(self, captureVars=False, cleanFailure=False): +- """ +- Assert that L{printDetailedTraceback} produces and prints a detailed +- traceback. +- +- The detailed traceback consists of a header:: +- +- *--- Failure #20 --- +- +- The body contains the stacktrace:: +- +- /twisted/trial/_synctest.py:1180: _run(...) +- /twisted/python/util.py:1076: runWithWarningsSuppressed(...) +- --- --- +- /twisted/test/test_failure.py:39: getDivisionFailure(...) +- +- If C{captureVars} is enabled the body also includes a list of +- globals and locals:: +- +- [ Locals ] +- exampleLocalVar : 'xyz' +- ... +- ( Globals ) +- ... +- +- Or when C{captureVars} is disabled:: +- +- [Capture of Locals and Globals disabled (use captureVars=True)] +- +- When C{cleanFailure} is enabled references to other objects are removed +- and replaced with strings. +- +- And finally the footer with the L{Failure}'s value:: +- +- exceptions.ZeroDivisionError: float division +- *--- End of Failure #20 --- +- +- @param captureVars: Enables L{Failure.captureVars}. +- @type captureVars: C{bool} +- @param cleanFailure: Enables L{Failure.cleanFailure}. +- @type cleanFailure: C{bool} +- """ +- if captureVars: +- exampleLocalVar = "xyz" +- # Silence the linter as this variable is checked via +- # the traceback. +- exampleLocalVar +- +- f = getDivisionFailure(captureVars=captureVars) +- out = StringIO() +- if cleanFailure: +- f.cleanFailure() +- f.printDetailedTraceback(out) +- +- tb = out.getvalue() +- start = "*--- Failure #%d%s---\n" % ( +- f.count, +- (f.pickled and " (pickled) ") or " ", +- ) +- end = "{}: {}\n*--- End of Failure #{} ---\n".format( +- reflect.qual(f.type), +- reflect.safe_str(f.value), +- f.count, +- ) +- self.assertTracebackFormat(tb, start, end) +- +- # Variables are printed on lines with 2 leading spaces. +- linesWithVars = [line for line in tb.splitlines() if line.startswith(" ")] +- +- if captureVars: +- self.assertNotEqual([], linesWithVars) +- if cleanFailure: +- line = " exampleLocalVar : \"'xyz'\"" +- else: +- line = " exampleLocalVar : 'xyz'" +- self.assertIn(line, linesWithVars) +- else: +- self.assertEqual([], linesWithVars) +- self.assertIn( +- " [Capture of Locals and Globals disabled (use " "captureVars=True)]\n", +- tb, +- ) +- +- def assertBriefTraceback(self, captureVars=False): +- """ +- Assert that L{printBriefTraceback} produces and prints a brief +- traceback. +- +- The brief traceback consists of a header:: +- +- Traceback: : float division +- +- The body with the stacktrace:: +- +- /twisted/trial/_synctest.py:1180:_run +- /twisted/python/util.py:1076:runWithWarningsSuppressed +- +- And the footer:: +- +- --- --- +- /twisted/test/test_failure.py:39:getDivisionFailure +- +- @param captureVars: Enables L{Failure.captureVars}. +- @type captureVars: C{bool} +- """ +- if captureVars: +- exampleLocalVar = "abcde" +- # Silence the linter as this variable is checked via +- # the traceback. +- exampleLocalVar +- +- f = getDivisionFailure() +- out = StringIO() +- f.printBriefTraceback(out) +- tb = out.getvalue() +- stack = "" +- for method, filename, lineno, localVars, globalVars in f.frames: +- stack += f"{filename}:{lineno}:{method}\n" +- +- zde = repr(ZeroDivisionError) +- self.assertTracebackFormat( +- tb, +- f"Traceback: {zde}: ", +- f"{failure.EXCEPTION_CAUGHT_HERE}\n{stack}", +- ) +- +- if captureVars: +- self.assertIsNone(re.search("exampleLocalVar.*abcde", tb)) +- +- def assertDefaultTraceback(self, captureVars=False): +- """ +- Assert that L{printTraceback} produces and prints a default traceback. +- +- The default traceback consists of a header:: +- +- Traceback (most recent call last): +- +- The body with traceback:: +- +- File "/twisted/trial/_synctest.py", line 1180, in _run +- runWithWarningsSuppressed(suppress, method) +- +- And the footer:: +- +- --- --- +- File "twisted/test/test_failure.py", line 39, in getDivisionFailure +- 1 / 0 +- exceptions.ZeroDivisionError: float division +- +- @param captureVars: Enables L{Failure.captureVars}. +- @type captureVars: C{bool} +- """ +- if captureVars: +- exampleLocalVar = "xyzzy" +- # Silence the linter as this variable is checked via +- # the traceback. +- exampleLocalVar +- +- f = getDivisionFailure(captureVars=captureVars) +- out = StringIO() +- f.printTraceback(out) +- tb = out.getvalue() +- stack = "" +- for method, filename, lineno, localVars, globalVars in f.frames: +- stack += f' File "{filename}", line {lineno}, in {method}\n' +- stack += f" {linecache.getline(filename, lineno).strip()}\n" +- +- self.assertTracebackFormat( +- tb, +- "Traceback (most recent call last):", +- "%s\n%s%s: %s\n" +- % ( +- failure.EXCEPTION_CAUGHT_HERE, +- stack, +- reflect.qual(f.type), +- reflect.safe_str(f.value), +- ), +- ) +- +- if captureVars: +- self.assertIsNone(re.search("exampleLocalVar.*xyzzy", tb)) +- +- def test_printDetailedTraceback(self): +- """ +- L{printDetailedTraceback} returns a detailed traceback including the +- L{Failure}'s count. +- """ +- self.assertDetailedTraceback() +- +- def test_printBriefTraceback(self): +- """ +- L{printBriefTraceback} returns a brief traceback. +- """ +- self.assertBriefTraceback() +- +- def test_printTraceback(self): +- """ +- L{printTraceback} returns a traceback. +- """ +- self.assertDefaultTraceback() +- +- def test_printDetailedTracebackCapturedVars(self): +- """ +- L{printDetailedTraceback} captures the locals and globals for its +- stack frames and adds them to the traceback, when called on a +- L{Failure} constructed with C{captureVars=True}. +- """ +- self.assertDetailedTraceback(captureVars=True) +- +- def test_printBriefTracebackCapturedVars(self): +- """ +- L{printBriefTraceback} returns a brief traceback when called on a +- L{Failure} constructed with C{captureVars=True}. +- +- Local variables on the stack can not be seen in the resulting +- traceback. +- """ +- self.assertBriefTraceback(captureVars=True) +- +- def test_printTracebackCapturedVars(self): +- """ +- L{printTraceback} returns a traceback when called on a L{Failure} +- constructed with C{captureVars=True}. +- +- Local variables on the stack can not be seen in the resulting +- traceback. +- """ +- self.assertDefaultTraceback(captureVars=True) +- +- def test_printDetailedTracebackCapturedVarsCleaned(self): +- """ +- C{printDetailedTraceback} includes information about local variables on +- the stack after C{cleanFailure} has been called. +- """ +- self.assertDetailedTraceback(captureVars=True, cleanFailure=True) +- +- def test_invalidFormatFramesDetail(self): +- """ +- L{failure.format_frames} raises a L{ValueError} if the supplied +- C{detail} level is unknown. +- """ +- self.assertRaises( +- ValueError, failure.format_frames, None, None, detail="noisia" +- ) +- +- def test_ExplictPass(self): +- e = RuntimeError() +- f = failure.Failure(e) +- f.trap(RuntimeError) +- self.assertEqual(f.value, e) +- +- def _getInnermostFrameLine(self, f): +- try: +- f.raiseException() +- except ZeroDivisionError: +- tb = traceback.extract_tb(sys.exc_info()[2]) +- return tb[-1][-1] +- else: +- raise Exception("f.raiseException() didn't raise ZeroDivisionError!?") +- +- def test_RaiseExceptionWithTB(self): +- f = getDivisionFailure() +- innerline = self._getInnermostFrameLine(f) +- self.assertEqual(innerline, "1 / 0") +- +- def test_stringExceptionConstruction(self): +- """ +- Constructing a C{Failure} with a string as its exception value raises +- a C{TypeError}, as this is no longer supported as of Python 2.6. +- """ +- exc = self.assertRaises(TypeError, failure.Failure, "ono!") +- self.assertIn("Strings are not supported by Failure", str(exc)) +- +- def test_ConstructionFails(self): +- """ +- Creating a Failure with no arguments causes it to try to discover the +- current interpreter exception state. If no such state exists, creating +- the Failure should raise a synchronous exception. +- """ +- self.assertRaises(failure.NoCurrentExceptionError, failure.Failure) +- +- def test_getTracebackObject(self): +- """ +- If the C{Failure} has not been cleaned, then C{getTracebackObject} +- returns the traceback object that captured in its constructor. +- """ +- f = getDivisionFailure() +- self.assertEqual(f.getTracebackObject(), f.tb) +- +- def test_getTracebackObjectFromCaptureVars(self): +- """ +- C{captureVars=True} has no effect on the result of +- C{getTracebackObject}. +- """ +- try: +- 1 / 0 +- except ZeroDivisionError: +- noVarsFailure = failure.Failure() +- varsFailure = failure.Failure(captureVars=True) +- self.assertEqual(noVarsFailure.getTracebackObject(), varsFailure.tb) +- +- def test_getTracebackObjectFromClean(self): +- """ +- If the Failure has been cleaned, then C{getTracebackObject} returns an +- object that looks the same to L{traceback.extract_tb}. +- """ +- f = getDivisionFailure() +- expected = traceback.extract_tb(f.getTracebackObject()) +- f.cleanFailure() +- observed = traceback.extract_tb(f.getTracebackObject()) +- self.assertIsNotNone(expected) +- self.assertEqual(expected, observed) +- +- def test_getTracebackObjectFromCaptureVarsAndClean(self): +- """ +- If the Failure was created with captureVars, then C{getTracebackObject} +- returns an object that looks the same to L{traceback.extract_tb}. +- """ +- f = getDivisionFailure(captureVars=True) +- expected = traceback.extract_tb(f.getTracebackObject()) +- f.cleanFailure() +- observed = traceback.extract_tb(f.getTracebackObject()) +- self.assertEqual(expected, observed) +- +- def test_getTracebackObjectWithoutTraceback(self): +- """ +- L{failure.Failure}s need not be constructed with traceback objects. If +- a C{Failure} has no traceback information at all, C{getTracebackObject} +- just returns None. +- +- None is a good value, because traceback.extract_tb(None) -> []. +- """ +- f = failure.Failure(Exception("some error")) +- self.assertIsNone(f.getTracebackObject()) +- +- def test_tracebackFromExceptionInPython3(self): +- """ +- If a L{failure.Failure} is constructed with an exception but no +- traceback in Python 3, the traceback will be extracted from the +- exception's C{__traceback__} attribute. +- """ +- try: +- 1 / 0 +- except BaseException: +- klass, exception, tb = sys.exc_info() +- f = failure.Failure(exception) +- self.assertIs(f.tb, tb) +- +- def test_cleanFailureRemovesTracebackInPython3(self): +- """ +- L{failure.Failure.cleanFailure} sets the C{__traceback__} attribute of +- the exception to L{None} in Python 3. +- """ +- f = getDivisionFailure() +- self.assertIsNotNone(f.tb) +- self.assertIs(f.value.__traceback__, f.tb) +- f.cleanFailure() +- self.assertIsNone(f.value.__traceback__) +- +- def test_repr(self): +- """ +- The C{repr} of a L{failure.Failure} shows the type and string +- representation of the underlying exception. +- """ +- f = getDivisionFailure() +- typeName = reflect.fullyQualifiedName(ZeroDivisionError) +- self.assertEqual( +- repr(f), +- "" % (typeName,), +- ) +- +- +-class BrokenStr(Exception): +- """ +- An exception class the instances of which cannot be presented as strings +- via L{str}. +- """ +- +- def __str__(self) -> str: +- # Could raise something else, but there's no point as yet. +- raise self +- +- +-class BrokenExceptionMetaclass(type): +- """ +- A metaclass for an exception type which cannot be presented as a string via +- L{str}. +- """ +- +- def __str__(self) -> str: +- raise ValueError("You cannot make a string out of me.") +- +- +-class BrokenExceptionType(Exception, metaclass=BrokenExceptionMetaclass): +- +- """ +- The aforementioned exception type which cannot be presented as a string via +- L{str}. +- """ +- +- +-class GetTracebackTests(SynchronousTestCase): +- """ +- Tests for L{Failure.getTraceback}. +- """ +- +- def _brokenValueTest(self, detail): +- """ +- Construct a L{Failure} with an exception that raises an exception from +- its C{__str__} method and then call C{getTraceback} with the specified +- detail and verify that it returns a string. +- """ +- x = BrokenStr() +- f = failure.Failure(x) +- traceback = f.getTraceback(detail=detail) +- self.assertIsInstance(traceback, str) +- +- def test_brokenValueBriefDetail(self): +- """ +- A L{Failure} might wrap an exception with a C{__str__} method which +- raises an exception. In this case, calling C{getTraceback} on the +- failure with the C{"brief"} detail does not raise an exception. +- """ +- self._brokenValueTest("brief") +- +- def test_brokenValueDefaultDetail(self): +- """ +- Like test_brokenValueBriefDetail, but for the C{"default"} detail case. +- """ +- self._brokenValueTest("default") +- +- def test_brokenValueVerboseDetail(self): +- """ +- Like test_brokenValueBriefDetail, but for the C{"default"} detail case. +- """ +- self._brokenValueTest("verbose") +- +- def _brokenTypeTest(self, detail): +- """ +- Construct a L{Failure} with an exception type that raises an exception +- from its C{__str__} method and then call C{getTraceback} with the +- specified detail and verify that it returns a string. +- """ +- f = failure.Failure(BrokenExceptionType()) +- traceback = f.getTraceback(detail=detail) +- self.assertIsInstance(traceback, str) +- +- def test_brokenTypeBriefDetail(self): +- """ +- A L{Failure} might wrap an exception the type object of which has a +- C{__str__} method which raises an exception. In this case, calling +- C{getTraceback} on the failure with the C{"brief"} detail does not raise +- an exception. +- """ +- self._brokenTypeTest("brief") +- +- def test_brokenTypeDefaultDetail(self): +- """ +- Like test_brokenTypeBriefDetail, but for the C{"default"} detail case. +- """ +- self._brokenTypeTest("default") +- +- def test_brokenTypeVerboseDetail(self): +- """ +- Like test_brokenTypeBriefDetail, but for the C{"verbose"} detail case. +- """ +- self._brokenTypeTest("verbose") +- +- +-class FindFailureTests(SynchronousTestCase): +- """ +- Tests for functionality related to L{Failure._findFailure}. +- """ +- +- def test_findNoFailureInExceptionHandler(self): +- """ +- Within an exception handler, _findFailure should return +- L{None} in case no Failure is associated with the current +- exception. +- """ +- try: +- 1 / 0 +- except BaseException: +- self.assertIsNone(failure.Failure._findFailure()) +- else: +- self.fail("No exception raised from 1/0!?") +- +- def test_findNoFailure(self): +- """ +- Outside of an exception handler, _findFailure should return None. +- """ +- self.assertIsNone(sys.exc_info()[-1]) # environment sanity check +- self.assertIsNone(failure.Failure._findFailure()) +- +- def test_findFailure(self): +- """ +- Within an exception handler, it should be possible to find the +- original Failure that caused the current exception (if it was +- caused by raiseException). +- """ +- f = getDivisionFailure() +- f.cleanFailure() +- try: +- f.raiseException() +- except BaseException: +- self.assertEqual(failure.Failure._findFailure(), f) +- else: +- self.fail("No exception raised from raiseException!?") +- +- def test_failureConstructionFindsOriginalFailure(self): +- """ +- When a Failure is constructed in the context of an exception +- handler that is handling an exception raised by +- raiseException, the new Failure should be chained to that +- original Failure. +- Means the new failure should still show the same origin frame, +- but with different complete stack trace (as not thrown at same place). +- """ +- f = getDivisionFailure() +- f.cleanFailure() +- try: +- f.raiseException() +- except BaseException: +- newF = failure.Failure() +- tb = f.getTraceback().splitlines() +- new_tb = newF.getTraceback().splitlines() +- self.assertNotEqual(tb, new_tb) +- self.assertEqual(tb[-3:], new_tb[-3:]) +- else: +- self.fail("No exception raised from raiseException!?") +- +- @skipIf(raiser is None, "raiser extension not available") +- def test_failureConstructionWithMungedStackSucceeds(self): +- """ +- Pyrex and Cython are known to insert fake stack frames so as to give +- more Python-like tracebacks. These stack frames with empty code objects +- should not break extraction of the exception. +- """ +- try: +- raiser.raiseException() +- except raiser.RaiserException: +- f = failure.Failure() +- self.assertTrue(f.check(raiser.RaiserException)) +- else: +- self.fail("No exception raised from extension?!") +- +- +-# On Python 3.5, extract_tb returns "FrameSummary" objects, which are almost +-# like the old tuples. This being different does not affect the actual tests +-# as we are testing that the input works, and that extract_tb returns something +-# reasonable. +-def _tb(fn, lineno, name, text): +- return FrameSummary(fn, lineno, name) +- +- +-class FormattableTracebackTests(SynchronousTestCase): +- """ +- Whitebox tests that show that L{failure._Traceback} constructs objects that +- can be used by L{traceback.extract_tb}. +- +- If the objects can be used by L{traceback.extract_tb}, then they can be +- formatted using L{traceback.format_tb} and friends. +- """ +- +- def test_singleFrame(self): +- """ +- A C{_Traceback} object constructed with a single frame should be able +- to be passed to L{traceback.extract_tb}, and we should get a singleton +- list containing a (filename, lineno, methodname, line) tuple. +- """ +- tb = failure._Traceback([], [["method", "filename.py", 123, {}, {}]]) +- # Note that we don't need to test that extract_tb correctly extracts +- # the line's contents. In this case, since filename.py doesn't exist, +- # it will just use None. +- self.assertEqual( +- traceback.extract_tb(tb), [_tb("filename.py", 123, "method", None)] +- ) +- +- def test_manyFrames(self): +- """ +- A C{_Traceback} object constructed with multiple frames should be able +- to be passed to L{traceback.extract_tb}, and we should get a list +- containing a tuple for each frame. +- """ +- tb = failure._Traceback( +- [ +- ["caller1", "filename.py", 7, {}, {}], +- ["caller2", "filename.py", 8, {}, {}], +- ], +- [ +- ["method1", "filename.py", 123, {}, {}], +- ["method2", "filename.py", 235, {}, {}], +- ], +- ) +- self.assertEqual( +- traceback.extract_tb(tb), +- [ +- _tb("filename.py", 123, "method1", None), +- _tb("filename.py", 235, "method2", None), +- ], +- ) +- +- # We should also be able to extract_stack on it +- self.assertEqual( +- traceback.extract_stack(tb.tb_frame), +- [ +- _tb("filename.py", 7, "caller1", None), +- _tb("filename.py", 8, "caller2", None), +- _tb("filename.py", 123, "method1", None), +- ], +- ) +- +- +-class FrameAttributesTests(SynchronousTestCase): +- """ +- _Frame objects should possess some basic attributes that qualify them as +- fake python Frame objects. +- """ +- +- def test_fakeFrameAttributes(self): +- """ +- L{_Frame} instances have the C{f_globals} and C{f_locals} attributes +- bound to C{dict} instance. They also have the C{f_code} attribute +- bound to something like a code object. +- """ +- frame = failure._Frame(("dummyname", "dummyfilename", None, None, None), None) +- self.assertIsInstance(frame.f_globals, dict) +- self.assertIsInstance(frame.f_locals, dict) +- self.assertIsInstance(frame.f_code, failure._Code) +- +- +-class DebugModeTests(SynchronousTestCase): +- """ +- Failure's debug mode should allow jumping into the debugger. +- """ +- +- def setUp(self): +- """ +- Override pdb.post_mortem so we can make sure it's called. +- """ +- # Make sure any changes we make are reversed: +- post_mortem = pdb.post_mortem +- origInit = failure.Failure.__init__ +- +- def restore(): +- pdb.post_mortem = post_mortem +- failure.Failure.__init__ = origInit +- +- self.addCleanup(restore) +- +- self.result = [] +- pdb.post_mortem = self.result.append +- failure.startDebugMode() +- +- def test_regularFailure(self): +- """ +- If startDebugMode() is called, calling Failure() will first call +- pdb.post_mortem with the traceback. +- """ +- try: +- 1 / 0 +- except BaseException: +- typ, exc, tb = sys.exc_info() +- f = failure.Failure() +- self.assertEqual(self.result, [tb]) +- self.assertFalse(f.captureVars) +- +- def test_captureVars(self): +- """ +- If startDebugMode() is called, passing captureVars to Failure() will +- not blow up. +- """ +- try: +- 1 / 0 +- except BaseException: +- typ, exc, tb = sys.exc_info() +- f = failure.Failure(captureVars=True) +- self.assertEqual(self.result, [tb]) +- self.assertTrue(f.captureVars) +- +- +-class ExtendedGeneratorTests(SynchronousTestCase): +- """ +- Tests C{failure.Failure} support for generator features added in Python 2.5 +- """ +- +- def _throwIntoGenerator(self, f, g): +- try: +- f.throwExceptionIntoGenerator(g) +- except StopIteration: +- pass +- else: +- self.fail("throwExceptionIntoGenerator should have raised " "StopIteration") +- +- def test_throwExceptionIntoGenerator(self): +- """ +- It should be possible to throw the exception that a Failure +- represents into a generator. +- """ +- stuff = [] +- +- def generator(): +- try: +- yield +- except BaseException: +- stuff.append(sys.exc_info()) +- else: +- self.fail("Yield should have yielded exception.") +- +- g = generator() +- f = getDivisionFailure() +- next(g) +- self._throwIntoGenerator(f, g) +- +- self.assertEqual(stuff[0][0], ZeroDivisionError) +- self.assertIsInstance(stuff[0][1], ZeroDivisionError) +- +- self.assertEqual(traceback.extract_tb(stuff[0][2])[-1][-1], "1 / 0") +- +- def test_findFailureInGenerator(self): +- """ +- Within an exception handler, it should be possible to find the +- original Failure that caused the current exception (if it was +- caused by throwExceptionIntoGenerator). +- """ +- f = getDivisionFailure() +- f.cleanFailure() +- foundFailures = [] +- +- def generator(): +- try: +- yield +- except BaseException: +- foundFailures.append(failure.Failure._findFailure()) +- else: +- self.fail("No exception sent to generator") +- +- g = generator() +- next(g) +- self._throwIntoGenerator(f, g) +- +- self.assertEqual(foundFailures, [f]) +- +- def test_failureConstructionFindsOriginalFailure(self): +- """ +- When a Failure is constructed in the context of an exception +- handler that is handling an exception raised by +- throwExceptionIntoGenerator, the new Failure should be chained to that +- original Failure. +- """ +- f = getDivisionFailure() +- f.cleanFailure() +- original_failure_str = f.getTraceback() +- +- newFailures = [] +- +- def generator(): +- try: +- yield +- except BaseException: +- newFailures.append(failure.Failure()) +- else: +- self.fail("No exception sent to generator") +- +- g = generator() +- next(g) +- self._throwIntoGenerator(f, g) +- +- self.assertEqual(len(newFailures), 1) +- +- # The original failure should not be changed. +- self.assertEqual(original_failure_str, f.getTraceback()) +- +- # The new failure should be different and contain stack info for +- # our generator. +- self.assertNotEqual(newFailures[0].getTraceback(), f.getTraceback()) +- self.assertIn("generator", newFailures[0].getTraceback()) +- self.assertNotIn("generator", f.getTraceback()) +- +- def test_ambiguousFailureInGenerator(self): +- """ +- When a generator reraises a different exception, +- L{Failure._findFailure} inside the generator should find the reraised +- exception rather than original one. +- """ +- +- def generator(): +- try: +- try: +- yield +- except BaseException: +- [][1] +- except BaseException: +- self.assertIsInstance(failure.Failure().value, IndexError) +- +- g = generator() +- next(g) +- f = getDivisionFailure() +- self._throwIntoGenerator(f, g) +- +- def test_ambiguousFailureFromGenerator(self): +- """ +- When a generator reraises a different exception, +- L{Failure._findFailure} above the generator should find the reraised +- exception rather than original one. +- """ +- +- def generator(): +- try: +- yield +- except BaseException: +- [][1] +- +- g = generator() +- next(g) +- f = getDivisionFailure() +- try: +- self._throwIntoGenerator(f, g) +- except BaseException: +- self.assertIsInstance(failure.Failure().value, IndexError) diff -Nru twisted-20.3.0/debian/patches/tests/Testing-Ignore-test-around-git-tooling.patch twisted-22.1.0/debian/patches/tests/Testing-Ignore-test-around-git-tooling.patch --- twisted-20.3.0/debian/patches/tests/Testing-Ignore-test-around-git-tooling.patch 1970-01-01 00:00:00.000000000 +0000 +++ twisted-22.1.0/debian/patches/tests/Testing-Ignore-test-around-git-tooling.patch 2022-02-17 10:40:49.000000000 +0000 @@ -0,0 +1,51 @@ +From: Carsten Schoenert +Date: Mon, 17 Jan 2022 07:30:38 +0100 +Subject: Testing: Ignore test around git tooling + +We build based on extracted data from an tarball, therefore the git +checking functions can't work and we place simply a static link to the +upstream data. +--- + src/twisted/python/test/test_release.py | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/src/twisted/python/test/test_release.py b/src/twisted/python/test/test_release.py +index 30ce8d9..1487d93 100644 +--- a/src/twisted/python/test/test_release.py ++++ b/src/twisted/python/test/test_release.py +@@ -55,6 +55,8 @@ PRECOMMIT_CI_ENVIRON = {"GITHUB_HEAD_REF": "pre-commit-ci-update-config"} + GENERIC_CI_ENVIRON = {"GITHUB_HEAD_REF": "1234-some-branch-name"} + + ++skip = "Not relevant within Debian." ++ + class ExternalTempdirTestCase(TestCase): + """ + A test case which has mkdir make directories outside of the usual spot, so +@@ -797,6 +799,8 @@ class CommandsTestMixin(StructureAssertingMixin): + Test mixin for the VCS commands used by the release scripts. + """ + ++ skip = "Not relevant within Debian." ++ + def setUp(self): + self.tmpDir = FilePath(self.mktemp()) + +@@ -882,6 +886,8 @@ class GitCommandTest(CommandsTestMixin, ExternalTempdirTestCase): + L{GitCommand}. + """ + ++ skip = "Not relevant within Debian." ++ + createCommand = GitCommand + + def makeRepository(self, root): +@@ -952,6 +958,8 @@ class CheckNewsfragmentScriptTests(ExternalTempdirTestCase): + L{CheckNewsfragmentScript}. + """ + ++ skip = "Not relevant within Debian." ++ + def setUp(self): + self.origin = FilePath(self.mktemp()) + _gitInit(self.origin) diff -Nru twisted-20.3.0/debian/patches/tests/Tests-Drop-test_givesMeaningfulErrorMessageIfNoCipherMatc.patch twisted-22.1.0/debian/patches/tests/Tests-Drop-test_givesMeaningfulErrorMessageIfNoCipherMatc.patch --- twisted-20.3.0/debian/patches/tests/Tests-Drop-test_givesMeaningfulErrorMessageIfNoCipherMatc.patch 1970-01-01 00:00:00.000000000 +0000 +++ twisted-22.1.0/debian/patches/tests/Tests-Drop-test_givesMeaningfulErrorMessageIfNoCipherMatc.patch 2022-02-17 10:40:49.000000000 +0000 @@ -0,0 +1,36 @@ +From: Balint Reczey +Date: Wed, 28 Nov 2018 22:09:42 +0100 +Subject: Tests: Drop test_givesMeaningfulErrorMessageIfNoCipherMatches + +With OpenSSL 1.1.1 no ValueError is raised. +Forwarded: not-needed +--- + src/twisted/test/test_sslverify.py | 15 --------------- + 1 file changed, 15 deletions(-) + +diff --git a/src/twisted/test/test_sslverify.py b/src/twisted/test/test_sslverify.py +index e9725ab..f62c29b 100644 +--- a/src/twisted/test/test_sslverify.py ++++ b/src/twisted/test/test_sslverify.py +@@ -896,21 +896,6 @@ class OpenSSLOptionsTests(OpenSSLOptionsTestsMixin, TestCase): + ctx = opts.getContext() + self.assertEqual(opts._cipherString.encode("ascii"), ctx._cipherList) + +- def test_givesMeaningfulErrorMessageIfNoCipherMatches(self): +- """ +- If there is no valid cipher that matches the user's wishes, +- a L{ValueError} is raised. +- """ +- self.assertRaises( +- ValueError, +- sslverify.OpenSSLCertificateOptions, +- privateKey=self.sKey, +- certificate=self.sCert, +- acceptableCiphers=sslverify.OpenSSLAcceptableCiphers.fromOpenSSLCipherString( +- "" +- ), +- ) +- + def test_honorsAcceptableCiphersArgument(self): + """ + If acceptable ciphers are passed, they are used. diff -Nru twisted-20.3.0/debian/patches/tests/Tests-Fix-ckeygen-test-writing-to-stderr.patch twisted-22.1.0/debian/patches/tests/Tests-Fix-ckeygen-test-writing-to-stderr.patch --- twisted-20.3.0/debian/patches/tests/Tests-Fix-ckeygen-test-writing-to-stderr.patch 1970-01-01 00:00:00.000000000 +0000 +++ twisted-22.1.0/debian/patches/tests/Tests-Fix-ckeygen-test-writing-to-stderr.patch 2022-02-17 10:40:49.000000000 +0000 @@ -0,0 +1,36 @@ +From: Free Ekanayaka +Date: Sat, 26 Nov 2016 11:37:52 +0000 +Subject: Tests: Fix ckeygen test writing to stderr + +Improve test process which writes on stderr which will fail within the +Debian autopkgtest environment otherwise. + +Forwarded: https://twistedmatrix.com/trac/ticket/8924 +--- + src/twisted/conch/test/test_ckeygen.py | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/src/twisted/conch/test/test_ckeygen.py b/src/twisted/conch/test/test_ckeygen.py +index 8b0ed86..584f59e 100644 +--- a/src/twisted/conch/test/test_ckeygen.py ++++ b/src/twisted/conch/test/test_ckeygen.py +@@ -8,6 +8,7 @@ Tests for L{twisted.conch.scripts.ckeygen}. + import getpass + import subprocess + import sys ++import os + from io import StringIO + + from twisted.conch.test.keydata import ( +@@ -105,7 +106,10 @@ class KeyGenTests(TestCase): + def test_runBadKeytype(self): + filename = self.mktemp() + with self.assertRaises(subprocess.CalledProcessError): +- subprocess.check_call(["ckeygen", "-t", "foo", "-f", filename]) ++ with open(os.devnull, "rb") as devnull: ++ subprocess.check_call( ++ ['ckeygen', '-t', 'foo', '-f', filename], ++ stderr=devnull) + + def test_enumrepresentation(self): + """ diff -Nru twisted-20.3.0/debian/patches/tests/Tests-Handle-setlocale-more-tolerant.patch twisted-22.1.0/debian/patches/tests/Tests-Handle-setlocale-more-tolerant.patch --- twisted-20.3.0/debian/patches/tests/Tests-Handle-setlocale-more-tolerant.patch 1970-01-01 00:00:00.000000000 +0000 +++ twisted-22.1.0/debian/patches/tests/Tests-Handle-setlocale-more-tolerant.patch 2022-02-17 10:40:49.000000000 +0000 @@ -0,0 +1,27 @@ +From: Free Ekanayaka +Date: Sat, 18 Feb 2017 13:50:23 +0000 +Subject: Tests: Handle setlocale more tolerant + +Gracefully handle setlocale failures during the test suite (e.g when +running in a container). +--- + src/twisted/conch/test/test_cftp.py | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/src/twisted/conch/test/test_cftp.py b/src/twisted/conch/test/test_cftp.py +index be855df..8fc3b33 100644 +--- a/src/twisted/conch/test/test_cftp.py ++++ b/src/twisted/conch/test/test_cftp.py +@@ -185,9 +185,9 @@ class ListingTests(TestCase): + except locale.Error: + localeSkip = True + else: +- localeSkip = False +- finally: +- locale.setlocale(locale.LC_ALL, currentLocale) ++ locale.setlocale(locale.LC_ALL, currentLocale) ++ except: ++ pass + + @skipIf(localeSkip, "The es_AR.UTF8 locale is not installed.") + def test_localeIndependent(self): diff -Nru twisted-20.3.0/debian/patches/tests/Tests-Ignore-test_listingModulesAlreadyImport.patch twisted-22.1.0/debian/patches/tests/Tests-Ignore-test_listingModulesAlreadyImport.patch --- twisted-20.3.0/debian/patches/tests/Tests-Ignore-test_listingModulesAlreadyImport.patch 1970-01-01 00:00:00.000000000 +0000 +++ twisted-22.1.0/debian/patches/tests/Tests-Ignore-test_listingModulesAlreadyImport.patch 2022-02-17 10:40:49.000000000 +0000 @@ -0,0 +1,23 @@ +From: Carsten Schoenert +Date: Sat, 22 Jan 2022 19:45:03 +0100 +Subject: Tests: Ignore test_listingModulesAlreadyImport + +Don't proceed this test for now, it fails with an error message we can +ignore. +--- + src/twisted/test/test_modules.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/twisted/test/test_modules.py b/src/twisted/test/test_modules.py +index 77a7552..ed981be 100644 +--- a/src/twisted/test/test_modules.py ++++ b/src/twisted/test/test_modules.py +@@ -357,7 +357,7 @@ class PathModificationTests(TwistedModulesTestCase): + self._setupSysPath() + self._listModules() + +- def test_listingModulesAlreadyImported(self): ++ def _test_listingModulesAlreadyImported(self): + """ + Make sure the module list comes back as we expect from iterModules on a + package, whether zipped or not, even if the package has already been diff -Nru twisted-20.3.0/debian/patches/tests/Tests-Ignore-tests-with-some-version-checking.patch twisted-22.1.0/debian/patches/tests/Tests-Ignore-tests-with-some-version-checking.patch --- twisted-20.3.0/debian/patches/tests/Tests-Ignore-tests-with-some-version-checking.patch 1970-01-01 00:00:00.000000000 +0000 +++ twisted-22.1.0/debian/patches/tests/Tests-Ignore-tests-with-some-version-checking.patch 2022-02-17 10:40:49.000000000 +0000 @@ -0,0 +1,83 @@ +From: Carsten Schoenert +Date: Mon, 24 Jan 2022 16:24:01 +0100 +Subject: Tests: Ignore tests with some version checking + +Ignoring these tests for now, otherwise we will see errors like these: + +GfuBJzdQw0E/eMmO//94KHezzfXqbIRKGzUVj5hVG8Q= +Traceback (most recent call last): + File "/usr/lib/python3/dist-packages/twisted/python/test/test_versions.py", line 161, in test_baseWithPrerelease + self.assertEqual(Version("foo", 1, 0, 0, prerelease=8).base(), "1.0.0.rc8") + File "/usr/lib/python3/dist-packages/twisted/trial/_synctest.py", line 424, in assertEqual + super().assertEqual(first, second, msg) + File "/usr/lib/python3.9/unittest/case.py", line 837, in assertEqual + assertion_func(first, second, msg=msg) + File "/usr/lib/python3.9/unittest/case.py", line 1217, in assertMultiLineEqual + self.fail(self._formatMessage(msg, standardMsg)) +twisted.trial.unittest.FailTest: '1.0.0rc8' != '1.0.0.rc8' +- 1.0.0rc8 ++ 1.0.0.rc8 +? + + +Traceback (most recent call last): + File "/usr/lib/python3/dist-packages/twisted/python/test/test_versions.py", line 146, in test_getVersionStringWithPrerelease + self.assertEqual( + File "/usr/lib/python3/dist-packages/twisted/trial/_synctest.py", line 424, in assertEqual + super().assertEqual(first, second, msg) + File "/usr/lib/python3.9/unittest/case.py", line 837, in assertEqual + assertion_func(first, second, msg=msg) + File "/usr/lib/python3.9/unittest/case.py", line 1217, in assertMultiLineEqual + self.fail(self._formatMessage(msg, standardMsg)) +twisted.trial.unittest.FailTest: 'whatever 8.0.0rc1' != 'whatever 8.0.0.rc1' +- whatever 8.0.0rc1 ++ whatever 8.0.0.rc1 +? + + +Traceback (most recent call last): + File "/usr/lib/python3/dist-packages/twisted/python/test/test_versions.py", line 128, in test_strWithPrerelease + self.assertEqual( + File "/usr/lib/python3/dist-packages/twisted/trial/_synctest.py", line 424, in assertEqual + super().assertEqual(first, second, msg) + File "/usr/lib/python3.9/unittest/case.py", line 837, in assertEqual + assertion_func(first, second, msg=msg) + File "/usr/lib/python3.9/unittest/case.py", line 1217, in assertMultiLineEqual + self.fail(self._formatMessage(msg, standardMsg)) +twisted.trial.unittest.FailTest: '[dummy, version 1.0.0rc1]' != '[dummy, version 1.0.0.rc1]' +- [dummy, version 1.0.0rc1] ++ [dummy, version 1.0.0.rc1] +? +--- + src/twisted/python/test/test_versions.py | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/src/twisted/python/test/test_versions.py b/src/twisted/python/test/test_versions.py +index 351d930..862198e 100644 +--- a/src/twisted/python/test/test_versions.py ++++ b/src/twisted/python/test/test_versions.py +@@ -120,7 +120,7 @@ class VersionsTests(TestCase): + """ + self.assertEqual(str(Version("dummy", 1, 2, 3)), "[dummy, version 1.2.3]") + +- def test_strWithPrerelease(self): ++ def _test_strWithPrerelease(self): + """ + Calling C{str} on a version with a prerelease includes the prerelease. + """ +@@ -138,7 +138,7 @@ class VersionsTests(TestCase): + """ + self.assertEqual("Twisted 8.0.0", getVersionString(Version("Twisted", 8, 0, 0))) + +- def test_getVersionStringWithPrerelease(self): ++ def _test_getVersionStringWithPrerelease(self): + """ + L{getVersionString} includes the prerelease, if any. + """ +@@ -153,7 +153,7 @@ class VersionsTests(TestCase): + """ + self.assertEqual(Version("foo", 1, 0, 0).base(), "1.0.0") + +- def test_baseWithPrerelease(self): ++ def _test_baseWithPrerelease(self): + """ + The base version includes 'preX' for versions with prereleases. + """ diff -Nru twisted-20.3.0/debian/patches/tests/Tests-Ignore-test_unicodeLogFileUTF8.patch twisted-22.1.0/debian/patches/tests/Tests-Ignore-test_unicodeLogFileUTF8.patch --- twisted-20.3.0/debian/patches/tests/Tests-Ignore-test_unicodeLogFileUTF8.patch 1970-01-01 00:00:00.000000000 +0000 +++ twisted-22.1.0/debian/patches/tests/Tests-Ignore-test_unicodeLogFileUTF8.patch 2022-02-17 10:40:49.000000000 +0000 @@ -0,0 +1,22 @@ +From: Carsten Schoenert +Date: Thu, 27 Jan 2022 21:39:30 +0100 +Subject: Tests: Ignore test_unicodeLogFileUTF8() + +Ignore this test that is checking some ascii functionality. +--- + src/twisted/trial/_dist/test/test_worker.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/twisted/trial/_dist/test/test_worker.py b/src/twisted/trial/_dist/test/test_worker.py +index 1d20c5e..81f8710 100644 +--- a/src/twisted/trial/_dist/test/test_worker.py ++++ b/src/twisted/trial/_dist/test/test_worker.py +@@ -336,7 +336,7 @@ class LocalWorkerTests(TestCase): + self.assertEqual(b"foo", localWorker._ampProtocol.dataString) + self.assertEqual(b"bar", localWorker._outLog.getvalue()) + +- def test_unicodeLogFileUTF8(self): ++ def _test_unicodeLogFileUTF8(self): + """ + L{LocalWorker} write the log data with local newlines but + in UTF-8 encoding regardless of the default encoding. diff -Nru twisted-20.3.0/debian/patches/tests/Tests-Skip-test-for-empty-cypher-string.patch twisted-22.1.0/debian/patches/tests/Tests-Skip-test-for-empty-cypher-string.patch --- twisted-20.3.0/debian/patches/tests/Tests-Skip-test-for-empty-cypher-string.patch 1970-01-01 00:00:00.000000000 +0000 +++ twisted-22.1.0/debian/patches/tests/Tests-Skip-test-for-empty-cypher-string.patch 2022-02-17 10:40:49.000000000 +0000 @@ -0,0 +1,25 @@ +From: Balint Reczey +Date: Wed, 28 Nov 2018 20:39:26 +0100 +Subject: Tests: Skip test for empty cypher string + +OpenSSL does not throw error now, OTOH this is the expected behavior. +But this would make running the test suite failing. + +Forwarded: https://github.com/openssl/openssl/issues/7725 +--- + src/twisted/test/test_sslverify.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/twisted/test/test_sslverify.py b/src/twisted/test/test_sslverify.py +index faae0ac..e9725ab 100644 +--- a/src/twisted/test/test_sslverify.py ++++ b/src/twisted/test/test_sslverify.py +@@ -2789,7 +2789,7 @@ class ExpandCipherStringTests(TestCase): + if skipSSL: + skip = skipSSL + +- def test_doesNotStumbleOverEmptyList(self): ++ def _test_doesNotStumbleOverEmptyList(self): + """ + If the expanded cipher list is empty, an empty L{list} is returned. + """ diff -Nru twisted-20.3.0/debian/python3-twisted.lintian-overrides twisted-22.1.0/debian/python3-twisted.lintian-overrides --- twisted-20.3.0/debian/python3-twisted.lintian-overrides 2021-04-24 16:36:24.000000000 +0000 +++ twisted-22.1.0/debian/python3-twisted.lintian-overrides 1970-01-01 00:00:00.000000000 +0000 @@ -1,6 +0,0 @@ -# Override this error as the Google Analytics is part of a layout template -# that is only used internally by the Twisted project to generate online -# API documentation. -python3-twisted: privacy-breach-google-adsense usr/lib/python3/dist-packages/twisted/python/_pydoctortemplates/common.html -python3-twisted: privacy-breach-google-adsense usr/lib/python3/dist-packages/twisted/python/_pydoctortemplates/index.html -python3-twisted: privacy-breach-google-adsense usr/lib/python3/dist-packages/twisted/python/_pydoctortemplates/summary.html diff -Nru twisted-20.3.0/debian/python3-twisted.postinst twisted-22.1.0/debian/python3-twisted.postinst --- twisted-20.3.0/debian/python3-twisted.postinst 2021-04-24 16:36:24.000000000 +0000 +++ twisted-22.1.0/debian/python3-twisted.postinst 2022-02-17 10:40:49.000000000 +0000 @@ -9,20 +9,21 @@ # remove all cache files, then rebuild for the installed python3 versions rm -f /usr/lib/python3/*-packages/twisted/plugins/dropin.cache for p in $(py3versions -i); do - $p -c 'from twisted.plugin import IPlugin, getPlugins; list(getPlugins(IPlugin))' \ - >/dev/null 2>&1 || true + "${p}" -c 'from twisted.plugin import IPlugin, getPlugins; list(getPlugins(IPlugin))' \ + >/dev/null 2>&1 || true done } case "$1" in triggered) - if [ "$2" = twisted-plugins-cache ]; then - rebuild_cache - fi - ;; + if [ "$2" = twisted-plugins-cache ]; then + rebuild_cache + fi + ;; + configure) - rebuild_cache - ;; + rebuild_cache + ;; esac exit 0 diff -Nru twisted-20.3.0/debian/python3-twisted.postrm twisted-22.1.0/debian/python3-twisted.postrm --- twisted-20.3.0/debian/python3-twisted.postrm 2021-04-24 16:36:24.000000000 +0000 +++ twisted-22.1.0/debian/python3-twisted.postrm 2022-02-17 10:40:49.000000000 +0000 @@ -4,12 +4,12 @@ case "$1" in remove|purge) - find /usr/lib/python3/dist-packages/twisted/plugins \ - -name dropin.cache 2>/dev/null | xargs -r rm -f - for d in /usr/lib/python3/dist-packages/twisted/plugins; do - rmdir $d 2>/dev/null || true - done - ;; + find /usr/lib/python3/dist-packages/twisted/plugins \ + -name dropin.cache 2>/dev/null | xargs -r rm -f + for d in /usr/lib/python3/dist-packages/twisted/plugins; do + rmdir "${d}" 2>/dev/null || true + done + ;; esac #DEBHELPER# diff -Nru twisted-20.3.0/debian/rules twisted-22.1.0/debian/rules --- twisted-20.3.0/debian/rules 2021-04-24 16:36:24.000000000 +0000 +++ twisted-22.1.0/debian/rules 2022-02-17 10:40:49.000000000 +0000 @@ -1,44 +1,23 @@ -#! /usr/bin/make -f +#!/usr/bin/make -f +# -*- makefile -*- + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 SHELL = /bin/bash -# all versions -PY3VERS := $(shell py3versions -vs) -VER3 := $(shell /usr/bin/python3 -c 'import sys; print(sys.version[:3])') -DEBVERS := $(shell dpkg-parsechangelog | sed -n -e 's/^Version: //p') - -twversion := $(subst twisted-,,$(notdir $(CURDIR))) - -include /usr/share/python3/python.mk - -with_docs= -ifeq (,$(findstring nodoc,$(DEB_BUILD_OPTIONS) $(DEB_BUILD_PROFILES))) - with_docs=yes -endif +export PYBUILD_NAME=twisted +# Don't do any testing while build, it's done within autopkgtesting. +export PYBUILD_DISABLE=test +export PYBUILD_AFTER_INSTALL=rm -rf {destdir}/usr/lib/python3*/dist-packages/twisted/internet/iocpreactor/notes.txt -build: build-stamp -build-arch: build-stamp -build-indep: build-stamp -build-indep: build-stamp build-doc -build-stamp: $(PY3VERS:%=build3-python%) - touch $@ - -build3-python%: - python$* setup.py build - python$*-dbg setup.py build - touch $@ - -build-doc: -ifeq ($(with_docs),yes) - $(MAKE) -C docs html - touch $@ -endif +%: + dh $@ --with sphinxdoc --buildsystem=pybuild -clean: +override_dh_clean: rm -rf *-stamp build tmp rm -rf docs/_build build-doc rm -rf build3-python* - rm -rf apidocs py3 rm -rf $(addprefix debian/,$(packages)) debian/files debian/substvars rm -rf _trial_temp test.log rm -f twisted/plugins/dropin.cache @@ -46,121 +25,56 @@ find . -name "*.pyc" |xargs -r rm dh_clean -install: build-stamp install-prereq $(PY3VERS:%=install3-python%) install-nover - -install-prereq: build-stamp - dh_testdir - dh_testroot - dh_prep - -install3-python%: install-prereq - : # python3-twisted $* - python$* setup.py install \ - --root=$(CURDIR)/debian/python3-twisted --install-layout=deb +override_dh_sphinxdoc: +ifeq (,$(findstring nodoc, $(DEB_BUILD_OPTIONS))) + PYTHONPATH=src python3 -m sphinx -b html -N docs/ $(CURDIR)/debian/twisted-doc/usr/share/doc/twisted-doc/html + dh_sphinxdoc + cp -a docs/core/* debian/twisted-doc/usr/share/doc/twisted-doc/ + -find debian/twisted-doc -type d -name man | xargs rm -rf + # Replace all '#!' calls to python with /usr/bin/python3 and make them + # NON executable. + for i in `find debian/twisted-doc -mindepth 3 -type f`; do \ + sed '1s,#!.*python[^ ]*\(.*\),#! /usr/bin/python3\1,' \ + $${i} > $${i}.temp; \ + if cmp --quiet $${i} $${i}.temp; then \ + rm -f $${i}.temp; \ + else \ + mv -f $${i}.temp $${i}; \ + chmod 644 $${i}; \ + echo "fixed interpreter: $${i}"; \ + fi; \ + done +endif +override_dh_auto_install: + dh_auto_install find debian/python3-twisted -name '__pycache__' | xargs -r rm -rf - - : # python3-twisted-bin - dh_movefiles -ppython3-twisted-bin \ - --sourcedir=debian/python3-twisted \ - $(call py_libdir,$*)/twisted/test/raiser.cpython-$(subst .,,$*)$(if $(filter 3.7,$*),m)-$(DEB_HOST_MULTIARCH).so -# $(call py_libdir,$*)/twisted/python/sendmsg.cpython-$(subst .,,$*)$(if $(filter 3.7,$*),m)-$(DEB_HOST_MULTIARCH).so - - : # Replace all '#!' calls to python with /usr/bin/python3 - : # and make them executable - for i in `find debian/python3-twisted debian/python3-twisted-* -mindepth 3 -type f`; do \ + # Replace all '#!' calls to python with /usr/bin/python3 and make them + # executable. + for i in `find debian/python3-twisted -mindepth 3 -type f`; do \ sed '1s,#!.*python[^ ]*\(.*\),#! /usr/bin/python3\1,' \ - $$i > $$i.temp; \ - if cmp --quiet $$i $$i.temp; then \ - rm -f $$i.temp; \ + $${i} > $${i}.temp; \ + if cmp --quiet $${i} $${i}.temp; then \ + rm -f $${i}.temp; \ else \ - mv -f $$i.temp $$i; \ - chmod 755 $$i; \ - echo "fixed interpreter: $$i"; \ + mv -f $${i}.temp $${i}; \ + chmod 755 $${i}; \ + echo "fixed interpreter: $${i}"; \ fi; \ done - - : # python-twisted-bin-dbg - python$*-dbg setup.py install \ - --root=$(CURDIR)/debian/python3-twisted-bin-dbg --no-compile --install-layout=deb - find debian/python3-twisted-bin-dbg ! -type d ! -name '*.so' | xargs rm -f - find debian/python3-twisted-bin-dbg -depth -empty -exec rmdir {} \; - -install-nover: - : # python3 man pages + # Tune Python3 man pages. for i in debian/python3-twisted/usr/bin/*; do \ - mv $$i $${i}3; \ + mv $${i} $${i}3; \ done mkdir -p debian/python3-twisted/usr/share/man/man1 for i in docs/core/man/{trial,twistd}.1; do \ - i2=$$(basename $$i .1)3.1; \ - cp -p $$i debian/python3-twisted/usr/share/man/man1/$$i2; \ + i2=$$(basename $${i} .1)3.1; \ + cp -p $${i} debian/python3-twisted/usr/share/man/man1/$${i2}; \ done -ifeq ($(with_docs),yes) - : # twisted-doc - mkdir -p debian/twisted-doc/usr/share/doc/twisted-doc - cp -a docs/core/* debian/twisted-doc/usr/share/doc/twisted-doc/ - -find debian/twisted-doc -type d -name man | xargs rm -rf -endif - -binary-indep: build-indep build-doc install - dh_testdir - dh_testroot - dh_installchangelogs -i - dh_installdocs -i -ifeq ($(with_docs),yes) - cp -a docs/_build/html debian/twisted-doc/usr/share/doc/twisted-doc/. - dh_sphinxdoc -ptwisted-doc -endif - -ifeq ($(with_docs),yes) - -cp -ua apidocs debian/twisted-doc/usr/share/doc/twisted-doc/ - # Replace links to Twisted website API doc by links to the local doc. We - # need to compute path to the API which goes up enough in the folder - # hierarchy to access the doc. - -[ -d apidocs ] && \ - for i in $$(cd debian/twisted-doc/usr/share/doc/twisted-doc/ && \ - grep -rl "http://twistedmatrix.com/documents/$(twversion)/api/" .); do \ - path="$$(echo $$i | sed "s:[^/]::g" | sed "s:/:../:g" | cut -c 4-)apidocs/"; \ - sed -i "s:http\://twistedmatrix.com/documents/$(twversion)/api/:$$path:g" \ - debian/twisted-doc/usr/share/doc/twisted-doc/$$i; \ - done -endif - dh_installmenu -i +override_dh_compress: + # Don't compress an Python files. Especially not in twisted-doc. dh_compress -i -X.py - dh_fixperms -i - dh_lintian -i + +override_dh_python3: dh_python3 -i -X hamcrest - # post-process .substvars until #806316 is fixed and -X starts working - sed -i 's/ python3*-hamcrest,//' debian/python3-twisted.substvars - dh_installdeb -i - dh_gencontrol -ptwisted-doc -ppython3-twisted - dh_md5sums -i - dh_builddeb -i - -binary-arch: build-arch install - dh_testdir - dh_testroot - dh_installchangelogs -a - dh_lintian -a - dh_installdocs -a - dh_installmenu -a - - dh_strip -ppython3-twisted-bin --dbg-package=python3-twisted-bin-dbg - rm -rf debian/python3-twisted-bin-dbg/usr/share/doc/python3-twisted-bin-dbg - ln -s python3-twisted-bin \ - debian/python3-twisted-bin-dbg/usr/share/doc/python3-twisted-bin-dbg - - dh_compress -a -X.py - dh_fixperms -a - dh_python3 -a - dh_installdeb -a - dh_shlibdeps -a - dh_gencontrol -a - dh_md5sums -a - dh_builddeb -a - -binary: binary-indep binary-arch -.PHONY: build clean binary-indep binary-arch binary install install-nover install-prereq -.NOTPARALLEL: diff -Nru twisted-20.3.0/debian/source/options twisted-22.1.0/debian/source/options --- twisted-20.3.0/debian/source/options 2021-04-24 16:36:24.000000000 +0000 +++ twisted-22.1.0/debian/source/options 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -extend-diff-ignore = "^\.travis\.yml$" diff -Nru twisted-20.3.0/debian/tests/control twisted-22.1.0/debian/tests/control --- twisted-20.3.0/debian/tests/control 2021-04-24 16:36:24.000000000 +0000 +++ twisted-22.1.0/debian/tests/control 2022-02-17 10:40:49.000000000 +0000 @@ -1,5 +1,6 @@ Tests: unit-tests-3 Restrictions: needs-root -Depends: @, - python3-setuptools, - python3-hamcrest, +Depends: + python3-hamcrest, + python3-setuptools, + @, diff -Nru twisted-20.3.0/debian/tests/unit-tests-3 twisted-22.1.0/debian/tests/unit-tests-3 --- twisted-20.3.0/debian/tests/unit-tests-3 2021-04-24 16:36:24.000000000 +0000 +++ twisted-22.1.0/debian/tests/unit-tests-3 2022-02-17 10:40:49.000000000 +0000 @@ -1,5 +1,7 @@ #!/bin/sh -e -chown nobody $AUTOPKGTEST_TMP -cd $AUTOPKGTEST_TMP -echo python3 -m twisted.trial --reporter=summary -e twisted | su -s /bin/sh nobody +chown nobody ${AUTOPKGTEST_TMP} + +cd ${AUTOPKGTEST_TMP} && \ + ls -la && \ + echo "python3 -m twisted.trial --temp-directory=${AUTOPKGTEST_TMP}/_trial_temp --reactor=default --reporter=verbose -e twisted" | su -s /bin/sh nobody diff -Nru twisted-20.3.0/debian/twisted-doc.lintian-overrides twisted-22.1.0/debian/twisted-doc.lintian-overrides --- twisted-20.3.0/debian/twisted-doc.lintian-overrides 1970-01-01 00:00:00.000000000 +0000 +++ twisted-22.1.0/debian/twisted-doc.lintian-overrides 2022-02-17 10:40:49.000000000 +0000 @@ -0,0 +1,2 @@ +# This isn't a possible real privacy breach, it uses 'example.org' to show the usage. +twisted-doc: privacy-breach-generic usr/share/doc/twisted-doc/html/_downloads/* * diff -Nru twisted-20.3.0/debian/upstream/metadata twisted-22.1.0/debian/upstream/metadata --- twisted-20.3.0/debian/upstream/metadata 2021-04-24 16:36:24.000000000 +0000 +++ twisted-22.1.0/debian/upstream/metadata 2022-02-17 10:40:49.000000000 +0000 @@ -1,2 +1,5 @@ +Bug-Database: https://twistedmatrix.com/trac/report +Bug-Submit: https://twistedmatrix.com/trac/newticket +FAQ: https://twistedmatrix.com/trac/wiki/FrequentlyAskedQuestions Repository: https://github.com/twisted/twisted.git Repository-Browse: https://github.com/twisted/twisted diff -Nru twisted-20.3.0/debian/watch twisted-22.1.0/debian/watch --- twisted-20.3.0/debian/watch 2021-04-24 16:36:24.000000000 +0000 +++ twisted-22.1.0/debian/watch 2022-02-17 10:40:49.000000000 +0000 @@ -1,2 +1,10 @@ -version=3 -https://pypi.debian.net/Twisted/Twisted-([\d\.]*)\.tar\.bz2 +# watch file for twisted + +version=4 + +opts="mode=git, \ + filenamemangle=s/.+\/(\d\S*)\.tar\.gz/twisted-$1.tar.gz/ \ + uversionmangle=s/(\d)[_\.\-\+]?((RC|rc|pre|dev|beta|alpha)\.?\d*)$/$1~$2/, \ + dversionmangle=s/\+ds(\.?\d+)?$//" \ +https://github.com/twisted/twisted.git \ + refs/tags/twisted-(\d{2}\.\d.\d) diff -Nru twisted-20.3.0/docs/api/index.rst twisted-22.1.0/docs/api/index.rst --- twisted-20.3.0/docs/api/index.rst 1970-01-01 00:00:00.000000000 +0000 +++ twisted-22.1.0/docs/api/index.rst 2022-02-07 13:12:15.000000000 +0000 @@ -0,0 +1,8 @@ +API Reference +============= + +This file will be overwritten by the pydoctor build triggered at the end +of the Sphinx build. + +It's a hack to be able to reference the API index page from inside Sphinx +and have it as part of the TOC. diff -Nru twisted-20.3.0/docs/conch/benchmarks/buffering_mixin.py twisted-22.1.0/docs/conch/benchmarks/buffering_mixin.py --- twisted-20.3.0/docs/conch/benchmarks/buffering_mixin.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/conch/benchmarks/buffering_mixin.py 2022-02-07 13:12:15.000000000 +0000 @@ -7,20 +7,17 @@ L{BufferingMixin} mixed in to perform Nagle-like write coalescing. """ -from __future__ import print_function -from sys import stdout from pprint import pprint +from sys import stdout from time import time -from twisted.python.usage import Options -from twisted.python.log import startLogging - -from twisted.internet.protocol import ServerFactory, Protocol, ClientCreator -from twisted.internet.defer import Deferred -from twisted.internet import reactor - from twisted.conch.mixin import BufferingMixin +from twisted.internet import reactor +from twisted.internet.defer import Deferred +from twisted.internet.protocol import ClientCreator, Protocol, ServerFactory +from twisted.python.log import startLogging +from twisted.python.usage import Options class BufferingBenchmark(Options): @@ -29,12 +26,16 @@ """ optParameters = [ - ('scale', 's', '1', - 'Work multiplier (bigger takes longer, might resist noise better)')] + ( + "scale", + "s", + "1", + "Work multiplier (bigger takes longer, might resist noise better)", + ) + ] def postOptions(self): - self['scale'] = int(self['scale']) - + self["scale"] = int(self["scale"]) class ServerProtocol(Protocol): @@ -42,11 +43,11 @@ A silent protocol which only waits for a particular amount of input and then fires a Deferred. """ + def __init__(self, expected, finished): self.expected = expected self.finished = finished - def dataReceived(self, bytes): self.expected -= len(bytes) if self.expected == 0: @@ -54,14 +55,12 @@ finished.callback(None) - class BufferingProtocol(Protocol, BufferingMixin): """ A protocol which uses the buffering mixin to provide a write method. """ - class UnbufferingProtocol(Protocol): """ A protocol which provides a naive write method which simply passes through @@ -78,24 +77,24 @@ self.flush = lambda: None - def _write(proto, byteCount): write = proto.write flush = proto.flush for i in range(byteCount): - write('x') + write("x") flush() - def _benchmark(byteCount, clientProtocol): result = {} finished = Deferred() + def cbFinished(ignored): - result[u'disconnected'] = time() - result[u'duration'] = result[u'disconnected'] - result[u'connected'] + result["disconnected"] = time() + result["duration"] = result["disconnected"] - result["connected"] return result + finished.addCallback(cbFinished) f = ServerFactory() @@ -103,26 +102,25 @@ server = reactor.listenTCP(0, f) f2 = ClientCreator(reactor, clientProtocol) - proto = f2.connectTCP('127.0.0.1', server.getHost().port) + proto = f2.connectTCP("127.0.0.1", server.getHost().port) + def connected(proto): - result[u'connected'] = time() + result["connected"] = time() return proto + proto.addCallback(connected) proto.addCallback(_write, byteCount) return finished - def _benchmarkBuffered(byteCount): return _benchmark(byteCount, BufferingProtocol) - def _benchmarkUnbuffered(byteCount): return _benchmark(byteCount, UnbufferingProtocol) - def benchmark(scale=1): """ Benchmark and return information regarding the relative performance of a @@ -134,7 +132,7 @@ @return: A Deferred which will fire with a dictionary mapping each of the two unicode strings C{u'buffered'} and C{u'unbuffered'} to - dictionaries describing the performance of a protocol of each type. + dictionaries describing the performance of a protocol of each type. These value dictionaries will map the unicode strings C{u'connected'} and C{u'disconnected'} to the times at which each of those events occurred and C{u'duration'} two the difference between these two values. @@ -144,19 +142,22 @@ byteCount = 1024 bufferedDeferred = _benchmarkBuffered(byteCount * scale) + def didBuffered(bufferedResult): - overallResult[u'buffered'] = bufferedResult - unbufferedDeferred = _benchmarkUnbuffered(byteCount * scale) + overallResult["buffered"] = bufferedResult + unbufferedDeferred = _benchmarkUnbuffered(byteCount * scale) + def didUnbuffered(unbufferedResult): - overallResult[u'unbuffered'] = unbufferedResult + overallResult["unbuffered"] = unbufferedResult return overallResult + unbufferedDeferred.addCallback(didUnbuffered) return unbufferedDeferred + bufferedDeferred.addCallback(didBuffered) return bufferedDeferred - def main(args=None): """ Perform a single benchmark run, starting and stopping the reactor and @@ -167,17 +168,22 @@ options = BufferingBenchmark() options.parseOptions(args) - d = benchmark(options['scale']) + d = benchmark(options["scale"]) + def cbBenchmark(result): pprint(result) + def ebBenchmark(err): print(err.getTraceback()) + d.addCallbacks(cbBenchmark, ebBenchmark) + def stopReactor(ign): reactor.stop() + d.addBoth(stopReactor) reactor.run() -if __name__ == '__main__': +if __name__ == "__main__": main() diff -Nru twisted-20.3.0/docs/conch/examples/demo_draw.tac twisted-22.1.0/docs/conch/examples/demo_draw.tac --- twisted-20.3.0/docs/conch/examples/demo_draw.tac 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/conch/examples/demo_draw.tac 2022-02-07 13:12:15.000000000 +0000 @@ -20,19 +20,19 @@ from twisted.application import internet, service from twisted.conch.insults import insults -from twisted.conch.telnet import TelnetTransport, TelnetBootstrapProtocol from twisted.conch.manhole_ssh import ConchFactory, TerminalRealm from twisted.conch.ssh import keys +from twisted.conch.telnet import TelnetBootstrapProtocol, TelnetTransport from twisted.cred import checkers, portal from twisted.internet import protocol - class Draw(insults.TerminalProtocol): """Protocol which accepts arrow key and spacebar input and places the requested characters onto the terminal. """ - cursors = list('!@#$%^&*()_+-=') + + cursors = list("!@#$%^&*()_+-=") def connectionMade(self): self.terminal.eraseDisplay() @@ -48,29 +48,35 @@ self.terminal.cursorBackward() elif keyID == self.terminal.RIGHT_ARROW: self.terminal.cursorForward() - elif keyID == ' ': - self.cursor = self.cursors[(self.cursors.index(self.cursor) + 1) % len(self.cursors)] + elif keyID == " ": + self.cursor = self.cursors[ + (self.cursors.index(self.cursor) + 1) % len(self.cursors) + ] else: return self.terminal.write(self.cursor) self.terminal.cursorBackward() + def makeService(args): checker = checkers.InMemoryUsernamePasswordDatabaseDontUse(username=b"password") f = protocol.ServerFactory() - f.protocol = lambda: TelnetTransport(TelnetBootstrapProtocol, - insults.ServerProtocol, - args['protocolFactory'], - *args.get('protocolArgs', ()), - **args.get('protocolKwArgs', {})) - tsvc = internet.TCPServer(args['telnet'], f) + f.protocol = lambda: TelnetTransport( + TelnetBootstrapProtocol, + insults.ServerProtocol, + args["protocolFactory"], + *args.get("protocolArgs", ()), + **args.get("protocolKwArgs", {}), + ) + tsvc = internet.TCPServer(args["telnet"], f) def chainProtocolFactory(): return insults.ServerProtocol( - args['protocolFactory'], - *args.get('protocolArgs', ()), - **args.get('protocolKwArgs', {})) + args["protocolFactory"], + *args.get("protocolArgs", ()), + **args.get("protocolKwArgs", {}), + ) rlm = TerminalRealm() rlm.chainedProtocolFactory = chainProtocolFactory @@ -78,14 +84,15 @@ f = ConchFactory(ptl) f.publicKeys[b"ssh-rsa"] = keys.Key.fromFile("ssh-keys/ssh_host_rsa_key.pub") f.privateKeys[b"ssh-rsa"] = keys.Key.fromFile("ssh-keys/ssh_host_rsa_key") - csvc = internet.TCPServer(args['ssh'], f) + csvc = internet.TCPServer(args["ssh"], f) m = service.MultiService() tsvc.setServiceParent(m) csvc.setServiceParent(m) return m + application = service.Application("Insults Demo App") -makeService({'protocolFactory': Draw, - 'telnet': 6023, - 'ssh': 6022}).setServiceParent(application) +makeService({"protocolFactory": Draw, "telnet": 6023, "ssh": 6022}).setServiceParent( + application +) diff -Nru twisted-20.3.0/docs/conch/examples/demo_insults.tac twisted-22.1.0/docs/conch/examples/demo_insults.tac --- twisted-20.3.0/docs/conch/examples/demo_insults.tac 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/conch/examples/demo_insults.tac 2022-02-07 13:12:15.000000000 +0000 @@ -26,24 +26,25 @@ animation process. """ -import random, string +import random +import string from twisted.application import internet, service from twisted.conch.insults import insults -from twisted.conch.telnet import TelnetTransport, TelnetBootstrapProtocol from twisted.conch.manhole_ssh import ConchFactory, TerminalRealm from twisted.conch.ssh import keys +from twisted.conch.telnet import TelnetBootstrapProtocol, TelnetTransport from twisted.cred import checkers, portal from twisted.internet import protocol, task from twisted.python import log - class DrawingFinished(Exception): """Sentinel exception, raised when no \"frames\" for a particular \"animation\" remain to be drawn. """ + class Drawable: """Representation of an animation. @@ -56,6 +57,7 @@ Frames are defined with draw_ prefixed methods. Erasure is performed by erase_ prefixed methods. """ + n = 0 def __init__(self, proto, col, line): @@ -73,9 +75,9 @@ line += 1 def iterate(self): - getattr(self, 'erase_' + str(self.n))() + getattr(self, "erase_" + str(self.n))() self.n += 1 - f = getattr(self, 'draw_' + str(self.n), None) + f = getattr(self, "draw_" + str(self.n), None) if f is None: raise DrawingFinished() f() @@ -90,84 +92,86 @@ def draw_1(self): # . . - #. . . + # . . . # . . - self.drawLines(' . .\n. . .\n . .') + self.drawLines(" . .\n. . .\n . .") def erase_1(self): - self.drawLines(' \n \n ') + self.drawLines(" \n \n ") def draw_2(self): # . . . . # . o o o . - #. o o o o . + # . o o o o . # . o o o . # . . . . - self.drawLines(' . . . .\n . o o o .\n. o o o o .\n . o o o .\n . . . .') + self.drawLines(" . . . .\n . o o o .\n. o o o o .\n . o o o .\n . . . .") def erase_2(self): - self.drawLines(' \n \n \n \n ') + self.drawLines(" \n \n \n \n ") def draw_3(self): # o o o o # o O O O o - #o O O O O o + # o O O O O o # o O O O o # o o o o - self.drawLines(' o o o o\n o O O O o\no O O O O o\n o O O O o\n o o o o') + self.drawLines(" o o o o\n o O O O o\no O O O O o\n o O O O o\n o o o o") erase_3 = erase_2 def draw_4(self): # O O O O # O . . . O - #O . . . . O + # O . . . . O # O . . . O # O O O O - self.drawLines(' O O O O\n O . . . O\nO . . . . O\n O . . . O\n O O O O') + self.drawLines(" O O O O\n O . . . O\nO . . . . O\n O . . . O\n O O O O") erase_4 = erase_3 def draw_5(self): # . . . . # . . - #. . + # . . # . . # . . . . - self.drawLines(' . . . .\n . .\n. .\n . .\n . . . .') + self.drawLines(" . . . .\n . .\n. .\n . .\n . . . .") erase_5 = erase_4 + class Drop(Drawable): WIDTH = 3 HEIGHT = 4 def draw_1(self): # o - self.drawLines(' o') + self.drawLines(" o") def erase_1(self): - self.drawLines(' ') + self.drawLines(" ") def draw_2(self): # _ - #/ \ - #\./ - self.drawLines(' _ \n/ \\\n\\./') + # / \ + # \./ + self.drawLines(" _ \n/ \\\n\\./") def erase_2(self): - self.drawLines(' \n \n ') + self.drawLines(" \n \n ") def draw_3(self): # O - self.drawLines(' O') + self.drawLines(" O") def erase_3(self): - self.drawLines(' ') + self.drawLines(" ") + class DemoProtocol(insults.TerminalProtocol): - """Draws random things at random places on the screen. - """ + """Draws random things at random places on the screen.""" + width = 80 height = 24 @@ -198,7 +202,9 @@ s = cls(self.terminal, col, line) c = task.LoopingCall(s.iterate) - c.start(self.rate).addErrback(lambda f: f.trap(DrawingFinished)).addErrback(log.err) + c.start(self.rate).addErrback(lambda f: f.trap(DrawingFinished)).addErrback( + log.err + ) # ITerminalListener def terminalSize(self, width, height): @@ -206,19 +212,19 @@ self.height = height def unhandledControlSequence(self, seq): - log.msg("Client sent something weird: %r" % (seq,)) + log.msg(f"Client sent something weird: {seq!r}") def keystrokeReceived(self, keyID, modifier): - if keyID == '+': + if keyID == "+": self.interval /= 1.1 - elif keyID == '-': + elif keyID == "-": self.interval *= 1.1 - elif keyID == '*': + elif keyID == "*": self.rate /= 1.1 - elif keyID == '/': + elif keyID == "/": self.rate *= 1.1 else: - log.msg("Client sent: %r" % (keyID,)) + log.msg(f"Client sent: {keyID!r}") return self._call.stop() @@ -230,18 +236,21 @@ checker = checkers.InMemoryUsernamePasswordDatabaseDontUse(username=b"password") f = protocol.ServerFactory() - f.protocol = lambda: TelnetTransport(TelnetBootstrapProtocol, - insults.ServerProtocol, - args['protocolFactory'], - *args.get('protocolArgs', ()), - **args.get('protocolKwArgs', {})) - tsvc = internet.TCPServer(args['telnet'], f) + f.protocol = lambda: TelnetTransport( + TelnetBootstrapProtocol, + insults.ServerProtocol, + args["protocolFactory"], + *args.get("protocolArgs", ()), + **args.get("protocolKwArgs", {}), + ) + tsvc = internet.TCPServer(args["telnet"], f) def chainProtocolFactory(): return insults.ServerProtocol( - args['protocolFactory'], - *args.get('protocolArgs', ()), - **args.get('protocolKwArgs', {})) + args["protocolFactory"], + *args.get("protocolArgs", ()), + **args.get("protocolKwArgs", {}), + ) rlm = TerminalRealm() rlm.chainedProtocolFactory = chainProtocolFactory @@ -249,15 +258,16 @@ f = ConchFactory(ptl) f.publicKeys[b"ssh-rsa"] = keys.Key.fromFile("ssh-keys/ssh_host_rsa_key.pub") f.privateKeys[b"ssh-rsa"] = keys.Key.fromFile("ssh-keys/ssh_host_rsa_key") - csvc = internet.TCPServer(args['ssh'], f) + csvc = internet.TCPServer(args["ssh"], f) m = service.MultiService() tsvc.setServiceParent(m) csvc.setServiceParent(m) return m + application = service.Application("Insults Demo App") -makeService({'protocolFactory': DemoProtocol, - 'telnet': 6023, - 'ssh': 6022}).setServiceParent(application) +makeService( + {"protocolFactory": DemoProtocol, "telnet": 6023, "ssh": 6022} +).setServiceParent(application) diff -Nru twisted-20.3.0/docs/conch/examples/demo_manhole.tac twisted-22.1.0/docs/conch/examples/demo_manhole.tac --- twisted-20.3.0/docs/conch/examples/demo_manhole.tac 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/conch/examples/demo_manhole.tac 2022-02-07 13:12:15.000000000 +0000 @@ -23,28 +23,30 @@ from twisted.conch.manhole import ColoredManhole from twisted.conch.manhole_ssh import ConchFactory, TerminalRealm from twisted.conch.ssh import keys -from twisted.conch.telnet import TelnetTransport, TelnetBootstrapProtocol +from twisted.conch.telnet import TelnetBootstrapProtocol, TelnetTransport from twisted.cred import checkers, portal from twisted.internet import protocol - def makeService(args): checker = checkers.InMemoryUsernamePasswordDatabaseDontUse(username=b"password") f = protocol.ServerFactory() - f.protocol = lambda: TelnetTransport(TelnetBootstrapProtocol, - insults.ServerProtocol, - args['protocolFactory'], - *args.get('protocolArgs', ()), - **args.get('protocolKwArgs', {})) - tsvc = internet.TCPServer(args['telnet'], f) + f.protocol = lambda: TelnetTransport( + TelnetBootstrapProtocol, + insults.ServerProtocol, + args["protocolFactory"], + *args.get("protocolArgs", ()), + **args.get("protocolKwArgs", {}), + ) + tsvc = internet.TCPServer(args["telnet"], f) def chainProtocolFactory(): return insults.ServerProtocol( - args['protocolFactory'], - *args.get('protocolArgs', ()), - **args.get('protocolKwArgs', {})) + args["protocolFactory"], + *args.get("protocolArgs", ()), + **args.get("protocolKwArgs", {}), + ) rlm = TerminalRealm() rlm.chainedProtocolFactory = chainProtocolFactory @@ -52,16 +54,21 @@ f = ConchFactory(ptl) f.publicKeys[b"ssh-rsa"] = keys.Key.fromFile("ssh-keys/ssh_host_rsa_key.pub") f.privateKeys[b"ssh-rsa"] = keys.Key.fromFile("ssh-keys/ssh_host_rsa_key") - csvc = internet.TCPServer(args['ssh'], f) + csvc = internet.TCPServer(args["ssh"], f) m = service.MultiService() tsvc.setServiceParent(m) csvc.setServiceParent(m) return m + application = service.Application("Interactive Python Interpreter") -makeService({'protocolFactory': ColoredManhole, - 'protocolArgs': (None,), - 'telnet': 6023, - 'ssh': 6022}).setServiceParent(application) +makeService( + { + "protocolFactory": ColoredManhole, + "protocolArgs": (None,), + "telnet": 6023, + "ssh": 6022, + } +).setServiceParent(application) diff -Nru twisted-20.3.0/docs/conch/examples/demo_recvline.tac twisted-22.1.0/docs/conch/examples/demo_recvline.tac --- twisted-20.3.0/docs/conch/examples/demo_recvline.tac 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/conch/examples/demo_recvline.tac 2022-02-07 13:12:15.000000000 +0000 @@ -30,12 +30,11 @@ from twisted.conch.insults import insults from twisted.conch.manhole_ssh import ConchFactory, TerminalRealm from twisted.conch.ssh import keys -from twisted.conch.telnet import TelnetTransport, TelnetBootstrapProtocol +from twisted.conch.telnet import TelnetBootstrapProtocol, TelnetTransport from twisted.cred import checkers, portal from twisted.internet import protocol - class DemoRecvLine(recvline.HistoricRecvLine): """Simple echo protocol. @@ -51,22 +50,26 @@ self.terminal.nextLine() self.terminal.write(self.ps[self.pn]) + def makeService(args): checker = checkers.InMemoryUsernamePasswordDatabaseDontUse(username=b"password") f = protocol.ServerFactory() - f.protocol = lambda: TelnetTransport(TelnetBootstrapProtocol, - insults.ServerProtocol, - args['protocolFactory'], - *args.get('protocolArgs', ()), - **args.get('protocolKwArgs', {})) - tsvc = internet.TCPServer(args['telnet'], f) + f.protocol = lambda: TelnetTransport( + TelnetBootstrapProtocol, + insults.ServerProtocol, + args["protocolFactory"], + *args.get("protocolArgs", ()), + **args.get("protocolKwArgs", {}), + ) + tsvc = internet.TCPServer(args["telnet"], f) def chainProtocolFactory(): return insults.ServerProtocol( - args['protocolFactory'], - *args.get('protocolArgs', ()), - **args.get('protocolKwArgs', {})) + args["protocolFactory"], + *args.get("protocolArgs", ()), + **args.get("protocolKwArgs", {}), + ) rlm = TerminalRealm() rlm.chainedProtocolFactory = chainProtocolFactory @@ -74,15 +77,16 @@ f = ConchFactory(ptl) f.publicKeys[b"ssh-rsa"] = keys.Key.fromFile("ssh-keys/ssh_host_rsa_key.pub") f.privateKeys[b"ssh-rsa"] = keys.Key.fromFile("ssh-keys/ssh_host_rsa_key") - csvc = internet.TCPServer(args['ssh'], f) + csvc = internet.TCPServer(args["ssh"], f) m = service.MultiService() tsvc.setServiceParent(m) csvc.setServiceParent(m) return m + application = service.Application("Insults RecvLine Demo") -makeService({'protocolFactory': DemoRecvLine, - 'telnet': 6023, - 'ssh': 6022}).setServiceParent(application) +makeService( + {"protocolFactory": DemoRecvLine, "telnet": 6023, "ssh": 6022} +).setServiceParent(application) diff -Nru twisted-20.3.0/docs/conch/examples/demo_scroll.tac twisted-22.1.0/docs/conch/examples/demo_scroll.tac --- twisted-20.3.0/docs/conch/examples/demo_scroll.tac 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/conch/examples/demo_scroll.tac 2022-02-07 13:12:15.000000000 +0000 @@ -31,16 +31,15 @@ from twisted.conch.insults import insults from twisted.conch.manhole_ssh import ConchFactory, TerminalRealm from twisted.conch.ssh import keys -from twisted.conch.telnet import TelnetTransport, TelnetBootstrapProtocol +from twisted.conch.telnet import TelnetBootstrapProtocol, TelnetTransport from twisted.cred import checkers, portal from twisted.internet import protocol from twisted.python import log - class DemoProtocol(insults.TerminalProtocol): - """Copies input to an upwards scrolling region. - """ + """Copies input to an upwards scrolling region.""" + width = 80 height = 24 @@ -55,19 +54,19 @@ self.terminal.setScrollRegion(0, height - 1) self.terminal.cursorPosition(0, height) - self.terminal.write('> ') + self.terminal.write("> ") def unhandledControlSequence(self, seq): - log.msg("Client sent something weird: %r" % (seq,)) + log.msg(f"Client sent something weird: {seq!r}") def keystrokeReceived(self, keyID, modifier): - if keyID == '\r': + if keyID == "\r": self.terminal.cursorPosition(0, self.height - 2) self.terminal.nextLine() - self.terminal.write(''.join(self.buffer)) + self.terminal.write("".join(self.buffer)) self.terminal.cursorPosition(0, self.height - 1) self.terminal.eraseToLineEnd() - self.terminal.write('> ') + self.terminal.write("> ") self.buffer = [] elif keyID in list(string.printable): self.terminal.write(keyID) @@ -78,18 +77,21 @@ checker = checkers.InMemoryUsernamePasswordDatabaseDontUse(username=b"password") f = protocol.ServerFactory() - f.protocol = lambda: TelnetTransport(TelnetBootstrapProtocol, - insults.ServerProtocol, - args['protocolFactory'], - *args.get('protocolArgs', ()), - **args.get('protocolKwArgs', {})) - tsvc = internet.TCPServer(args['telnet'], f) + f.protocol = lambda: TelnetTransport( + TelnetBootstrapProtocol, + insults.ServerProtocol, + args["protocolFactory"], + *args.get("protocolArgs", ()), + **args.get("protocolKwArgs", {}), + ) + tsvc = internet.TCPServer(args["telnet"], f) def chainProtocolFactory(): return insults.ServerProtocol( - args['protocolFactory'], - *args.get('protocolArgs', ()), - **args.get('protocolKwArgs', {})) + args["protocolFactory"], + *args.get("protocolArgs", ()), + **args.get("protocolKwArgs", {}), + ) rlm = TerminalRealm() rlm.chainedProtocolFactory = chainProtocolFactory @@ -97,15 +99,16 @@ f = ConchFactory(ptl) f.publicKeys[b"ssh-rsa"] = keys.Key.fromFile("ssh-keys/ssh_host_rsa_key.pub") f.privateKeys[b"ssh-rsa"] = keys.Key.fromFile("ssh-keys/ssh_host_rsa_key") - csvc = internet.TCPServer(args['ssh'], f) + csvc = internet.TCPServer(args["ssh"], f) m = service.MultiService() tsvc.setServiceParent(m) csvc.setServiceParent(m) return m + application = service.Application("Scroll Region Demo App") -makeService({'protocolFactory': DemoProtocol, - 'telnet': 6023, - 'ssh': 6022}).setServiceParent(application) +makeService( + {"protocolFactory": DemoProtocol, "telnet": 6023, "ssh": 6022} +).setServiceParent(application) diff -Nru twisted-20.3.0/docs/conch/examples/demo.tac twisted-22.1.0/docs/conch/examples/demo.tac --- twisted-20.3.0/docs/conch/examples/demo.tac 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/conch/examples/demo.tac 2022-02-07 13:12:15.000000000 +0000 @@ -16,13 +16,19 @@ """ from twisted.application import service + application = service.Application("TAC Demo") from twisted.conch import manhole_tap -manhole_tap.makeService({"telnetPort": "tcp:6023", - "sshPort": "tcp:6022", - "namespace": {"foo": "bar"}, - "passwd": "passwd", - "sshKeyDir": "", - "sshKeyName": "server.key", - "sshKeySize": 4096}).setServiceParent(application) + +manhole_tap.makeService( + { + "telnetPort": "tcp:6023", + "sshPort": "tcp:6022", + "namespace": {"foo": "bar"}, + "passwd": "passwd", + "sshKeyDir": "", + "sshKeyName": "server.key", + "sshKeySize": 4096, + } +).setServiceParent(application) diff -Nru twisted-20.3.0/docs/conch/examples/sshsimpleclient.py twisted-22.1.0/docs/conch/examples/sshsimpleclient.py --- twisted-20.3.0/docs/conch/examples/sshsimpleclient.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/conch/examples/sshsimpleclient.py 2022-02-07 13:12:15.000000000 +0000 @@ -2,12 +2,14 @@ # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. -from __future__ import print_function +import getpass +import os +import struct +import sys -from twisted.conch.ssh import transport, userauth, connection, common, keys, channel +from twisted.conch.ssh import channel, common, connection, keys, transport, userauth from twisted.internet import defer, protocol, reactor from twisted.python import log -import struct, sys, getpass, os """ Example of using a simple SSH client. @@ -22,36 +24,34 @@ # Replace this with your username. # Default username and password will match the sshsimpleserver.py -USER = b'user' -HOST = 'localhost' +USER = b"user" +HOST = "localhost" PORT = 5022 -SERVER_FINGERPRINT = b'55:55:66:24:6b:03:0e:f1:ec:f8:66:c3:51:df:27:4b' +SERVER_FINGERPRINT = b"55:55:66:24:6b:03:0e:f1:ec:f8:66:c3:51:df:27:4b" # Path to RSA SSH keys accepted by the server. -CLIENT_RSA_PUBLIC = 'ssh-keys/client_rsa.pub' +CLIENT_RSA_PUBLIC = "ssh-keys/client_rsa.pub" # Set CLIENT_RSA_PUBLIC to empty to not use SSH key auth. # CLIENT_RSA_PUBLIC = '' -CLIENT_RSA_PRIVATE = 'ssh-keys/client_rsa' +CLIENT_RSA_PRIVATE = "ssh-keys/client_rsa" class SimpleTransport(transport.SSHClientTransport): def verifyHostKey(self, hostKey, fingerprint): - print('Server host key fingerprint: %s' % fingerprint) + print("Server host key fingerprint: %s" % fingerprint) if SERVER_FINGERPRINT == fingerprint: return defer.succeed(True) else: - print('Bad host key. Expecting: %s' % SERVER_FINGERPRINT) - return defer.fail(Exception('Bad server key')) + print("Bad host key. Expecting: %s" % SERVER_FINGERPRINT) + return defer.fail(Exception("Bad server key")) def connectionSecure(self): - self.requestService( - SimpleUserAuth(USER, - SimpleConnection())) + self.requestService(SimpleUserAuth(USER, SimpleConnection())) -class SimpleUserAuth(userauth.SSHUserAuthClient): +class SimpleUserAuth(userauth.SSHUserAuthClient): def getPassword(self): - return defer.succeed(getpass.getpass("%s@%s's password: " % (USER, HOST))) + return defer.succeed(getpass.getpass(f"{USER}@{HOST}'s password: ")) def getGenericAnswers(self, name, instruction, questions): print(name) @@ -59,24 +59,22 @@ answers = [] for prompt, echo in questions: if echo: - answer = raw_input(prompt) + answer = input(prompt) else: answer = getpass.getpass(prompt) answers.append(answer) return defer.succeed(answers) - def getPublicKey(self): if ( - not CLIENT_RSA_PUBLIC or - not os.path.exists(CLIENT_RSA_PUBLIC) or - self.lastPublicKey - ): + not CLIENT_RSA_PUBLIC + or not os.path.exists(CLIENT_RSA_PUBLIC) + or self.lastPublicKey + ): # the file doesn't exist, or we've tried a public key return return keys.Key.fromFile(filename=CLIENT_RSA_PUBLIC) - def getPrivateKey(self): """ A deferred can also be returned. @@ -84,63 +82,63 @@ return defer.succeed(keys.Key.fromFile(CLIENT_RSA_PRIVATE)) - class SimpleConnection(connection.SSHConnection): def serviceStarted(self): - self.openChannel(TrueChannel(2**16, 2**15, self)) - self.openChannel(FalseChannel(2**16, 2**15, self)) - self.openChannel(CatChannel(2**16, 2**15, self)) - + self.openChannel(TrueChannel(2 ** 16, 2 ** 15, self)) + self.openChannel(FalseChannel(2 ** 16, 2 ** 15, self)) + self.openChannel(CatChannel(2 ** 16, 2 ** 15, self)) class TrueChannel(channel.SSHChannel): - name = b'session' # needed for commands + name = b"session" # needed for commands def openFailed(self, reason): - print('true failed', reason) + print("true failed", reason) def channelOpen(self, ignoredData): - self.conn.sendRequest(self, 'exec', common.NS('true')) + self.conn.sendRequest(self, "exec", common.NS("true")) def request_exit_status(self, data): - status = struct.unpack('>L', data)[0] - print('true status was: %s' % status) + status = struct.unpack(">L", data)[0] + print("true status was: %s" % status) self.loseConnection() + class FalseChannel(channel.SSHChannel): - name = b'session' + name = b"session" def openFailed(self, reason): - print('false failed', reason) + print("false failed", reason) def channelOpen(self, ignoredData): - self.conn.sendRequest(self, 'exec', common.NS('false')) + self.conn.sendRequest(self, "exec", common.NS("false")) def request_exit_status(self, data): - status = struct.unpack('>L', data)[0] - print('false status was: %s' % status) + status = struct.unpack(">L", data)[0] + print("false status was: %s" % status) self.loseConnection() + class CatChannel(channel.SSHChannel): - name = b'session' + name = b"session" def openFailed(self, reason): - print('echo failed', reason) + print("echo failed", reason) def channelOpen(self, ignoredData): - self.data = b'' - d = self.conn.sendRequest(self, 'exec', common.NS('cat'), wantReply = 1) + self.data = b"" + d = self.conn.sendRequest(self, "exec", common.NS("cat"), wantReply=1) d.addCallback(self._cbRequest) def _cbRequest(self, ignored): - self.write(b'hello conch\n') + self.write(b"hello conch\n") self.conn.sendEOF(self) def dataReceived(self, data): self.data += data def closed(self): - print('got data from cat: %s' % repr(self.data)) + print("got data from cat: %s" % repr(self.data)) self.loseConnection() reactor.stop() diff -Nru twisted-20.3.0/docs/conch/examples/sshsimpleserver.py twisted-22.1.0/docs/conch/examples/sshsimpleserver.py --- twisted-20.3.0/docs/conch/examples/sshsimpleserver.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/conch/examples/sshsimpleserver.py 2022-02-07 13:12:15.000000000 +0000 @@ -3,17 +3,19 @@ # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. -from twisted.cred import portal -from twisted.cred.checkers import InMemoryUsernamePasswordDatabaseDontUse +import sys + +from zope.interface import implementer + from twisted.conch import avatar -from twisted.conch.checkers import SSHPublicKeyChecker, InMemorySSHKeyDB -from twisted.conch.ssh import factory, userauth, connection, keys, session +from twisted.conch.checkers import InMemorySSHKeyDB, SSHPublicKeyChecker +from twisted.conch.ssh import connection, factory, keys, session, userauth from twisted.conch.ssh.transport import SSHServerTransport -from twisted.internet import reactor, protocol -from twisted.python import log -from twisted.python import components -from zope.interface import implementer -import sys +from twisted.cred import portal +from twisted.cred.checkers import InMemoryUsernamePasswordDatabaseDontUse +from twisted.internet import protocol, reactor +from twisted.python import components, log + log.startLogging(sys.stderr) """ @@ -45,11 +47,11 @@ """ # Path to RSA SSH keys used by the server. -SERVER_RSA_PRIVATE = 'ssh-keys/ssh_host_rsa_key' -SERVER_RSA_PUBLIC = 'ssh-keys/ssh_host_rsa_key.pub' +SERVER_RSA_PRIVATE = "ssh-keys/ssh_host_rsa_key" +SERVER_RSA_PUBLIC = "ssh-keys/ssh_host_rsa_key.pub" # Path to RSA SSH keys accepted by the server. -CLIENT_RSA_PUBLIC = 'ssh-keys/client_rsa.pub' +CLIENT_RSA_PUBLIC = "ssh-keys/client_rsa.pub" # Pre-computed big prime numbers used in Diffie-Hellman Group Exchange as @@ -82,10 +84,53 @@ # Avoid 1024 bit primes https://weakdh.org # PRIMES = { - 2048: [(2, 24265446577633846575813468889658944748236936003103970778683933705240497295505367703330163384138799145013634794444597785054574812547990300691956176233759905976222978197624337271745471021764463536913188381724789737057413943758936963945487690939921001501857793275011598975080236860899147312097967655185795176036941141834185923290769258512343298744828216530595090471970401506268976911907264143910697166165795972459622410274890288999065530463691697692913935201628660686422182978481412651196163930383232742547281180277809475129220288755541335335798837173315854931040199943445285443708240639743407396610839820418936574217939)], - 4096: [(2, 889633836007296066695655481732069270550615298858522362356462966213994239650370532015908457586090329628589149803446849742862797136176274424808060302038380613106889959709419621954145635974564549892775660764058259799708313210328185716628794220535928019146593583870799700485371067763221569331286080322409646297706526831155237865417316423347898948704639476720848300063714856669054591377356454148165856508207919637875509861384449885655015865507939009502778968273879766962650318328175030623861285062331536562421699321671967257712201155508206384317725827233614202768771922547552398179887571989441353862786163421248709273143039795776049771538894478454203924099450796009937772259125621285287516787494652132525370682385152735699722849980820612370907638783461523042813880757771177423192559299945620284730833939896871200164312605489165789501830061187517738930123242873304901483476323853308396428713114053429620808491032573674192385488925866607192870249619437027459456991431298313382204980988971292641217854130156830941801474940667736066881036980286520892090232096545650051755799297658390763820738295370567143697617670291263734710392873823956589171067167839738896249891955689437111486748587887718882564384870583135509339695096218451174112035938859)], - } - + 2048: [ + ( + 2, + int( + "2426544657763384657581346888965894474823693600310397077868393" + "3705240497295505367703330163384138799145013634794444597785054" + "5748125479903006919561762337599059762229781976243372717454710" + "2176446353691318838172478973705741394375893696394548769093992" + "1001501857793275011598975080236860899147312097967655185795176" + "0369411418341859232907692585123432987448282165305950904719704" + "0150626897691190726414391069716616579597245962241027489028899" + "9065530463691697692913935201628660686422182978481412651196163" + "9303832327425472811802778094751292202887555413353357988371733" + "1585493104019994344528544370824063974340739661083982041893657" + "4217939" + ), + ) + ], + 4096: [ + ( + 2, + int( + "8896338360072960666956554817320692705506152988585223623564629" + "6621399423965037053201590845758609032962858914980344684974286" + "2797136176274424808060302038380613106889959709419621954145635" + "9745645498927756607640582597997083132103281857166287942205359" + "2801914659358387079970048537106776322156933128608032240964629" + "7706526831155237865417316423347898948704639476720848300063714" + "8566690545913773564541481658565082079196378755098613844498856" + "5501586550793900950277896827387976696265031832817503062386128" + "5062331536562421699321671967257712201155508206384317725827233" + "6142027687719225475523981798875719894413538627861634212487092" + "7314303979577604977153889447845420392409945079600993777225912" + "5621285287516787494652132525370682385152735699722849980820612" + "3709076387834615230428138807577711774231925592999456202847308" + "3393989687120016431260548916578950183006118751773893012324287" + "3304901483476323853308396428713114053429620808491032573674192" + "3854889258666071928702496194370274594569914312983133822049809" + "8897129264121785413015683094180147494066773606688103698028652" + "0892090232096545650051755799297658390763820738295370567143697" + "6176702912637347103928738239565891710671678397388962498919556" + "8943711148674858788771888256438487058313550933969509621845117" + "4112035938859" + ), + ) + ], +} class ExampleAvatar(avatar.ConchUser): @@ -96,20 +141,21 @@ This account will use L{session.SSHSession} to handle a channel of type I{session}. """ + def __init__(self, username): avatar.ConchUser.__init__(self) self.username = username - self.channelLookup.update({b'session':session.SSHSession}) - + self.channelLookup.update({b"session": session.SSHSession}) @implementer(portal.IRealm) -class ExampleRealm(object): +class ExampleRealm: """ When using Twisted Cred, the pluggable authentication framework, the C{requestAvatar} method should return a L{avatar.ConchUser} instance as required by the Conch SSH server. """ + def requestAvatar(self, avatarId, mind, *interfaces): """ See: L{portal.IRealm.requestAvatar} @@ -117,7 +163,6 @@ return interfaces[0], ExampleAvatar(avatarId), lambda: None - class EchoProtocol(protocol.Protocol): """ This is our protocol that we will run over the shell session. @@ -130,16 +175,16 @@ Just echo the received data and and if Ctrl+C is received, close the session. """ - if data == b'\r': - data = b'\r\n' - elif data == b'\x03': #^C + if data == b"\r": + data = b"\r\n" + elif data == b"\x03": # ^C self.transport.loseConnection() return self.transport.write(data) - -class ExampleSession(object): +@implementer(session.ISession, session.ISessionSetEnv) +class ExampleSession: """ This selects what to do for each type of session which is requested by the client via the SSH channel of type I{session}. @@ -152,12 +197,15 @@ only to specific accounts. """ - def getPty(self, term, windowSize, attrs): """ We don't support pseudo-terminal sessions. """ + def setEnv(self, name, value): + """ + We don't support setting environment variables. + """ def execCommand(self, proto, cmd): """ @@ -182,9 +230,9 @@ pass - -components.registerAdapter(ExampleSession, ExampleAvatar, session.ISession) - +components.registerAdapter( + ExampleSession, ExampleAvatar, session.ISession, session.ISessionSetEnv +) class ExampleFactory(factory.SSHFactory): @@ -201,22 +249,26 @@ * L{userauth.SSHUserAuthServer} handlers requests for the user authentication service. """ + protocol = SSHServerTransport # Server's host keys. # To simplify the example this server is defined only with a host key of # type RSA. - publicKeys = { - b'ssh-rsa': keys.Key.fromFile(SERVER_RSA_PUBLIC) - } - privateKeys = { - b'ssh-rsa': keys.Key.fromFile(SERVER_RSA_PRIVATE) - } + publicKeys = {b"ssh-rsa": keys.Key.fromFile(SERVER_RSA_PUBLIC)} + privateKeys = {b"ssh-rsa": keys.Key.fromFile(SERVER_RSA_PRIVATE)} # Service handlers. services = { - b'ssh-userauth': userauth.SSHUserAuthServer, - b'ssh-connection': connection.SSHConnection + b"ssh-userauth": userauth.SSHUserAuthServer, + b"ssh-connection": connection.SSHConnection, } + def __init__(self): + passwdDB = InMemoryUsernamePasswordDatabaseDontUse(user="password") + sshDB = SSHPublicKeyChecker( + InMemorySSHKeyDB({b"user": [keys.Key.fromFile(CLIENT_RSA_PUBLIC)]}) + ) + self.portal = portal.Portal(ExampleRealm(), [passwdDB, sshDB]) + def getPrimes(self): """ See: L{factory.SSHFactory} @@ -224,15 +276,6 @@ return PRIMES -portal = portal.Portal(ExampleRealm()) -passwdDB = InMemoryUsernamePasswordDatabaseDontUse() -passwdDB.addUser(b'user', b'password') -sshDB = SSHPublicKeyChecker(InMemorySSHKeyDB( - {b'user': [keys.Key.fromFile(CLIENT_RSA_PUBLIC)]})) -portal.registerChecker(passwdDB) -portal.registerChecker(sshDB) -ExampleFactory.portal = portal - -if __name__ == '__main__': +if __name__ == "__main__": reactor.listenTCP(5022, ExampleFactory()) reactor.run() diff -Nru twisted-20.3.0/docs/conch/examples/telnet_echo.tac twisted-22.1.0/docs/conch/examples/telnet_echo.tac --- twisted-20.3.0/docs/conch/examples/telnet_echo.tac 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/conch/examples/telnet_echo.tac 2022-02-07 13:12:15.000000000 +0000 @@ -11,32 +11,33 @@ No login for the telnet server is required. """ -from twisted.conch.telnet import TelnetTransport, TelnetProtocol -from twisted.internet.protocol import ServerFactory from twisted.application.internet import TCPServer from twisted.application.service import Application +from twisted.conch.telnet import TelnetProtocol, TelnetTransport +from twisted.internet.protocol import ServerFactory + class TelnetEcho(TelnetProtocol): def enableRemote(self, option): self.transport.write(b"You tried to enable " + option + b" (I rejected it)\r\n") return False - def disableRemote(self, option): self.transport.write(b"You disabled " + option + b"\r\n") - def enableLocal(self, option): - self.transport.write(b"You tried to make me enable " + option + b" (I rejected it)\r\n" % (option,)) + self.transport.write( + b"You tried to make me enable " + + option + + b" (I rejected it)\r\n" % (option,) + ) return False - def disableLocal(self, option): self.transport.write(b"You asked me to disable " + option + "\r\n") - def dataReceived(self, data): - self.transport.write(b"I received "+ data + b" from you\r\n") + self.transport.write(b"I received " + data + b" from you\r\n") factory = ServerFactory() diff -Nru twisted-20.3.0/docs/conch/examples/window.tac twisted-22.1.0/docs/conch/examples/window.tac --- twisted-20.3.0/docs/conch/examples/window.tac 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/conch/examples/window.tac 2022-02-07 13:12:15.000000000 +0000 @@ -25,22 +25,20 @@ "password" is the password. """ -from __future__ import division -import string, random +import random +import string from twisted.application import internet, service - from twisted.conch.insults import insults, window from twisted.conch.manhole_ssh import ConchFactory, TerminalRealm from twisted.conch.ssh import keys -from twisted.conch.telnet import TelnetTransport, TelnetBootstrapProtocol +from twisted.conch.telnet import TelnetBootstrapProtocol, TelnetTransport from twisted.cred import checkers, portal from twisted.internet import protocol, reactor, task from twisted.python import log - class DrawableCanvas(window.Canvas): x = 0 y = 0 @@ -67,7 +65,7 @@ self.repaint() def keystrokeReceived(self, keyID, modifier): - if keyID == b'\r' or keyID == b'\v': + if keyID == b"\r" or keyID == b"\v": return window.Canvas.keystrokeReceived(self, keyID, modifier) if self.x >= self.width: @@ -129,14 +127,16 @@ t3 = window.TextOutputArea(longLines=window.TextOutputArea.TRUNCATE) t4 = window.TextOutputArea(longLines=window.TextOutputArea.TRUNCATE) for _t in t1, t2, t3, t4: - _t.setText(((b'This is a very long string. ' * 3) + b'\n') * 3) + _t.setText(((b"This is a very long string. " * 3) + b"\n") * 3) vp = window.Viewport(t3) d = [1] + def spin(): vp.xOffset += d[0] if vp.xOffset == 0 or vp.xOffset == 25: d[0] *= -1 + self.call = task.LoopingCall(spin) self.call.start(0.25, now=False) hbox.addChild(window.Border(vp)) @@ -168,7 +168,6 @@ self.terminal.eraseDisplay() self._redraw() - def keystrokeReceived(self, keyID, modifier): self.window.keystrokeReceived(keyID, modifier) @@ -176,7 +175,7 @@ self.canvas.clear() def _setText(self, text): - self.input.setText(b'') + self.input.setText(b"") self.output.setText(text) @@ -184,18 +183,21 @@ checker = checkers.InMemoryUsernamePasswordDatabaseDontUse(username=b"password") f = protocol.ServerFactory() - f.protocol = lambda: TelnetTransport(TelnetBootstrapProtocol, - insults.ServerProtocol, - args['protocolFactory'], - *args.get('protocolArgs', ()), - **args.get('protocolKwArgs', {})) - tsvc = internet.TCPServer(args['telnet'], f) + f.protocol = lambda: TelnetTransport( + TelnetBootstrapProtocol, + insults.ServerProtocol, + args["protocolFactory"], + *args.get("protocolArgs", ()), + **args.get("protocolKwArgs", {}), + ) + tsvc = internet.TCPServer(args["telnet"], f) def chainProtocolFactory(): return insults.ServerProtocol( - args['protocolFactory'], - *args.get('protocolArgs', ()), - **args.get('protocolKwArgs', {})) + args["protocolFactory"], + *args.get("protocolArgs", ()), + **args.get("protocolKwArgs", {}), + ) rlm = TerminalRealm() rlm.chainedProtocolFactory = chainProtocolFactory @@ -203,15 +205,16 @@ f = ConchFactory(ptl) f.publicKeys[b"ssh-rsa"] = keys.Key.fromFile("ssh-keys/ssh_host_rsa_key.pub") f.privateKeys[b"ssh-rsa"] = keys.Key.fromFile("ssh-keys/ssh_host_rsa_key") - csvc = internet.TCPServer(args['ssh'], f) + csvc = internet.TCPServer(args["ssh"], f) m = service.MultiService() tsvc.setServiceParent(m) csvc.setServiceParent(m) return m + application = service.Application("Window Demo") -makeService({'protocolFactory': ButtonDemo, - 'telnet': 6023, - 'ssh': 6022}).setServiceParent(application) +makeService( + {"protocolFactory": ButtonDemo, "telnet": 6023, "ssh": 6022} +).setServiceParent(application) diff -Nru twisted-20.3.0/docs/conch/howto/conch_client.rst twisted-22.1.0/docs/conch/howto/conch_client.rst --- twisted-20.3.0/docs/conch/howto/conch_client.rst 2019-06-15 21:59:27.000000000 +0000 +++ twisted-22.1.0/docs/conch/howto/conch_client.rst 2022-02-07 13:12:15.000000000 +0000 @@ -34,7 +34,7 @@ If your objective is to execute a command on a remote host over an SSH connection, then the easiest approach may be to -use :api:`twisted.conch.endpoints.SSHCommandClientEndpoint ` . +use :py:class:`twisted.conch.endpoints.SSHCommandClientEndpoint` . If you haven't used endpoints before, first take a look at :doc:`the endpoint howto <../../core/howto/endpoints>` to get an idea of how endpoints work in general. @@ -134,7 +134,7 @@ -The ``keys`` argument gives any SSH :api:`twisted.conch.ssh.keys.Key ` objects which may be useful for authentication. +The ``keys`` argument gives any SSH :py:class:`Key ` objects which may be useful for authentication. These keys are available to the endpoint for authentication, but only keys that the server indicates are useful will actually be used. This argument is optional. If key authentication against the server is either unnecessary or undesired, it may be omitted entirely. @@ -156,7 +156,7 @@ -The ``knownHosts`` argument accepts a :api:`twisted.conch.client.knownhosts.KnownHostsFile ` instance and controls how server keys are checked and stored. +The ``knownHosts`` argument accepts a :py:class:`KnownHostsFile ` instance and controls how server keys are checked and stored. This object has the opportunity to reject server keys if they differ from expectations. It can also save server keys when they are first observed. @@ -169,9 +169,9 @@ This argument is closely related to the ``knownHosts`` argument described above. ``KnownHostsFile`` may require user-input under certain circumstances - for example, to ask if it should accept a server key the first time it is observed. The ``ui`` object is how this user-input is obtained. -By default, a :api:`twisted.conch.client.knownhosts.ConsoleUI ` instance associated with */dev/tty* will be used. +By default, a :py:class:`ConsoleUI ` instance associated with */dev/tty* will be used. This gives about the same behavior as is seen in a standard command-line ssh client. -See :api:`twisted.conch.endpoints.SSHCommandClientEndpoint.newConnection ` for details about how edge cases are handled for this default value. +See :py:meth:`SSHCommandClientEndpoint.newConnection ` for details about how edge cases are handled for this default value. For use of ``SSHCommandClientEndpoint`` that is intended to be completely autonomous, applications will probably want to specify a custom ``ui`` object which can make the necessary decisions without user-input. @@ -210,7 +210,7 @@ -Writing a client with Conch involves sub-classing 4 classes: :api:`twisted.conch.ssh.transport.SSHClientTransport ` , :api:`twisted.conch.ssh.userauth.SSHUserAuthClient ` , :api:`twisted.conch.ssh.connection.SSHConnection ` , and :api:`twisted.conch.ssh.channel.SSHChannel ` . We'll start out +Writing a client with Conch involves sub-classing 4 classes: :py:class:`twisted.conch.ssh.transport.SSHClientTransport` , :py:class:`twisted.conch.ssh.userauth.SSHUserAuthClient` , :py:class:`twisted.conch.ssh.connection.SSHConnection` , and :py:class:`twisted.conch.ssh.channel.SSHChannel` . We'll start out with ``SSHClientTransport`` because it's the base of the client. @@ -252,7 +252,7 @@ is called with two strings: the public key sent by the server and its fingerprint. You should verify the host key the server sends, either by checking against a hard-coded value as in the example, or by asking -the user. ``verifyHostKey`` returns a :api:`twisted.internet.defer.Deferred ` which gets a callback +the user. ``verifyHostKey`` returns a :py:class:`twisted.internet.defer.Deferred` which gets a callback if the host key is valid, or an errback if it is not. Note that in the above, replace 'user' with the username you're attempting to ssh with, for instance a call to ``os.getlogin()`` for the @@ -324,11 +324,11 @@ the password to use. ``getPublicKey()`` returns the SSH key data for the public key to use. -:api:`Key ` will take a key in OpenSSH, LSH or any supported format, as a string, and generate a new :api:`Key `. +:py:meth:`Key.fromString() ` will take a key in OpenSSH, LSH or any supported format, as a string, and generate a new :py:class:`Key `. Alternatively, ``keys.Key.fromFile()`` can be used instead, which -will take the filename of a key in the supported format, and and generate a new :api:`Key `. +will take the filename of a key in the supported format, and and generate a new :py:class:`Key `. -``getPrivateKey()`` returns a ``Deferred`` which is called back with the private :api:`Key `. +``getPrivateKey()`` returns a ``Deferred`` which is called back with the private :py:class:`Key `. ``getPassword()`` and ``getPrivateKey()`` return ``Deferreds`` because they may need to ask the user for input. @@ -418,7 +418,7 @@ events to the other side. We pass the method self so that it knows to send the request for this channel. The 2nd argument of 'exec' tells the server that we want to execute a command. The third argument is the data -that accompanies the request. :api:`twisted.conch.ssh.common.NS ` encodes +that accompanies the request. :py:func:`common.NS ` encodes the data as a length-prefixed string, which is how the server expects the data. We also say that we want a reply saying that the process has a been started. ``sendRequest()`` then returns a ``Deferred`` which we add a callback for. @@ -426,7 +426,7 @@ -Once the callback fires, we send the data. ``SSHChannel`` supports the :api:`twisted.internet.interfaces.ITransport ` +Once the callback fires, we send the data. ``SSHChannel`` supports the :py:class:`twisted.internet.interfaces.ITransport` interface, so it can be given to Protocols to run them over the secure connection. In our case, we just write the data directly. ``sendEOF()`` does not follow the interface, @@ -470,7 +470,7 @@ We call ``connectTCP()`` to connect to localhost, port 22 (the standard port for ssh), and pass it an instance -of :api:`twisted.internet.protocol.ClientFactory ` . +of :py:class:`twisted.internet.protocol.ClientFactory` . This instance has the attribute ``protocol`` set to our earlier ``ClientTransport`` class. Note that the protocol attribute is set to the class ``ClientTransport`` , not an instance of ``ClientTransport`` ! When the ``connectTCP`` call completes, the protocol will be diff -Nru twisted-20.3.0/docs/conch/howto/listings/echoclient_shared_ssh.py twisted-22.1.0/docs/conch/howto/listings/echoclient_shared_ssh.py --- twisted-20.3.0/docs/conch/howto/listings/echoclient_shared_ssh.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/conch/howto/listings/echoclient_shared_ssh.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,34 +1,33 @@ #!/usr/bin/env python # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. -from __future__ import print_function -if __name__ == '__main__': +if __name__ == "__main__": import sys + import echoclient_shared_ssh + from twisted.internet.task import react + react(echoclient_shared_ssh.main, sys.argv[1:]) -from twisted.internet.task import cooperate -from twisted.internet.defer import Deferred, gatherResults -from twisted.internet.protocol import Factory, Protocol +from echoclient_ssh import ConnectionParameters from twisted.conch.endpoints import SSHCommandClientEndpoint - -from echoclient_ssh import ConnectionParameters +from twisted.internet.defer import Deferred, gatherResults +from twisted.internet.protocol import Factory, Protocol +from twisted.internet.task import cooperate class PrinterProtocol(Protocol): def dataReceived(self, data): - print("Got some data:", data, end=' ') - + print("Got some data:", data, end=" ") def connectionLost(self, reason): print("Lost my connection") self.factory.done.callback(None) - def main(reactor, *argv): parameters = ConnectionParameters.fromCommandLine(reactor, argv) endpoint = parameters.endpointForCommand(b"/bin/cat") @@ -48,7 +47,8 @@ done.append(factory.done) e = SSHCommandClientEndpoint.existingConnection( - conn, b"/bin/echo %d" % (i,)) + conn, b"/bin/echo %d" % (i,) + ) yield e.connect(factory) d.addCallback(gotConnection) diff -Nru twisted-20.3.0/docs/conch/howto/listings/echoclient_ssh.py twisted-22.1.0/docs/conch/howto/listings/echoclient_ssh.py --- twisted-20.3.0/docs/conch/howto/listings/echoclient_ssh.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/conch/howto/listings/echoclient_ssh.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,46 +1,57 @@ #!/usr/bin/env python # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. -from __future__ import print_function -if __name__ == '__main__': +if __name__ == "__main__": import sys + import echoclient_ssh + from twisted.internet.task import react + react(echoclient_ssh.main, sys.argv[1:]) -import os, getpass +import getpass +import os -from twisted.python.filepath import FilePath -from twisted.python.usage import Options -from twisted.internet.defer import Deferred -from twisted.internet.protocol import Factory, Protocol -from twisted.internet.endpoints import UNIXClientEndpoint -from twisted.conch.ssh.keys import EncryptedKeyError, Key from twisted.conch.client.knownhosts import KnownHostsFile from twisted.conch.endpoints import SSHCommandClientEndpoint +from twisted.conch.ssh.keys import EncryptedKeyError, Key +from twisted.internet.defer import Deferred +from twisted.internet.endpoints import UNIXClientEndpoint +from twisted.internet.protocol import Factory, Protocol +from twisted.python.filepath import FilePath +from twisted.python.usage import Options class EchoOptions(Options): optParameters = [ - ("host", "h", "localhost", - "hostname of the SSH server to which to connect"), - ("port", "p", 22, - "port number of SSH server to which to connect", int), - ("username", "u", getpass.getuser(), - "username with which to authenticate with the SSH server"), - ("identity", "i", None, - "file from which to read a private key to use for authentication"), - ("password", None, None, - "password to use for authentication"), - ("knownhosts", "k", "~/.ssh/known_hosts", - "file containing known ssh server public key data"), - ] + ("host", "h", "localhost", "hostname of the SSH server to which to connect"), + ("port", "p", 22, "port number of SSH server to which to connect", int), + ( + "username", + "u", + getpass.getuser(), + "username with which to authenticate with the SSH server", + ), + ( + "identity", + "i", + None, + "file from which to read a private key to use for authentication", + ), + ("password", None, None, "password to use for authentication"), + ( + "knownhosts", + "k", + "~/.ssh/known_hosts", + "file containing known ssh server public key data", + ), + ] optFlags = [ ["no-agent", None, "Disable use of key agent"], - ] - + ] class NoiseProtocol(Protocol): @@ -49,36 +60,32 @@ self.strings = ["bif", "pow", "zot"] self.sendNoise() - def sendNoise(self): if self.strings: self.transport.write(self.strings.pop(0) + "\n") else: self.transport.loseConnection() - def dataReceived(self, data): print("Server says:", data) self.sendNoise() - def connectionLost(self, reason): self.finished.callback(None) - def readKey(path): try: return Key.fromFile(path) except EncryptedKeyError: - passphrase = getpass.getpass("%r keyphrase: " % (path,)) + passphrase = getpass.getpass(f"{path!r} keyphrase: ") return Key.fromFile(path, passphrase=passphrase) - -class ConnectionParameters(object): - def __init__(self, reactor, host, port, username, password, keys, - knownHosts, agent): +class ConnectionParameters: + def __init__( + self, reactor, host, port, username, password, keys, knownHosts, agent + ): self.reactor = reactor self.host = host self.port = port @@ -88,7 +95,6 @@ self.knownHosts = knownHosts self.agent = agent - @classmethod def fromCommandLine(cls, reactor, argv): config = EchoOptions() @@ -109,21 +115,31 @@ if config["no-agent"] or "SSH_AUTH_SOCK" not in os.environ: agentEndpoint = None else: - agentEndpoint = UNIXClientEndpoint( - reactor, os.environ["SSH_AUTH_SOCK"]) + agentEndpoint = UNIXClientEndpoint(reactor, os.environ["SSH_AUTH_SOCK"]) return cls( - reactor, config["host"], config["port"], - config["username"], config["password"], keys, - knownHosts, agentEndpoint) - + reactor, + config["host"], + config["port"], + config["username"], + config["password"], + keys, + knownHosts, + agentEndpoint, + ) def endpointForCommand(self, command): return SSHCommandClientEndpoint.newConnection( - self.reactor, command, self.username, self.host, - port=self.port, keys=self.keys, password=self.password, - agentEndpoint=self.agent, knownHosts=self.knownHosts) - + self.reactor, + command, + self.username, + self.host, + port=self.port, + keys=self.keys, + password=self.password, + agentEndpoint=self.agent, + knownHosts=self.knownHosts, + ) def main(reactor, *argv): diff -Nru twisted-20.3.0/docs/conch/man/cftp.1 twisted-22.1.0/docs/conch/man/cftp.1 --- twisted-20.3.0/docs/conch/man/cftp.1 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/conch/man/cftp.1 2022-02-07 13:12:15.000000000 +0000 @@ -80,9 +80,9 @@ .Sh AUTHOR cftp by Paul Swartz . Man page by Mary Gardiner . .Sh "REPORTING BUGS" -Report bugs to \fIhttp://twistedmatrix.com/trac/\fR +Report bugs to \fIhttps://twistedmatrix.com/trac/\fR .Sh COPYRIGHT -Copyright \(co 2005-2008 Twisted Matrix Laboratories +Copyright \(co 2005-2020 Twisted Matrix Laboratories .br This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. diff -Nru twisted-20.3.0/docs/conch/man/ckeygen.1 twisted-22.1.0/docs/conch/man/ckeygen.1 --- twisted-20.3.0/docs/conch/man/ckeygen.1 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/conch/man/ckeygen.1 2022-02-07 13:12:15.000000000 +0000 @@ -50,9 +50,9 @@ .SH AUTHOR Written by Moshe Zadka, based on ckeygen's help messages .SH "REPORTING BUGS" -To report a bug, visit \fIhttp://twistedmatrix.com/trac/\fR +To report a bug, visit \fIhttps://twistedmatrix.com/trac/\fR .SH COPYRIGHT -Copyright \(co 2002-2011 Twisted Matrix Laboratories. +Copyright \(co 2002-2020 Twisted Matrix Laboratories. .br This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. diff -Nru twisted-20.3.0/docs/conch/man/conch.1 twisted-22.1.0/docs/conch/man/conch.1 --- twisted-20.3.0/docs/conch/man/conch.1 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/conch/man/conch.1 2022-02-07 13:12:15.000000000 +0000 @@ -196,9 +196,9 @@ .Sh AUTHOR Written by Paul Swartz . .Sh "REPORTING BUGS" -To report a bug, visit \fIhttp://twistedmatrix.com/trac/\fR +To report a bug, visit \fIhttps://twistedmatrix.com/trac/\fR .Sh COPYRIGHT -Copyright \(co 2002-2008 Twisted Matrix Laboratories. +Copyright \(co 2002-2020 Twisted Matrix Laboratories. .br This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. diff -Nru twisted-20.3.0/docs/conch/man/tkconch.1 twisted-22.1.0/docs/conch/man/tkconch.1 --- twisted-20.3.0/docs/conch/man/tkconch.1 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/conch/man/tkconch.1 2022-02-07 13:12:15.000000000 +0000 @@ -62,9 +62,9 @@ .SH AUTHOR Written by Moshe Zadka, based on conch's help messages .SH "REPORTING BUGS" -To report a bug, visit \fIhttp://twistedmatrix.com/trac/\fR +To report a bug, visit \fIhttps://twistedmatrix.com/trac/\fR .SH COPYRIGHT -Copyright \(co 2002-2008 Twisted Matrix Laboratories. +Copyright \(co 2002-2020 Twisted Matrix Laboratories. .br This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. diff -Nru twisted-20.3.0/docs/conf.py twisted-22.1.0/docs/conf.py --- twisted-20.3.0/docs/conf.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/conf.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Twisted documentation build configuration file, created by # sphinx-quickstart on Tue Jan 14 11:31:15 2014. @@ -12,246 +11,146 @@ # All configuration values have a default; values that are commented out # serve to show the default. -import sys import os +import pathlib +import subprocess +import sys +from datetime import date +from pprint import pprint # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. -sys.path.insert(0, os.path.abspath('./_extensions')) -sys.path.insert(0, os.path.abspath('..')) +sys.path.insert(0, os.path.abspath("./_extensions")) +sys.path.insert(0, os.path.abspath("..")) # -- General configuration ------------------------------------------------ -# If your documentation needs a minimal Sphinx version, state it here. -needs_sphinx = '1.2' +# See setup.cfg for the Sphinx version required to build the documentation. +# needs_sphinx is not use to avoid duplication and getting these values +# out of sync. # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ "sphinx.ext.intersphinx", + "pydoctor.sphinx_ext.build_apidocs", ] try: import rst2pdf.pdfbuilder - extensions.append('rst2pdf.pdfbuilder') + + extensions.append("rst2pdf.pdfbuilder") except ImportError: pass -extensions.append('apilinks') -extensions.append('traclinks') +extensions.append("traclinks") from twisted import version as twisted_version_object - # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # The suffix of source filenames. -source_suffix = '.rst' +source_suffix = ".rst" # The encoding of source files. -#source_encoding = 'utf-8-sig' +# source_encoding = 'utf-8-sig' # The master toctree document. -master_doc = 'index' +master_doc = "index" +_today = date.today() # General information about the project. -project = u'Twisted' -copyright = u'2017, Twisted Matrix Labs' +project = "Twisted" +copyright = "{}, Twisted Matrix Labs. Ver {}. Built on {}".format( + _today.year, + twisted_version_object.public(), + _today.isoformat(), +) # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. -version = "{major}.{minor}".format(major=twisted_version_object.major, - minor=twisted_version_object.minor) +version = "{major}.{minor}".format( + major=twisted_version_object.major, minor=twisted_version_object.minor +) # The full version, including alpha/beta/rc tags. release = twisted_version_object.short() -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -#language = None - # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: -#today = '' +# today = '' # Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' +# today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. -exclude_patterns = ['_build'] - -# The reST default role (used for this markup: `text`) to use for all -# documents. -#default_role = None - -# If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True - -# If true, the current module name will be prepended to all description -# unit titles (such as .. function::). -#add_module_names = True - -# If true, sectionauthor and moduleauthor directives will be shown in the -# output. They are ignored by default. -#show_authors = False +exclude_patterns = ["_build"] # The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' - -# A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] - -# If true, keep warnings as "system message" paragraphs in the built documents. -#keep_warnings = False - +pygments_style = "sphinx" # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -on_rtd = os.environ.get('READTHEDOCS', None) == 'True' +# See Read The Docs environment variables +# https://docs.readthedocs.io/en/stable/builds.html#build-environment +on_rtd = os.environ.get("READTHEDOCS", None) == "True" if not on_rtd: - html_theme = 'twistedtrac' - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -#html_theme_options = {} + html_theme = "twistedtrac" # Add any paths that contain custom themes here, relative to this directory. -html_theme_path = ['_themes'] - -# The name for this set of Sphinx documents. If None, it defaults to -# " v documentation". -#html_title = None - -# A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None - -# The name of an image file (relative to this directory) to place at the top -# of the sidebar. -#html_logo = None - -# The name of an image file (within the static path) to use as favicon of the -# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 -# pixels large. -#html_favicon = None +html_theme_path = ["_themes"] # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] - -# Add any extra paths that contain custom files (such as robots.txt or -# .htaccess) here, relative to this directory. These files are copied -# directly to the root of the documentation. -#html_extra_path = [] - -# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, -# using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' - -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -#html_use_smartypants = True +html_static_path = ["_static"] -# Custom sidebar templates, maps document names to template names. -#html_sidebars = {} -# Additional templates that should be rendered to pages, maps page names to -# template names. -#html_additional_pages = {} - -# If false, no module index is generated. -#html_domain_indices = True - -# If false, no index is generated. -#html_use_index = True - -# If true, the index is split into individual pages for each letter. -#html_split_index = False - -# If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True - -# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True - -# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -#html_show_copyright = True - -# If true, an OpenSearch description file will be output, and all pages will -# contain a tag referring to it. The value of this option must be the -# base URL from which the finished HTML is served. -#html_use_opensearch = '' - -# This is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = None +# These paths are either relative to html_static_path +# or fully qualified paths (eg. https://...) +html_js_files = [ + "js/custom.js", + # Here we have a Sphinx HTML injection hack to make the JS script load without blocking. + 'https://sidecar.gitter.im/dist/sidecar.v1.js" defer hack="', +] # Output file base name for HTML help builder. -htmlhelp_basename = 'Twisteddoc' +htmlhelp_basename = "Twisteddoc" # -- Options for LaTeX output --------------------------------------------- latex_elements = { -# The paper size ('letterpaper' or 'a4paper'). -#'papersize': 'letterpaper', - -# The font size ('10pt', '11pt' or '12pt'). -#'pointsize': '10pt', - -# Additional stuff for the LaTeX preamble. -#'preamble': '', + # The paper size ('letterpaper' or 'a4paper'). + #'papersize': 'letterpaper', + # The font size ('10pt', '11pt' or '12pt'). + #'pointsize': '10pt', + # Additional stuff for the LaTeX preamble. + #'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - ('index', 'Twisted.tex', u'Twisted Documentation', - u'Twisted Matrix Labs', 'manual'), + ("index", "Twisted.tex", "Twisted Documentation", "Twisted Matrix Labs", "manual"), ] -# The name of an image file (relative to this directory) to place at the top of -# the title page. -#latex_logo = None - -# For "manual" documents, if this is true, then toplevel headings are parts, -# not chapters. -#latex_use_parts = False - -# If true, show page references after internal links. -#latex_show_pagerefs = False - -# If true, show URL addresses after external links. -#latex_show_urls = False - -# Documents to append as an appendix to all manuals. -#latex_appendices = [] - -# If false, no module index is generated. -#latex_domain_indices = True - # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). -man_pages = [ - ('index', 'twisted', u'Twisted Documentation', - [u'Twisted Matrix Labs'], 1) -] - -# If true, show URL addresses after external links. -#man_show_urls = False +man_pages = [("index", "twisted", "Twisted Documentation", ["Twisted Matrix Labs"], 1)] # -- Options for Texinfo output ------------------------------------------- @@ -260,108 +159,104 @@ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - ('index', 'Twisted', u'Twisted Documentation', - u'Twisted Matrix Labs', 'Twisted', 'One line description of project.', - 'Miscellaneous'), + ( + "index", + "Twisted", + "Twisted Documentation", + "Twisted Matrix Labs", + "Twisted", + "One line description of project.", + "Miscellaneous", + ), ] -# Documents to append as an appendix to all manuals. -#texinfo_appendices = [] - -# If false, no module index is generated. -#texinfo_domain_indices = True - -# How to display URL addresses: 'footnote', 'no', or 'inline'. -#texinfo_show_urls = 'footnote' - -# If true, do not generate a @detailmenu in the "Top" node's menu. -#texinfo_no_detailmenu = False - # -- Options for Epub output ---------------------------------------------- # Bibliographic Dublin Core info. -epub_title = u'Twisted' -epub_author = u'Twisted Matrix Labs' -epub_publisher = u'Twisted Matrix Labs' -epub_copyright = u'2014, Twisted Matrix Labs' - -# The basename for the epub file. It defaults to the project name. -#epub_basename = u'Twisted' - -# The HTML theme for the epub output. Since the default themes are not optimized -# for small screen space, using the same theme for HTML and epub output is -# usually not wise. This defaults to 'epub', a theme designed to save visual -# space. -#epub_theme = 'epub' - -# The language of the text. It defaults to the language option -# or en if the language is not set. -#epub_language = '' - -# The scheme of the identifier. Typical schemes are ISBN or URL. -#epub_scheme = '' - -# The unique identifier of the text. This can be an ISBN number -# or the project homepage. -#epub_identifier = '' +epub_title = "Twisted" +epub_author = "Twisted Matrix Labs" +epub_publisher = "Twisted Matrix Labs" +epub_copyright = "2020, Twisted Matrix Labs" -# A unique identification for the text. -#epub_uid = '' -# A tuple containing the cover image and cover page html template filenames. -#epub_cover = () - -# A sequence of (type, uri, title) tuples for the guide element of content.opf. -#epub_guide = () - -# HTML files that should be inserted before the pages created by sphinx. -# The format is a list of tuples containing the path and title. -#epub_pre_files = [] - -# HTML files shat should be inserted after the pages created by sphinx. -# The format is a list of tuples containing the path and title. -#epub_post_files = [] - -# A list of files that should not be packed into the epub file. -#epub_exclude_files = [] - -# The depth of the table of contents in toc.ncx. -#epub_tocdepth = 3 - -# Allow duplicate toc entries. -#epub_tocdup = True - -# Choose between 'default' and 'includehidden'. -#epub_tocscope = 'default' - -# Fix unsupported image types using the PIL. -#epub_fix_images = False - -# Scale large images. -#epub_max_image_width = 0 - -# How to display URL addresses: 'footnote', 'no', or 'inline'. -#epub_show_urls = 'inline' - -# If false, no index is generated. -#epub_use_index = True +# -- Extension configuration ---------------------------------------------- +_git_reference = subprocess.run( + ["git", "rev-parse", "--abbrev-ref", "HEAD"], + text=True, + encoding="utf8", + capture_output=True, + check=True, +).stdout + + +print(f"== Environment dump for {_git_reference} ===") +pprint(dict(os.environ)) +print("======") + +# Base url for API docs +api_base_url = f"/documents/{release}/api/" +if on_rtd: + # For a PR the link is like: + # https://twisted--1422.org.readthedocs.build/en/1422/ + # For a release: + # https://docs.twistedmatrix.com/en/twisted-20.3.0/ + # https://docs.twistedmatrix.com/en/latest/ + api_base_url = "/{}/{}/api/".format( + os.environ["READTHEDOCS_LANGUAGE"], + os.environ["READTHEDOCS_VERSION"], + ) + +# Try to find URL fragment for the GitHub source page based on current +# branch or tag. + +if _git_reference == "HEAD": + # It looks like the branch has no name. + # Fallback to commit ID. + _git_reference = subprocess.getoutput("git rev-parse HEAD") + +if os.environ.get("READTHEDOCS", "") == "True": + rtd_version = os.environ.get("READTHEDOCS_VERSION", "") + if "." in rtd_version: + # It looks like we have a tag build. + _git_reference = rtd_version + +_source_root = pathlib.Path(__file__).parent.parent / "src" +pydoctor_args = [ + "--quiet", + f"--html-viewsource-base=https://github.com/twisted/twisted/tree/{_git_reference}/src", + "--project-name=Twisted", + "--project-url=https://twistedmatrix.com/", + "--system-class=twisted.python._pydoctor.TwistedSystem", + "--docformat=epytext", + "--intersphinx=https://docs.python.org/3/objects.inv", + "--intersphinx=https://cryptography.io/en/latest/objects.inv", + "--intersphinx=https://pyopenssl.readthedocs.io/en/stable/objects.inv", + "--intersphinx=https://hyperlink.readthedocs.io/en/stable/objects.inv", + "--intersphinx=https://twisted.org/constantly/docs/objects.inv", + "--intersphinx=https://twisted.org/incremental/docs/objects.inv", + "--intersphinx=https://python-hyper.org/projects/hyper-h2/en/stable/objects.inv", + "--intersphinx=https://priority.readthedocs.io/en/stable/objects.inv", + "--intersphinx=https://zopeinterface.readthedocs.io/en/latest/objects.inv", + "--intersphinx=https://automat.readthedocs.io/en/latest/objects.inv", + f"--project-base-dir={_source_root}", + "--html-output={outdir}/api", + str(_source_root / "twisted"), +] -# Base url for apilinks extension -apilinks_base_url = 'https://twistedmatrix.com/documents/%s/api/' % (release,) -traclinks_base_url = 'https://twistedmatrix.com/trac' +pydoctor_url_path = "/en/{rtd_version}/api/" -# -- Extension configuration ---------------------------------------------- +traclinks_base_url = "https://twistedmatrix.com/trac" # A dict mapping unique IDs (which can be used to disambiguate references) to a # tuple of (, ). # The inventory file may be None to use the default location at the given URI. intersphinx_mapping = { - 'py2': ('http://docs.python.org/2.7', None), - 'py3': ('http://docs.python.org/3.3', None), + "py3": ("https://docs.python.org/3", None), + "zopeinterface": ("https://zopeinterface.readthedocs.io/en/latest", None), } # How long to cache remote inventories. Positive is a number of days, # negative means infinite. The default is 5 days, which should be fine # for standard library documentation that does not typically change # significantly after release. -#intersphinx_cache_limit = 5 +# intersphinx_cache_limit = 5 diff -Nru twisted-20.3.0/docs/core/benchmarks/banana.py twisted-22.1.0/docs/core/benchmarks/banana.py --- twisted-20.3.0/docs/core/benchmarks/banana.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/benchmarks/banana.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,7 +1,7 @@ #!/usr/bin/python -from __future__ import print_function from timer import timeit + from twisted.spread.banana import b1282int ITERATIONS = 100000 diff -Nru twisted-20.3.0/docs/core/benchmarks/deferreds.py twisted-22.1.0/docs/core/benchmarks/deferreds.py --- twisted-20.3.0/docs/core/benchmarks/deferreds.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/benchmarks/deferreds.py 2022-02-07 13:12:15.000000000 +0000 @@ -7,52 +7,65 @@ This is mainly useful to compare cdefer.Deferred to defer.Deferred """ -from __future__ import print_function + +from timer import timeit from twisted.internet import defer from twisted.python.compat import range -from timer import timeit benchmarkFuncs = [] + def benchmarkFunc(iter, args=()): """ A decorator for benchmark functions that measure a single iteration count. Registers the function with the given iteration count to the global benchmarkFuncs list """ + def decorator(func): benchmarkFuncs.append((func, args, iter)) return func + return decorator + def benchmarkNFunc(iter, ns): """ A decorator for benchmark functions that measure multiple iteration counts. Registers the function with the given iteration count to the global benchmarkFuncs list. """ + def decorator(func): for n in ns: benchmarkFuncs.append((func, (n,), iter)) return func + return decorator + def instantiate(): """ Only create a deferred """ d = defer.Deferred() + + instantiate = benchmarkFunc(100000)(instantiate) + def instantiateShootCallback(): """ Create a deferred and give it a normal result """ d = defer.Deferred() d.callback(1) + + instantiateShootCallback = benchmarkFunc(100000)(instantiateShootCallback) + def instantiateShootErrback(): """ Create a deferred and give it an exception result. To avoid Unhandled @@ -60,28 +73,38 @@ """ d = defer.Deferred() try: - 1/0 - except: + 1 / 0 + except BaseException: d.errback() d.addErrback(lambda x: None) + + instantiateShootErrback = benchmarkFunc(200)(instantiateShootErrback) ns = [10, 1000, 10000] + def instantiateAddCallbacksNoResult(n): """ Creates a deferred and adds a trivial callback/errback/both to it the given number of times. """ d = defer.Deferred() + def f(result): return result + for i in range(n): d.addCallback(f) d.addErrback(f) d.addBoth(f) d.addCallbacks(f, f) -instantiateAddCallbacksNoResult = benchmarkNFunc(20, ns)(instantiateAddCallbacksNoResult) + + +instantiateAddCallbacksNoResult = benchmarkNFunc(20, ns)( + instantiateAddCallbacksNoResult +) + def instantiateAddCallbacksBeforeResult(n): """ @@ -89,15 +112,22 @@ number of times, and then shoots a result through all of the callbacks. """ d = defer.Deferred() + def f(result): return result + for i in range(n): d.addCallback(f) d.addErrback(f) d.addBoth(f) d.addCallbacks(f) d.callback(1) -instantiateAddCallbacksBeforeResult = benchmarkNFunc(20, ns)(instantiateAddCallbacksBeforeResult) + + +instantiateAddCallbacksBeforeResult = benchmarkNFunc(20, ns)( + instantiateAddCallbacksBeforeResult +) + def instantiateAddCallbacksAfterResult(n): """ @@ -106,15 +136,22 @@ callbacks as they are added. """ d = defer.Deferred() + def f(result): return result + d.callback(1) for i in range(n): d.addCallback(f) d.addErrback(f) d.addBoth(f) d.addCallbacks(f) -instantiateAddCallbacksAfterResult = benchmarkNFunc(20, ns)(instantiateAddCallbacksAfterResult) + + +instantiateAddCallbacksAfterResult = benchmarkNFunc(20, ns)( + instantiateAddCallbacksAfterResult +) + def pauseUnpause(n): """ @@ -123,8 +160,10 @@ callbacks. """ d = defer.Deferred() + def f(result): return result + d.callback(1) d.pause() for i in range(n): @@ -133,8 +172,11 @@ d.addBoth(f) d.addCallbacks(f) d.unpause() + + pauseUnpause = benchmarkNFunc(20, ns)(pauseUnpause) + def benchmark(): """ Run all of the benchmarks registered in the benchmarkFuncs list @@ -143,5 +185,6 @@ for func, args, iter in benchmarkFuncs: print(func.__name__, args, timeit(func, iter, *args)) -if __name__ == '__main__': + +if __name__ == "__main__": benchmark() diff -Nru twisted-20.3.0/docs/core/benchmarks/failure.py twisted-22.1.0/docs/core/benchmarks/failure.py --- twisted-20.3.0/docs/core/benchmarks/failure.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/benchmarks/failure.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,20 +1,22 @@ - """See how slow failure creation is""" -from __future__ import print_function import random + from twisted.python import failure random.seed(10050) O = [0, 20, 40, 60, 80, 10, 30, 50, 70, 90] DEPTH = 30 + def pickVal(): - return random.choice([None, 1, 'Hello', [], {1: 1}, (1, 2, 3)]) + return random.choice([None, 1, "Hello", [], {1: 1}, (1, 2, 3)]) + def makeLocals(n): - return ';'.join(['x%d = %s' % (i, pickVal()) for i in range(n)]) + return ";".join(["x%d = %s" % (i, pickVal()) for i in range(n)]) + for nLocals in O: for i in range(DEPTH): @@ -22,39 +24,56 @@ def deepFailure%d_%d(): %s deepFailure%d_%d() -""" % (nLocals, i, makeLocals(nLocals), nLocals, i + 1) +""" % ( + nLocals, + i, + makeLocals(nLocals), + nLocals, + i + 1, + ) exec(s) - exec(""" + exec( + """ def deepFailure%d_%d(): 1 / 0 -""" % (nLocals, DEPTH)) +""" + % (nLocals, DEPTH) + ) R = range(5000) + + def fail(n): for i in R: try: - eval('deepFailure%d_0' % n)() - except: + eval("deepFailure%d_0" % n)() + except BaseException: failure.Failure() + def fail_str(n): for i in R: try: - eval('deepFailure%d_0' % n)() - except: + eval("deepFailure%d_0" % n)() + except BaseException: str(failure.Failure()) -class PythonException(Exception): pass + +class PythonException(Exception): + pass + def fail_easy(n): for i in R: try: failure.Failure(PythonException()) - except: + except BaseException: pass + from timer import timeit + # for i in O: # timeit(fail, 1, i) @@ -62,7 +81,7 @@ # print('easy failing', i, timeit(fail_easy, 1, i)) for i in O: - print('failing', i, timeit(fail, 1, i)) + print("failing", i, timeit(fail, 1, i)) # for i in O: # print('string failing', i, timeit(fail_str, 1, i)) diff -Nru twisted-20.3.0/docs/core/benchmarks/linereceiver.py twisted-22.1.0/docs/core/benchmarks/linereceiver.py --- twisted-20.3.0/docs/core/benchmarks/linereceiver.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/benchmarks/linereceiver.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,25 +1,26 @@ -from __future__ import division, print_function - import time -from twisted.python.compat import range from twisted.protocols import basic +from twisted.python.compat import range + class CollectingLineReceiver(basic.LineReceiver): def __init__(self): self.lines = [] self.lineReceived = self.lines.append + def deliver(proto, chunks): return [proto.dataReceived(chunk) for chunk in chunks] + def benchmark(chunkSize, lineLength, numLines): - bytes = (b'x' * lineLength + b'\r\n') * numLines + bytes = (b"x" * lineLength + b"\r\n") * numLines chunkCount = len(bytes) // chunkSize + 1 chunks = [] for n in range(chunkCount): - chunks.append(bytes[n*chunkSize:(n+1)*chunkSize]) - assert b''.join(chunks) == bytes, (chunks, bytes) + chunks.append(bytes[n * chunkSize : (n + 1) * chunkSize]) + assert b"".join(chunks) == bytes, (chunks, bytes) p = CollectingLineReceiver() before = time.clock() @@ -28,11 +29,10 @@ assert bytes.splitlines() == p.lines, (bytes.splitlines(), p.lines) - print('chunkSize:', chunkSize, end=' ') - print('lineLength:', lineLength, end=' ') - print('numLines:', numLines, end=' ') - print('CPU Time: ', after - before) - + print("chunkSize:", chunkSize, end=" ") + print("lineLength:", lineLength, end=" ") + print("numLines:", numLines, end=" ") + print("CPU Time: ", after - before) def main(): @@ -46,5 +46,6 @@ for chunkSize in (51, 500, 5000): benchmark(chunkSize, lineLength, numLines) -if __name__ == '__main__': + +if __name__ == "__main__": main() diff -Nru twisted-20.3.0/docs/core/benchmarks/netstringreceiver.py twisted-22.1.0/docs/core/benchmarks/netstringreceiver.py --- twisted-20.3.0/docs/core/benchmarks/netstringreceiver.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/benchmarks/netstringreceiver.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,15 +1,15 @@ # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. -from __future__ import print_function + +import gc +import math +import os +import sys +import time from twisted.protocols.test import test_basic from twisted.python.compat import range, raw_input from twisted.test import proto_helpers -import math -import time -import sys -import os -import gc NETSTRING_POSTFIX = b"," USAGE = """\ @@ -24,7 +24,8 @@ increase it. Stop when the performance starts to deteriorate ;-). """ -class PerformanceTester(object): + +class PerformanceTester: """ A class for testing the performance of some """ @@ -42,14 +43,14 @@ not accept. """ if os.path.isfile(filename): - response = raw_input(("A file named %s exists. " - "Overwrite it (y/n)? ") % filename) + response = raw_input( + ("A file named %s exists. " "Overwrite it (y/n)? ") % filename + ) if response.lower() != "y": print("Performance test cancelled.") sys.exit(1) self.filename = filename - def testPerformance(self, number): """ Drives the execution of C{performTest} with arguments between @@ -61,7 +62,6 @@ for iteration in range(number): self.performTest(iteration) - def performTest(self, iteration): """ Performs one test iteration. Overwrite this. @@ -74,7 +74,6 @@ """ raise NotImplementedError - def createReport(self): """ Creates a file and writes a table with performance data. @@ -93,16 +92,14 @@ self.writeLineSeparator() print("The report was written to %s." % self.filename) - def writeHeader(self): """ Writes the table header for the report. """ self.writeLineSeparator() - self.outputFile.write("| %s |\n" % (" | ".join(self.headers),)) + self.outputFile.write("| {} |\n".format(" | ".join(self.headers))) self.writeLineSeparator() - def writeLineSeparator(self): """ Writes a 'line separator' made from '+' and '-' characters. @@ -110,17 +107,15 @@ dashes = ("-" * (len(header) + 2) for header in self.headers) self.outputFile.write("+%s+\n" % "+".join(dashes)) - def writePerformanceData(self): """ Writes one line for each item in C{self.performanceData}. """ for combination, elapsed in sorted(self.performanceData.items()): totalSize, chunkSize, numberOfChunks = combination - self.outputFile.write(self.lineFormat % - (totalSize, chunkSize, numberOfChunks, - elapsed)) - + self.outputFile.write( + self.lineFormat % (totalSize, chunkSize, numberOfChunks, elapsed) + ) class NetstringPerformanceTester(PerformanceTester): @@ -133,10 +128,10 @@ data and time to process them. """ - headers = ["Chunk size", "Number of chunks", "Total size", - "Time to receive" ] - lineFormat = ("| %%%dd | %%%dd | %%%dd | %%%d.4f |\n" % - tuple([len(header) for header in headers])) + headers = ["Chunk size", "Number of chunks", "Total size", "Time to receive"] + lineFormat = "| %%%dd | %%%dd | %%%dd | %%%d.4f |\n" % tuple( + len(header) for header in headers + ) def __init__(self, filename): """ @@ -151,7 +146,6 @@ self.netstringReceiver = test_basic.TestNetstring() self.netstringReceiver.makeConnection(transport) - def performTest(self, number): """ Tests the performance of C{NetstringReceiver.dataReceived}. @@ -170,7 +164,6 @@ self.testCombination(chunkSize, numberOfChunks) numberOfChunks = numberOfChunks // 2 - def testCombination(self, chunkSize, numberOfChunks): """ Tests one combination of chunk size and number of chunks. @@ -187,7 +180,6 @@ key = (chunkSize, numberOfChunks, dataSize) self.performanceData[key] = elapsed - def configureCombination(self, chunkSize, numberOfChunks): """ Updates C{MAX_LENGTH} for {self.netstringReceiver} (to avoid @@ -212,11 +204,10 @@ numberOfDigits = math.ceil(math.log10(dataSize)) + 1 return chunk, dataSize - def receiveData(self, chunk, numberOfChunks, dataSize): dr = self.netstringReceiver.dataReceived now = time.time() - dr("{}:".format(dataSize).encode("ascii")) + dr(f"{dataSize}:".encode("ascii")) for idx in range(numberOfChunks): dr(chunk) dr(NETSTRING_POSTFIX) @@ -227,7 +218,7 @@ def disableGarbageCollector(): gc.disable() - print('Disabled Garbage Collector.') + print("Disabled Garbage Collector.") def main(number, filename): diff -Nru twisted-20.3.0/docs/core/benchmarks/task.py twisted-22.1.0/docs/core/benchmarks/task.py --- twisted-20.3.0/docs/core/benchmarks/task.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/benchmarks/task.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,13 +1,12 @@ - """ Benchmarks for L{twisted.internet.task}. """ -from __future__ import print_function from timer import timeit from twisted.internet import task + def test_performance(): """ L{LoopingCall} should not take long to skip a lot of iterations. @@ -23,5 +22,6 @@ def main(): print("LoopingCall large advance takes", timeit(test_performance, iter=1)) -if __name__ == '__main__': + +if __name__ == "__main__": main() diff -Nru twisted-20.3.0/docs/core/benchmarks/timer.py twisted-22.1.0/docs/core/benchmarks/timer.py --- twisted-20.3.0/docs/core/benchmarks/timer.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/benchmarks/timer.py 2022-02-07 13:12:15.000000000 +0000 @@ -4,20 +4,22 @@ """ Helper stuff for benchmarks. """ -from __future__ import print_function import gc + gc.disable() -print('Disabled GC') +print("Disabled GC") + -def timeit(func, iter = 1000, *args, **kwargs): +def timeit(func, iter=1000, *args, **kwargs): """ timeit(func, iter = 1000 *args, **kwargs) -> elapsed time - + calls func iter times with args and kwargs, returns time elapsed """ from time import time as currentTime + r = range(iter) t = currentTime() for i in r: diff -Nru twisted-20.3.0/docs/core/benchmarks/tpclient_nt.py twisted-22.1.0/docs/core/benchmarks/tpclient_nt.py --- twisted-20.3.0/docs/core/benchmarks/tpclient_nt.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/benchmarks/tpclient_nt.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,12 +1,14 @@ """Non-twisted throughput client.""" -from __future__ import print_function -import socket, time, sys +import socket +import sys +import time TIMES = 50000 S = "0123456789" * 1024 sent = len(S) * TIMES + def main(): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((sys.argv[1], int(sys.argv[2]))) @@ -19,5 +21,6 @@ print("Throughput: %s kbytes/sec" % ((sent / passed) / 1024)) s.close() -if __name__ == '__main__': + +if __name__ == "__main__": main() diff -Nru twisted-20.3.0/docs/core/benchmarks/tpclient.py twisted-22.1.0/docs/core/benchmarks/tpclient.py --- twisted-20.3.0/docs/core/benchmarks/tpclient.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/benchmarks/tpclient.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,8 +1,9 @@ """Throughput test.""" -from __future__ import print_function -import time, sys -from twisted.internet import reactor, protocol +import sys +import time + +from twisted.internet import protocol, reactor from twisted.python import log TIMES = 10000 @@ -10,8 +11,8 @@ toReceive = len(S) * TIMES -class Sender(protocol.Protocol): +class Sender(protocol.Protocol): def connectionMade(self): start() self.numSent = 0 @@ -23,7 +24,7 @@ def pauseProducing(self): pass - + def resumeProducing(self): self.numSent += 1 self.transport.write(S) @@ -37,10 +38,12 @@ started = None + def start(): global started started = time.time() + def shutdown(success): if not success: raise SystemExit("failure or something") @@ -56,6 +59,6 @@ reactor.run() -if __name__ == '__main__': - #log.startLogging(sys.stdout) +if __name__ == "__main__": + # log.startLogging(sys.stdout) main() diff -Nru twisted-20.3.0/docs/core/benchmarks/tpserver_nt.py twisted-22.1.0/docs/core/benchmarks/tpserver_nt.py --- twisted-20.3.0/docs/core/benchmarks/tpserver_nt.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/benchmarks/tpserver_nt.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,16 +1,19 @@ """Non-twisted throughput server.""" -from __future__ import print_function -import socket, signal, sys +import signal +import socket +import sys + def signalhandler(*args): print("alarm!") sys.stdout.flush() + signal.signal(signal.SIGALRM, signalhandler) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) -s.bind(('', 8001)) +s.bind(("", 8001)) s.listen(1) while 1: c, (h, p) = s.accept() diff -Nru twisted-20.3.0/docs/core/benchmarks/tpserver.py twisted-22.1.0/docs/core/benchmarks/tpserver.py --- twisted-20.3.0/docs/core/benchmarks/tpserver.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/benchmarks/tpserver.py 2022-02-07 13:12:15.000000000 +0000 @@ -2,8 +2,8 @@ import sys -from twisted.protocols.wire import Discard from twisted.internet import protocol, reactor +from twisted.protocols.wire import Discard from twisted.python import log @@ -14,6 +14,5 @@ reactor.run() -if __name__ == '__main__': +if __name__ == "__main__": main() - diff -Nru twisted-20.3.0/docs/core/development/listings/new_module_template.py twisted-22.1.0/docs/core/development/listings/new_module_template.py --- twisted-20.3.0/docs/core/development/listings/new_module_template.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/development/listings/new_module_template.py 2022-02-07 13:12:15.000000000 +0000 @@ -6,7 +6,5 @@ Docstring goes here. """ -from __future__ import absolute_import, division, print_function - __all__ = [] diff -Nru twisted-20.3.0/docs/core/development/policy/code-dev.rst twisted-22.1.0/docs/core/development/policy/code-dev.rst --- twisted-20.3.0/docs/core/development/policy/code-dev.rst 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/development/policy/code-dev.rst 2022-02-07 13:12:15.000000000 +0000 @@ -33,18 +33,18 @@ Git tutorials can be found elsewhere, see in particular `Git and GitHub learning resources `_ . The relevant data you need to check out a copy of the Twisted tree is available on -the `development page `_ , and is as follows: - - - - +the `development page `_ , and is as follows: .. code-block:: console - $ git clone https://github.com/twisted/twisted Twisted +The output of ``git blame`` `will be better `_ if you configure it to use our ignore file: +.. code-block:: console + + $ cd Twisted + $ git config blame.ignoreRevsFile .git-blame-ignore-revs @@ -203,7 +203,7 @@ Building docs ------------- -Twisted documentation (not including the automatically-generated API docs) is generated by `Sphinx `_ . +Twisted documentation (not including the automatically-generated API docs) is generated by `Sphinx `_ . The docs are written in Restructured Text (``.rst``) and translated into ``.html`` files by the ``bin/admin/build-docs`` script. To build the HTML form of the docs into the ``doc/`` directory, do the following: @@ -259,7 +259,7 @@ -A minor mode for development with Twisted using Emacs is available. See ``twisted-dev.el`` , provided by `twisted-emacs `_ , +A minor mode for development with Twisted using Emacs is available. See ``twisted-dev.el`` , provided by `twisted-emacs `_ , for several utility functions which make it easier to grep for methods, run test cases, etc. @@ -273,13 +273,5 @@ Our support for building Debian packages has fallen into disrepair. We would very much like to restore this functionality, but until we do so, if -you are interested in this, you are on your own. See `stdeb `_ for one possible approach to +you are interested in this, you are on your own. See `stdeb `_ for one possible approach to this. - - -Travis CI integration ---------------------- - -Travis CI is configured to run a subset of the official Buildbot builders for each push to a PR or to ``trunk`` - -The tests are executed using ``tox-travis``. See the ``tox.ini`` file for the actual configuration. diff -Nru twisted-20.3.0/docs/core/development/policy/coding-standard.rst twisted-22.1.0/docs/core/development/policy/coding-standard.rst --- twisted-20.3.0/docs/core/development/policy/coding-standard.rst 2019-06-15 22:29:05.000000000 +0000 +++ twisted-22.1.0/docs/core/development/policy/coding-standard.rst 2022-02-07 13:12:15.000000000 +0000 @@ -5,7 +5,7 @@ ------ Try to choose names which are both easy to remember and meaningful. -Some silliness is OK at the module naming level (see :api:`twisted.spread ` ...) but when choosing class names, be as precise as possible. +Some silliness is OK at the module naming level (see :py:mod:`twisted.spread` ...) but when choosing class names, be as precise as possible. Try to avoid terms that may have existing definitions or uses. This rule is often broken, since it is incredibly difficult, as most normal words have already been taken by some other software. @@ -24,8 +24,8 @@ Overview ~~~~~~~~ -Twisted development should always be `test-driven `_ . -The complete test suite in the head of the Git trunk is required to be passing on `supported platforms `_ at all times. +Twisted development should always be `test-driven `_ . +The complete test suite in the head of the Git trunk is required to be passing on `supported platforms `_ at all times. Regressions in the test suite are addressed by reverting whatever revisions introduced them. @@ -44,7 +44,7 @@ Parts of the Twisted test suite may serve as good examples of how to write tests for Twisted or for Twisted-based libraries (newer parts of the test suite are generally better examples than older parts - check when the code you are looking at was written before you use it as an example of what you should write). The names of test modules must begin with ``test_`` so that they are automatically discoverable by test runners such as Trial. -Twisted's unit tests are written using :api:`twisted.trial `, an xUnit library which has been extensively customized for use in testing Twisted and Twisted-based libraries. +Twisted's unit tests are written using :py:mod:`twisted.trial`, an xUnit library which has been extensively customized for use in testing Twisted and Twisted-based libraries. Implementation (i.e., non-test) source files should begin with a ``test-case-name`` tag which gives the name of any test modules or packages which exercise them. This lets tools discover a subset of the entire test suite which they can run first to find tests which might be broken by a particular change. @@ -72,23 +72,19 @@ Whitespace ---------- -Indentation is 4 spaces per indent. -Tabs are not allowed. -It is preferred that every block appears on a new line, so that control structure indentation is always visible. +Code must be formatted according to the `The Black Code Style `_. +This entire source tree can be reformatted by running: -Lines are flowed at 79 columns. -They must not have trailing whitespace. -Long lines must be wrapped using implied line continuation inside parentheses; backslashes aren't allowed. -To handle long import lines, please wrap them inside parentheses: +.. code-block:: console + + tox -e lint -.. code-block:: python - from very.long.package import (foo, bar, baz, - qux, quux, quuux) +Only changed files can be reformatted by running: +.. code-block:: console -Top-level classes and functions must be separated with 3 blank lines, and class-level functions with 2 blank lines. -The control-L (i.e. ``^L``) form feed character must not be used. + pipx run pre-commit run Modules @@ -198,7 +194,7 @@ transport.write(b"HTTP/" + HTTPVersion) -Utilities are available in :api:`twisted.python.compat ` to paper over some use cases where other Python code (especially the standard library) expects a "native string", or provides a native string where a bytestring is actually required (namely :api:`twisted.python.compat ` and :api:`twisted.python.compat `) +Utilities are available in :py:mod:`twisted.python.compat` to paper over some use cases where other Python code (especially the standard library) expects a "native string", or provides a native string where a bytestring is actually required (namely :py:mod:`twisted.python.compat.nativeString ` and :py:mod:`twisted.python.compat.networkString `) String Formatting Operations @@ -322,7 +318,7 @@ Comments -------- -Start by reading the `PEP8 Comments section `_. +Start by reading the `PEP8 Comments section `_. Ignore `Documentation Strings` section from PEP8 as Twisted uses a different docstring standard. `FIXME/TODO` comments must have an associated ticket and contain a reference to it in the form of a full URL to the ticket. @@ -379,7 +375,7 @@ For each "script" , that is, a program you expect a Twisted user to run from the command-line, the following things must be done: -#. Write a module in :api:`twisted.scripts ` which contains a callable global named ``run``. +#. Write a module in :py:mod:`twisted.scripts` which contains a callable global named ``run``. This will be called by the command line part with no arguments (it will usually read ``sys.argv`` ). Feel free to write more functions or classes in this module, if you feel they are useful to others. #. Create a file which contains a shebang line for Python. @@ -397,15 +393,15 @@ Always make sure that ``/usr/bin/env`` exists or use a softlink/symbolic link to point it to the correct path. Python's distutils will rewrite the shebang line upon installation so this policy only covers the source files in version control. -#. For core scripts, add an entry to ``_CONSOLE_SCRIPTS`` in ``src/twisted/python/_setup.py``: +#. For core scripts, add an entry point to ``"console_scripts"`` in ``setup.cfg``: - .. code-block:: python + .. code-block:: cfg - _CONSOLE_SCRIPTS = [ + [options.entry_points] + console_scripts = ... - "twistd = twisted.scripts.twistd:run", - "yourmodule" = "twisted.scripts.yourmodule:run", - ] + twistd = twisted.scripts.twistd:run + yourmodule = twisted.scripts.yourmodule:run #. And end with: @@ -453,7 +449,7 @@ Some modules don't exist across all supported Python versions. For example, Python 2.3's ``sets`` module was deprecated in Python 2.6 in favor of the ``set`` and ``frozenset`` builtins. -:api:`twisted.python.compat ` would be the place to add ``set`` and ``frozenset`` implementations that work across Python versions. +:py:mod:`twisted.python.compat` would be the place to add ``set`` and ``frozenset`` implementations that work across Python versions. Classes @@ -544,13 +540,13 @@ Callback Arguments ------------------ -There are several methods whose purpose is to help the user set up callback functions, for example :api:`twisted.internet.defer.Deferred.addCallback ` or the reactor's :api:`twisted.internet.base.ReactorBase.callLater ` method. +There are several methods whose purpose is to help the user set up callback functions, for example :py:meth:`Deferred.addCallback ` or the reactor's :py:meth:`callLater ` method. To make access to the callback as transparent as possible, most of these methods use ``**kwargs`` to capture arbitrary arguments that are destined for the user's callback. This allows the call to the setup function to look very much like the eventual call to the target callback function. In these methods, take care to not have other argument names that will "steal" the user's callback's arguments. When sensible, prefix these "internal" argument names with an underscore. -For example, :api:`twisted.spread.pb.RemoteReference.callRemote ` is meant to be called like this: +For example, :py:meth:`RemoteReference.callRemote ` is meant to be called like this: .. code-block:: python @@ -621,12 +617,9 @@ Python 3 -------- -Twisted is being ported to Python 3, targeting Python 3.5+. +Twisted was ported to Python 3. Please see :doc:`Porting to Python 3 ` for details. -All new modules must be Python 2.7 & 3.5+ compatible, and all new code to ported modules must be Python 2.7 & 3.5+ compatible. -New code in non-ported modules must be written in a 2.7 & 3.5+ compatible way (explicit bytes/unicode strings, new exception raising format, etc) as to prevent extra work when that module is eventually ported. -Code targeting Python 3 specific features must gracefully fall-back on Python 2 as much as is reasonably possible (for example, Python 2 support for 'async/await' is not reasonably possible and would not be required, but code that uses a Python 3-specific module such as ipaddress should be able to use a backport to 2.7 if available). Database @@ -644,11 +637,11 @@ C Code ------ -C code must be optional, and work across multiple platforms (MSVC++9/10/14 for Pythons 2.7 and 3.5+ on Windows, as well as recent GCCs and Clangs for Linux, macOS, and FreeBSD). +C code must be optional, and work across multiple platforms (MSVC++14 for Python 3 on Windows, as well as recent GCCs and Clangs for Linux, macOS, and FreeBSD). C code should be kept in external bindings packages which Twisted depends on. If creating new C extension modules, using `cffi `_ is highly encouraged, as it will perform well on PyPy and CPython, and be easier to use on Python 2 and 3. -Consider optimizing for `PyPy `_ instead of creating bespoke C code. +Consider optimizing for `PyPy `_ instead of creating bespoke C code. Commit Messages @@ -674,7 +667,7 @@ Twisted currently uses Git for source control. All development must occur using branches; when a task is considered complete another Twisted developer may review it and if no problems are found, it may be merged into trunk. -The Twisted wiki has `a start `_. +The Twisted wiki has `a start `_. If you wish to ignore certain files, create a ``.gitignore`` file, or edit it if it exists. For example: @@ -695,8 +688,11 @@ Fallback -------- -In case of conventions not enforced in this document, the reference documents to use in fallback is `PEP 8 `_ for Python code and `PEP 7 `_ for C code. -For example, the paragraph **Whitespace in Expressions and Statements** in PEP 8 describes what should be done in Twisted code. +In case of conventions not enforced in this document, the reference documents to use in fallback are: + +* `The Black code style `_ +* `PEP 8 `_ for Python code +* `PEP 7 `_ for C code Recommendations diff -Nru twisted-20.3.0/docs/core/development/policy/compatibility-policy.rst twisted-22.1.0/docs/core/development/policy/compatibility-policy.rst --- twisted-20.3.0/docs/core/development/policy/compatibility-policy.rst 2019-06-15 21:59:27.000000000 +0000 +++ twisted-22.1.0/docs/core/development/policy/compatibility-policy.rst 2022-02-07 13:12:15.000000000 +0000 @@ -97,13 +97,13 @@ **Every change is unique.** -Sometimes, we'll want to make a change that fits with this spirit of this document (keeping Twisted working for applications which rely upon it) but may not fit with the letter of the procedure described above (the change modifies behavior of an existing API sufficiently that something might break). -Generally, the reason that one would want to do this is to give applications a performance enhancement or bug fix that could break behavior that unanticipated, hypothetical uses of an existing API, but we don't want well-behaved applications to pay the penalty of a deprecation/adopt-a-new-API/removal cycle in order to get the benefits of the improvement if they don't need to. +Sometimes, we'll want to make a change that fits with the spirit of this document (keeping Twisted working for applications which rely upon it) but may not fit with the letter of the procedure described above (the change modifies behavior of an existing API sufficiently that something might break). +Generally, the reason that one would want to do this is to give applications a performance enhancement or bug fix that could break behavior in unintended hypothetical uses of an existing API, but we don't want well-behaved applications to pay the penalty of a deprecation/adopt-a-new-API/removal cycle in order to get the benefits of the improvement if they don't need to. If this is the case for your change, it's possible to make such a modification without a deprecation/removal cycle. However, we must give users an opportunity to discover whether a particular incompatible change affects them: we should not trust our own assessments of how code uses the API. In order to propose an incompatible change, start a discussion on the mailing list. -Make sure that it is eye-catching so those who don't read all list messages in depth will notice it, by prefixing the subject with **INCOMPATIBLE CHANGE:** (capitalized like so). +Make sure that it is eye-catching, so those who don't read all list messages in depth will notice it, by prefixing the subject with **INCOMPATIBLE CHANGE:** (capitalized like so). Always include a link to the ticket, and branch (if relevant). In order to **conclude** such a discussion, there must be a branch available so that developers can run their unit tests against it to mechanically verify that their understanding of their own code is correct. @@ -117,7 +117,7 @@ The announcement forum for incompatible changes and the waiting period required are subject to change as we discover how effective this method is; the important aspect of this policy is that users have some way of finding out in advance about changes which might affect them. -Compatible Changes. Changed not Covered by the Compatibility Policy +Compatible Changes. Changes not Covered by the Compatibility Policy ------------------------------------------------------------------- Here is a non-exhaustive list of changes which are not covered by the compatibility policy. @@ -132,8 +132,6 @@ Test code and test helpers are considered private API and should not be imported outside of the Twisted testing infrastructure. -As an exception to this, :api:`twisted.test.proto_helpers` is considered a public API -(see `#6435 `_ for more discussion). Private Changes @@ -162,14 +160,14 @@ That means that no application may ever rely on, for example, the value of any **func_code** object's **co_code** attribute remaining stable, or the **checksum** of a .py file remaining stable. **Docstrings** may also change at any time. -No application code may expect any Twisted class, module, or method's __doc__ attribute to remain the same. +Applications must not depend on any Twisted class, module, or method's metadata attributes such as ``__module__``, ``__name__``, ``__qualname__``, ``__annotations__`` and ``__doc__`` to remain the same. New Attributes ^^^^^^^^^^^^^^ New code may also be added. -No application may ever rely on the output of the ``dir()`` function on any object remaining stable, nor on any object's ``__all__`` attribute, nor on any object's ``__dict__`` not having new keys added to it. +Applications must not depend on the output of the ``dir()`` function on any object remaining stable, nor on any object's ``__all__`` attribute, nor on any object's ``__dict__`` not having new keys added to it. These may happen in any maintenance or bugfix release, no matter how minor. @@ -177,9 +175,22 @@ ^^^^^^^^ Even though Python objects can be pickled and unpickled without explicit support for this, whether a particular pickled object can be unpickled after any particular change to the implementation of that object is less certain. -Because of this, no application may depend on any object defined by Twisted to provide pickle compatibility between any release unless the object explicitly documents this as a feature it has. +Because of this, applications must not depend on any object defined by Twisted to provide pickle compatibility between any release unless the object explicitly documents this as a feature it has. +Representations +^^^^^^^^^^^^^^^ + +The printable representations of objects, as returned by ``repr()`` and defined by ``def __repr__(self):`` are for debugging and informational purposes. +Because of this, applications must not depend on any object defined by Twisted to provide repr compatibility between any release. +Attribute Access +^^^^^^^^^^^^^^^^ +How an object's attributes are defined and accessed is considered an implementation detail. +To allow backwards compatibility, an attribute may be moved from the instance ``__dict__`` into an ``@property`` or other descriptor based accessor. + +Adding new attributes to a constructed object, or monkey patching, is not considered a public use. This restriction allows both creating and converting to slotted classes. +Because of this, applications must not depend on any object defined by Twisted to provide ``__dict__`` or ``__slots__`` compatibility between any release. + Changes Covered by the Compatibility Policy ------------------------------------------- @@ -306,7 +317,7 @@ Classes ^^^^^^^ -Classes are deprecated by raising a warning when they are access from within their module, using the :api:`twisted.python.deprecate.deprecatedModuleAttribute ` helper. +Classes are deprecated by raising a warning when they are access from within their module, using the :py:func:`deprecatedModuleAttribute ` helper. .. code-block:: python @@ -328,7 +339,7 @@ The deprecation message must include the name of the function which is deprecated, the version of Twisted in which it was first deprecated, and a suggestion for a replacement. If the API provides functionality which it is determined is beyond the scope of Twisted or it has no replacement, then it may be deprecated without a replacement. -There is also a :api:`twisted.python.deprecate.deprecated ` decorator which works for new-style classes. +There is also a :py:func:`deprecated ` decorator which works for new-style classes. For example: @@ -374,7 +385,7 @@ ^^^^^^^^^^^^^^^^^^^ To deprecate an attribute on instances of a new-type class, make the attribute into a property and call ``warnings.warn`` from the getter and/or setter function for that property. -You can also use the :api:`twisted.python.deprecate.deprecatedProperty ` decorator which works for new-style classes. +You can also use the :py:func:`deprecatedProperty ` decorator which works for new-style classes. .. code-block:: python @@ -414,7 +425,7 @@ Module attributes ^^^^^^^^^^^^^^^^^ -Modules cannot have properties, so module attributes should be deprecated using the :api:`twisted.python.deprecate.deprecatedModuleAttribute ` helper. +Modules cannot have properties, so module attributes should be deprecated using the :py:func:`deprecatedModuleAttribute ` helper. .. code-block:: python @@ -434,7 +445,7 @@ Modules ^^^^^^^ -To deprecate an entire module, :api:`twisted.python.deprecate.deprecatedModuleAttribute ` can be used on the parent package's ``__init__.py``. +To deprecate an entire module, :py:func:`deprecatedModuleAttribute ` can be used on the parent package's ``__init__.py``. There are two other options: @@ -445,14 +456,24 @@ Testing Deprecation Code ------------------------ -Like all changes in Twisted, deprecations must come with associated automated tested. +Like all changes in Twisted, deprecations must come with associated automated tests. + +Due to a bug in Trial (`#6348 `_), unhandled deprecation warnings will not cause test failures or show in test results. + +While the Trial bug is not fixed, to trigger test failures on unhandled deprecation warnings use: + +.. code-block:: console + + python -Werror::DeprecationWarning ./bin/trial twisted.conch + There are several options for checking that a code is deprecated and that using it raises a ``DeprecationWarning``. -In order of decreasing preference: +There are helper methods available for handling deprecated callables (:py:meth:`callDeprecated `) and deprecated classes or module attributes (:py:meth:`getDeprecatedModuleAttribute `). -* :api:`twisted.trial.unittest.SynchronousTestCase.flushWarnings ` -* :api:`twisted.trial.unittest.SynchronousTestCase.assertWarns ` -* :api:`twisted.trial.unittest.SynchronousTestCase.callDeprecated ` +If the deprecation warning has a customized message or cannot be caught using these helpers, you can use :py:meth:`assertWarns ` to specify the exact warning you expect. + +Lastly, you can use :py:meth:`flushWarnings ` after performing any deprecated activity. +This is the most precise, but also the most verbose, way to assert that you've raised a ``DeprecationWarning``. .. code-block:: python @@ -464,8 +485,6 @@ """ Tests for deprecated code. """ - - def test_deprecationUsingFlushWarnings(self): """ flushWarnings() is the recommended way of checking for deprecations. @@ -485,6 +504,15 @@ self.assertEqual(message, warnings[0]['message']) + def test_deprecationUsingCallDeprecated(self): + """ + callDeprecated() assumes that the DeprecationWarning message + follows Twisted's standard format. + """ + self.callDeprecated( + Version("Twisted", 1, 2, 0), db.getUser, 'some-user') + + def test_deprecationUsingAssertWarns(self): """ assertWarns() is designed as a general helper to check any @@ -498,17 +526,9 @@ db.getUser, 'some-user') - def test_deprecationUsingCallDeprecated(self): - """ - Avoid using self.callDeprecated() just to check the deprecation - call. - """ - self.callDeprecated( - Version("Twisted", 1, 2, 0), db.getUser, 'some-user') - When code is deprecated, all previous tests in which the code is called and tested will now raise ``DeprecationWarning``\ s. -Making calls to the deprecated code without raising these warnings can be done using the :api:`twisted.trial.unittest.TestCase.callDeprecated ` helper. +Making calls to the deprecated code without raising these warnings can be done using the :py:meth:`callDeprecated ` helper. .. code-block:: python @@ -532,10 +552,24 @@ self.assertEqual('some-value', user.homePath) -Due to a bug in Trial (`#6348 `_), unhandled deprecation warnings will not cause test failures or show in test results. +Tests which need to use deprecated classes should use the :py:meth:`getDeprecatedModuleAttribute ` helper. -While the Trial bug is not fixed, to trigger test failures on unhandled deprecation warnings use: +.. code-block:: python -.. code-block:: console + from twisted.trial import unittest - python -Werror::DeprecationWarning ./bin/trial twisted.conch + + class UsernameHashedPasswordTests(unittest.TestCase): + """ + Tests for L{UsernameHashedPassword}. + """ + def test_initialisation(self): + """ + The initialisation of L{UsernameHashedPassword} will set C{username} + and C{hashed} on it. + """ + UsernameHashedPassword = self.getDeprecatedModuleAttribute( + 'twisted.cred.credentials', 'UsernameHashedPassword', Version('Twisted', 20, 3, 0)) + creds = UsernameHashedPassword(b"foo", b"bar") + self.assertEqual(creds.username, b"foo") + self.assertEqual(creds.hashed, b"bar") diff -Nru twisted-20.3.0/docs/core/development/policy/release-process.rst twisted-22.1.0/docs/core/development/policy/release-process.rst --- twisted-20.3.0/docs/core/development/policy/release-process.rst 2019-06-15 21:59:27.000000000 +0000 +++ twisted-22.1.0/docs/core/development/policy/release-process.rst 2022-02-07 13:12:15.000000000 +0000 @@ -4,8 +4,6 @@ This document describes the Twisted release process. Although it is still incomplete, every effort has been made to ensure that it is accurate and up-to-date. -This process has only been tested on Linux or macOS, so we recommend that you do the release on Linux or macOS. - If you want to make changes to the release process, follow the normal Twisted development process (contribute release automation software that has documentation and unit tests demonstrating that it works). @@ -14,14 +12,10 @@ By the end of a Twisted release we'll have: -- Tarballs for Twisted as a whole, and for each of its sub-projects -- Windows installers for the whole Twisted project -- Updated documentation (API & howtos) on the twistedmatrix.com site -- Updated documentation on Read The Docs -- Updated download links on the twistedmatrix.com site -- Announcement emails sent to major Python lists -- Announcement post on `the Twisted blog `_ -- A tag in our Git repository marking the release +- Wheel and sdist package published on `PyPI Twisted project `_. +- Updated documentation (API & howtos) on `Twisted Read The Docs `_ +- Announcement email sent to Twisted main list +- A `GitHub Release `_ with the associated tag in our Git repository Prerequisites @@ -29,26 +23,26 @@ To release Twisted, you will need: -- Commit privileges to Twisted -- Access to ``dornkirk.twistedmatrix.com`` as t-web -- Permissions to edit the Downloads wiki page -- Channel operator permissions for ``#twisted`` -- Admin privileges for Twisted's PyPI packages -- Contributor status for `the Twisted blog `_ -- Read The Docs access for the Twisted project +- Commit privileges to Twisted GitHub repository. Version numbers --------------- -Twisted releases use a time-based numbering scheme. -Releases versions like YY.MM.mm, where YY is the last two digits of the year of the release, MM is the month of release, and mm is the number of the patch release. +Twisted releases use a time-based numbering scheme following PEP440 convention. +Releases versions like YY.MM.mm, where YY is the last two digits of the year of the release, MM is the month of release, and mm is the number of the bugfix release. + +There are 3 release types: + +- Major release when YY.MM is updated. +- Bugfix / patch / point release when the mm number is updated +- Release candidates which are pre-releases as YY.MM.mmrc1 For example: - A release in Jan 2017 is 17.1.0 - A release in Nov 2017 is 17.11.0 -- If 17.11.0 has some critical defects, then a patch release would be numbered 17.11.1 +- If 17.11.0 has some critical defects, then a bugfix 17.11.1 - The first release candidate of 17.1.0 is 17.1.0rc1, the second is 17.1.0rc2 Every release of Twisted includes the whole project. @@ -75,29 +69,15 @@ Prepare for a release --------------------- -#. Check the milestone for the upcoming release - - - Get rid of any non-critical bugs - - Get any critical bugs fixed - - Check the release manager notes in case anyone has left anything which can only be done during the release. - -#. Check for any ​regressions +#. Check for any ​regressions using `Trac regression report `_ -#. Read through the ``INSTALL.rst`` and ``README.rst`` files to make sure things like the supported Python versions are correct - - - Check the required Python version. - - Check that the list matches the current set of buildbots. - - Any mistakes should be fixed in trunk before making the release branch +#. Any regression should be fixed and merged trunk before making the release branch #. Choose a version number. -#. File a ticket - - - Assign it to the upcoming release milestone - - Assign it to yourself - - Call it "Release $RELEASE" +#. File a ticket in Trac called "Release $RELEASE" and assign it to yourself. -#. Make a branch and attach it to the ticket: +#. Make a branch for the release and include the ticket number in the name (4290 is Trac ticket ID): - ``git fetch origin`` - ``git checkout origin/trunk`` @@ -107,42 +87,44 @@ How to do a release candidate ----------------------------- -#. Check ​buildbot to make sure all supported platforms are green (wait for pending builds if necessary). -#. If a previously supported platform does not currently have a buildbot, move from supported platforms to "expected to work" in ``INSTALL.rst``. + +Prepare the branch +~~~~~~~~~~~~~~~~~~ + #. In your Git repo, fetch and check out the new release branch. #. Run ``python -m incremental.update Twisted --rc`` #. Commit the changes made by Incremental. -#. Run ``towncrier``. -#. Commit the changes made by towncrier - this automatically removes the NEWS newsfragments. -#. Bump copyright dates in ``LICENSE``, ``twisted/copyright.py``, and ``README.rst`` if required -#. Push the changes up to GitHub. -#. Run ``python setup.py sdist --formats=bztar -d /tmp/twisted-release`` to build the tarballs. -#. Copy ``NEWS.rst`` to ``/tmp/twisted-release/`` for people to view without having to download the tarballs. - (e.g. ``cp NEWS.rst /tmp/twisted-release/NEWS.rst``) -#. Upload the tarballs to ``twistedmatrix.com/Releases/rc/$RELEASE`` (see #4353) +#. Run ``tox -e towncrier``. +#. Commit the changes made by towncrier - this automatically removes the newsfragment files. +#. Bump copyright dates in ``LICENSE``, ``src/twisted/copyright.py``, and ``README.rst`` if required +#. Push the changes up to GitHub and create a new release PR. +#. The GitHub PR is dedicated to the final release and the same PR is used to release the candidate and final version. +#. Use the `GitHub Create Release UI `_ the make a new release. +#. Create a tag using the format `twisted-VERSION` based on the latest commit on the release branch that was approved after the review. +#. Use `Twisted VERSION` as the name of the release. +#. Add the release NEWS to GitHub Release page. +#. Make sure 'This is a pre-release` is checked. +#. Github Actions will upload the dist to PyPI when a new tag is pushed to the repo. +#. Read the Docs hooks will publish a new version of the docs. - - You can use ``rsync --rsh=ssh --partial --progress -av /tmp/twisted-release/ t-web@dornkirk.twistedmatrix.com:/srv/t-web/data/releases/rc//`` to do this. -#. Write the release candidate announcement +Announce +~~~~~~~~ - - Read through the NEWS file and summarize the interesting changes for the release - - Get someone else to look over the announcement before doing it -#. Announce the release candidate on +#. Write the release announcement - - the twisted-python mailing list - - on IRC in the ``#twisted`` topic +#. Announce the release candidate on -Release candidate announcement ------------------------------- + - the twisted-python mailing list by sending the content of latest release NEWS + - on IRC in the ``#twisted-dev`` topic by sending the version number -The release candidate announcement should mention the important changes since the last release, and exhort readers to test this release candidate. +The release candidate announcement might mention the important changes since the last release, and ask readers to test this release candidate. Here's what the $RELEASErc1 release announcement might look like:: Live from PyCon Atlanta, I'm pleased to herald the approaching footsteps of the $API release. - Tarballs for the first Twisted $RELEASE release candidate are now available at: - http://people.canonical.com/~jml/Twisted/ + Wheels for Twisted $RELEASE release candidate are now available on PyPI. Highlights include: @@ -169,181 +151,46 @@ How to do a final release ------------------------- + Prepare the branch ~~~~~~~~~~~~~~~~~~ #. Have the release branch, previously used to generate a release candidate, checked out -#. Run ``python -m incremental.update Twisted``. -#. Revert the release candidate newsfile changes, in order. -#. Run ``towncrier`` to make the final newsfile. -#. Add the quote of the release to the ``README.rst`` -#. Make a new quote file for the next version - - - ``git mv docs/fun/Twisted.Quotes docs/historic/Quotes/Twisted-$API; echo '' > docs/fun/Twisted.Quotes; git add docs/fun/Twisted.Quotes`` - -#. Commit the version and ``README.rst`` changes. +#. Run ``python -m incremental.update Twisted --newversion $RELEASE`` +#. Manually update the release date if necessary. +#. Commit and push. #. Submit the ticket for review #. Pause until the ticket is reviewed and accepted. -#. Tag the release. - - - ``git tag -s twisted-$RELEASE -m "Tag $RELEASE release"`` - - ``git push --tags`` - - -Cut the tarballs & installers -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -#. Using a checkout of the release branch or the release tag (with no local changes!), build the tarballs: - - - ``python setup.py sdist --formats=bztar -d /tmp/twisted-release`` - -#. Build Windows wheel - - - Download the latest ``.whl`` files from `Buildbot `_ and save them in the staging directory - -#. Sign the tarballs and Windows installers. - (You will need a PGP key for this - use something like Seahorse to generate one, if you don't have one.) - - - MD5: ``md5sum Tw* | gpg -a --clearsign > /tmp/twisted-release/twisted-$RELEASE-md5sums.txt`` - - SHA512: ``shasum -a 512 Tw* | gpg -a --clearsign > /tmp/twisted-release/twisted-$RELEASE-shasums.txt`` - - Compare these to an ​example of ``twisted-$RELEASE-md5sums.txt`` - they should look the same. - - -Update documentation -~~~~~~~~~~~~~~~~~~~~ - -#. Get the dependencies - - - PyDoctor (from PyPI) - -#. Build the documentation - - - ``./bin/admin/build-docs .`` - - ``cp -R doc /tmp/twisted-release/`` - -#. Run the build-apidocs script to build the API docs and then upload them (See also #2891). - - - Copy the pydoctor directory from the twisted branch into your Git checkout. - - ``./bin/admin/build-apidocs . /tmp/twisted-release/api`` - - Documentation will be generated in a directory called ``/tmp/twisted-release/api`` - -#. Update the Read The Docs default to point to the release branch (via the `dashboard `_). - - -Distribute -~~~~~~~~~~ - -#. Create a tarball with the contents of the release directory: ``cd /tmp/twisted-release; tar -cvjf ../release.tar.bz2 *`` - -#. Upload to the official upload locations (see #2888) - - - ``cd ~; git clone https://github.com/twisted-infra/braid`` - - ``cd braid`` - - ``virtualenv ~/dev/braid; source ~/dev/braid/bin/activate; cd ~/braid; python setup.py develop;`` - - ``cd ~/braid; fab config.production t-web.uploadRelease:$RELEASE,/tmp/release.tar.bz2`` - -#. Test the generated docs - - - Browse to ``http://twistedmatrix.com/documents/$RELEASE/`` - - Make sure that there is content in each of the directories and that it looks good - - Follow each link on `the documentation page `_, replace current with ``$RELEASE`` (e.g. 10.0.0) and look for any obvious breakage - -#. Change the "current" symlink - - - Upload release: ``fab config.production t-web.updateCurrentDocumentation:$RELEASE`` +#. Use the `GitHub Create Release UI `_ the make a new release. +#. Create a tag using the format `twisted-VERSION` based on the latest commit on the release branch that was approved after the review. +#. Use `Twisted VERSION` as the name of the release. +#. Add the release NEWS to GitHub Release page. +#. Make sure 'This is a pre-release` is not checked. +#. Github Actions will upload the dist to PyPI when a new tag is pushed to the repo. PyPI is the only canonical source for Twisted packages. +#. Read the Docs hooks will publish a new version of the docs. Announce ~~~~~~~~ -#. Update Downloads pages - - - The following updates are automatic, due to the use of the ​ProjectVersion wiki macro throughout most of the Downloads page. - - - Text references to the old version to refer to the new version - - The link to the NEWS file to point to the new version - - Links and text to the main tarball - - - Add a new md5sum link - - Add a new shasum link - - Save the page, check all links - -#. Update PyPI records & upload files - - - ``pip install -U twine`` - - ``twine upload /tmp/twisted-release/Twisted-$RELEASE*`` - -#. Write the release announcement (see below) +#. Write the release announcement that should be similar to the release candidate, with the updated version and release date. #. Announce the release - - Send a text version of the announcement to: twisted-python@twistedmatrix.com, python-announce-list@python.org, python-list@python.org, twisted-web@twistedmatrix.com + - Send a text version of the announcement to: twisted-python@twistedmatrix.com, python-announce-list@python.org, twisted-web@twistedmatrix.com - ​http://labs.twistedmatrix.com (Post a web version of the announcements, with links instead of literal URLs) - Twitter, if you feel like it - ``#twisted`` topic on IRC (you'll need ops) -#. Run ``python -m incremental Twisted --dev`` to add a `dev0` postfix. - -#. Commit the dev0 update change. - -#. Merge the release branch into trunk, closing the release ticket at the same time. - -#. Close the release milestone (which should have no tickets in it). - -#. Open a milestone for the next release. - - -Release announcement -~~~~~~~~~~~~~~~~~~~~ - -The final release announcement should: -- Mention the version number -- Include links to where the release can be downloaded -- Summarize the significant changes in the release -- Consider including the quote of the release -- Thank the contributors to the release +Post release +~~~~~~~~~~~~ -Here's an example:: +#. Run ``python -m incremental.update Twisted --post`` to add a `post` postfix. - On behalf of Twisted Matrix Laboratories, I am honoured to announce - the release of Twisted 13.2! +#. Commit the post0 update change. - The highlights of this release are: - - * Twisted now includes a HostnameEndpoint implementation which uses - IPv4 and IPv6 in parallel, speeding up the connection by using - whichever connects first (the 'Happy Eyeballs'/RFC 6555 algorithm). - (#4859) - - * Improved support for Cancellable Deferreds by kaizhang, our GSoC - student. (#4320, #6532, #6572, #6639) - - * Improved Twisted.Mail documentation by shira, our Outreach Program - for Women intern. (#6649, #6652) - - * twistd now waits for the application to start successfully before - exiting after daemonization. (#823) - - * SSL server endpoint string descriptions now support the - specification of chain certificates. (#6499) - - * Over 70 closed tickets since 13.1.0. - - For more information, check the NEWS file (link provided below). - - You can find the downloads at - (or alternatively ) . - The NEWS file is also available at - . - - Many thanks to everyone who had a part in this release - the - supporters of the Twisted Software Foundation, the developers who - contributed code as well as documentation, and all the people building - great things with Twisted! - - Twisted Regards, - HawkOwl +#. Merge the release branch into trunk, closing the release ticket at the same time. When things go wrong @@ -353,19 +200,16 @@ 1. Abort the release, make a new point release (e.g. abort 10.0.0, make 10.0.1 after the bug is fixed) 2. Abort the release, make a new release candidate (e.g. abort 10.0.0, make 10.0.0pre3 after the bug is fixed) -3. Interrupt the release, fix the bug, then continue with it (e.g. release 10.0.0 with the bug fix) - -If you choose the third option, then you should: -- Delete the tag for the release -- Recreate the tag from the release branch once the fix has been applied to that branch +Don't delete a tag that was already pushed for a release. +Create a new tag with incremented version. Bug fix releases ---------------- Sometimes, bugs happen, and sometimes these are regressions in the current released version. -This section goes over doing these "point" releases. +This section goes over doing these "bugfix" releases. 1. Ensure all bugfixes are in trunk. @@ -377,19 +221,3 @@ - Instead of just ``--rc`` when running the change-versions script, add the patch flag, making it ``--patch --rc``. - Instead of waiting a week, a shorter pause is acceptable for a patch release. - - -Open questions --------------- - -- How do we manage the case where there are untested builds in trunk? - -- Should picking a release quote be part of the release or the release candidate? - -- What bugs should be considered release blockers? - - - All bugs with a type from the release blocker family - - Anybody can create/submit a new ticket with a release blocker type - - Ultimately it's the RM's discretion to accept a ticket as a release blocker - -- Should news fragments contain information about who made the changes? diff -Nru twisted-20.3.0/docs/core/development/policy/test-standard.rst twisted-22.1.0/docs/core/development/policy/test-standard.rst --- twisted-20.3.0/docs/core/development/policy/test-standard.rst 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/development/policy/test-standard.rst 2022-02-07 13:12:15.000000000 +0000 @@ -72,7 +72,7 @@ From the root of the Twisted source tree, run -`Trial `_ : +`Trial `_ : @@ -110,7 +110,7 @@ -Always, always, *always* be sure `all the tests pass `_ before committing any code. If someone else +Always, always, *always* be sure `all the tests pass `_ before committing any code. If someone else checks out code at the start of a development session and finds failing tests, they will not be happy and may decide to *hunt you down* . @@ -237,17 +237,17 @@ Most unit tests should avoid performing real, platform-implemented I/O operations. Real I/O is slow, unreliable, and unwieldy. -When implementing a protocol, :api:`twisted.test.proto_helpers.StringTransport` can be used instead of a real TCP transport. +When implementing a protocol, :py:class:`twisted.internet.testing.StringTransport` can be used instead of a real TCP transport. ``StringTransport`` is fast, deterministic, and can easily be used to exercise all possible network behaviors. -If you need pair a client to a server and have them talk to each other, use :api:`twisted.test.iosim.connect` with :api:`twisted.test.iosim.FakeTransport` transports. +If you need pair a client to a server and have them talk to each other, use ``twisted.test.iosim.connect`` with ``twisted.test.iosim.FakeTransport`` transports. Real Time ~~~~~~~~~ Most unit tests should also avoid waiting for real time to pass. -Unit tests which construct and advance a :api:`twisted.internet.task.Clock ` are fast and deterministic. +Unit tests which construct and advance a :py:class:`twisted.internet.task.Clock` are fast and deterministic. When designing your code allow for the reactor to be injected during tests. @@ -306,7 +306,7 @@ The only exceptions to this are unit tests for a real reactor implementation. Unit tests for protocol implementations or other application code should not use a reactor. Unit tests for real reactor implementations should not use the global reactor, but should -instead use :api:`twisted.internet.test.reactormixins.ReactorBuilder` so they can be applied to all of the reactor implementations automatically. +instead use ``twisted.internet.test.reactormixins.ReactorBuilder`` so they can be applied to all of the reactor implementations automatically. In no case should new unit tests use the global reactor. @@ -560,7 +560,7 @@ The ``test-case-name`` tag will allow ``trial --testmodule twisted/dir/myfile.py`` to determine which test cases need to be run to exercise the code in ``myfile.py`` . Several tools (as -well as http://launchpad.net/twisted-emacs's ``twisted-dev.el`` 's F9 command) use this to automatically +well as https://launchpad.net/twisted-emacs's ``twisted-dev.el`` 's F9 command) use this to automatically run the right tests. @@ -580,14 +580,14 @@ -- A chapter on `Unit Testing `_ - in Mark Pilgrim's `Dive Into Python `_ . -- `unittest `_ module documentation, in the `Python Library Reference `_ . +- A chapter on `Unit Testing `_ + in Mark Pilgrim's `Dive Into Python `_ . +- :mod:`unittest` module documentation, in the `Python Library Reference `_ . - `UnitTest `__ on the `PortlandPatternRepository Wiki `_ , where all the cool `ExtremeProgramming `_ kids hang out. - `Unit Tests `_ in `Extreme Programming: A Gentle Introduction `_ . -- Ron Jeffries expounds on the importance of `Unit Tests at 100% `_ . -- Ron Jeffries writes about the `Unit Test `_ in the `Extreme Programming practices of C3 `_ . +- Ron Jeffries expounds on the importance of `Unit Tests at 100% `_ . +- Ron Jeffries writes about the `Unit Test `_ in the `Extreme Programming practices of C3 `_ . - `PyUnit's homepage `_ . - The top-level tests directory, `twisted/test `_. diff -Nru twisted-20.3.0/docs/core/development/policy/writing-standard.rst twisted-22.1.0/docs/core/development/policy/writing-standard.rst --- twisted-20.3.0/docs/core/development/policy/writing-standard.rst 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/development/policy/writing-standard.rst 2022-02-07 13:12:15.000000000 +0000 @@ -388,7 +388,7 @@ impose upon authors the need to have a few dummy functions: in Twisted documentation the most common example is where a function is needed to generate a Deferred and fire it after some time has passed. An example -might be this, where :api:`twisted.internet.task.deferLater ` is used to fire a callback +might be this, where :py:func:`deferLater ` is used to fire a callback after a period of time: diff -Nru twisted-20.3.0/docs/core/examples/ampclient.py twisted-22.1.0/docs/core/examples/ampclient.py --- twisted-20.3.0/docs/core/examples/ampclient.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/examples/ampclient.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,36 +1,45 @@ -from __future__ import print_function +from ampserver import Divide, Sum -from twisted.internet import reactor, defer, endpoints +from twisted.internet import defer, reactor from twisted.internet.endpoints import TCP4ClientEndpoint, connectProtocol from twisted.protocols.amp import AMP -from ampserver import Sum, Divide def doMath(): - destination = TCP4ClientEndpoint(reactor, '127.0.0.1', 1234) + destination = TCP4ClientEndpoint(reactor, "127.0.0.1", 1234) sumDeferred = connectProtocol(destination, AMP()) + def connected(ampProto): return ampProto.callRemote(Sum, a=13, b=81) + sumDeferred.addCallback(connected) + def summed(result): - return result['total'] + return result["total"] + sumDeferred.addCallback(summed) divideDeferred = connectProtocol(destination, AMP()) + def connected(ampProto): return ampProto.callRemote(Divide, numerator=1234, denominator=0) + divideDeferred.addCallback(connected) + def trapZero(result): result.trap(ZeroDivisionError) print("Divided by zero: returning INF") return 1e1000 + divideDeferred.addErrback(trapZero) def done(result): - print('Done with math:', result) + print("Done with math:", result) reactor.stop() + defer.DeferredList([sumDeferred, divideDeferred]).addCallback(done) -if __name__ == '__main__': + +if __name__ == "__main__": doMath() reactor.run() diff -Nru twisted-20.3.0/docs/core/examples/ampserver.py twisted-22.1.0/docs/core/examples/ampserver.py --- twisted-20.3.0/docs/core/examples/ampserver.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/examples/ampserver.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,42 +1,43 @@ -from __future__ import print_function - from twisted.protocols import amp + class Sum(amp.Command): - arguments = [(b'a', amp.Integer()), - (b'b', amp.Integer())] - response = [(b'total', amp.Integer())] + arguments = [(b"a", amp.Integer()), (b"b", amp.Integer())] + response = [(b"total", amp.Integer())] class Divide(amp.Command): - arguments = [(b'numerator', amp.Integer()), - (b'denominator', amp.Integer())] - response = [(b'result', amp.Float())] - errors = {ZeroDivisionError: b'ZERO_DIVISION'} + arguments = [(b"numerator", amp.Integer()), (b"denominator", amp.Integer())] + response = [(b"result", amp.Float())] + errors = {ZeroDivisionError: b"ZERO_DIVISION"} class Math(amp.AMP): def sum(self, a, b): total = a + b - print('Did a sum: {} + {} = {}'.format(a, b, total)) - return {'total': total} + print(f"Did a sum: {a} + {b} = {total}") + return {"total": total} + Sum.responder(sum) def divide(self, numerator, denominator): result = float(numerator) / denominator - print('Divided: {} / {} = {}'.format(numerator, denominator, result)) - return {'result': result} + print(f"Divided: {numerator} / {denominator} = {result}") + return {"result": result} + Divide.responder(divide) def main(): from twisted.internet import reactor from twisted.internet.protocol import Factory + pf = Factory() pf.protocol = Math reactor.listenTCP(1234, pf) - print('started') + print("started") reactor.run() -if __name__ == '__main__': + +if __name__ == "__main__": main() diff -Nru twisted-20.3.0/docs/core/examples/bananabench.py twisted-22.1.0/docs/core/examples/bananabench.py --- twisted-20.3.0/docs/core/examples/bananabench.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/examples/bananabench.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,20 +1,20 @@ # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. -from __future__ import print_function - -import sys import time from io import BytesIO +from twisted.internet import protocol + # Twisted Imports from twisted.spread import banana -from twisted.internet import protocol iterationCount = 10000 + class BananaBench: - r = range( iterationCount ) + r = range(iterationCount) + def setUp(self, encClass): self.io = BytesIO() self.enc = encClass() @@ -35,7 +35,7 @@ self.enc.sendEncoded(value) self.io.truncate(0) endtime = time.time() - print(' Encode took {} seconds'.format(endtime - starttime)) + print(f" Encode took {endtime - starttime} seconds") return endtime - starttime def testDecode(self, value): @@ -45,7 +45,7 @@ for i in self.r: self.enc.dataReceived(encoded) endtime = time.time() - print(' Decode took {} seconds'.format(endtime - starttime)) + print(f" Decode took {endtime - starttime} seconds") return endtime - starttime def performTest(self, method, data, encClass): @@ -54,24 +54,35 @@ self.tearDown() def runTests(self, testData): - print('Test data is: {}'.format(testData)) - print(' Using Pure Python Banana:') + print(f"Test data is: {testData}") + print(" Using Pure Python Banana:") self.performTest(self.testEncode, testData, banana.Banana) self.performTest(self.testDecode, testData, banana.Banana) + bench = BananaBench() -print('Doing {} iterations of each test.'.format(iterationCount)) -print('') +print(f"Doing {iterationCount} iterations of each test.") +print("") testData = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] bench.runTests(testData) testData = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0] bench.runTests(testData) testData = [[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]] bench.runTests(testData) -testData = [b"one", b"two", b"three", b"four", b"five", b"six", b"seven", b"eight", b"nine", b"ten"] +testData = [ + b"one", + b"two", + b"three", + b"four", + b"five", + b"six", + b"seven", + b"eight", + b"nine", + b"ten", +] bench.runTests(testData) testData = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] bench.runTests(testData) testData = [1, 2, [3, 4], [30.5, 40.2], 5, [b"six", b"seven", [b"eight", 9]], [10], []] bench.runTests(testData) - diff -Nru twisted-20.3.0/docs/core/examples/chatserver.py twisted-22.1.0/docs/core/examples/chatserver.py --- twisted-20.3.0/docs/core/examples/chatserver.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/examples/chatserver.py 2022-02-07 13:12:15.000000000 +0000 @@ -3,12 +3,10 @@ run me with twistd -y chatserver.py, and then connect with multiple telnet clients to port 1025 """ -from __future__ import print_function from twisted.protocols import basic - class MyChat(basic.LineReceiver): def connectionMade(self): print("Got new client!") @@ -24,11 +22,11 @@ c.message(line) def message(self, message): - self.transport.write(message + b'\n') + self.transport.write(message + b"\n") +from twisted.application import internet, service from twisted.internet import protocol -from twisted.application import service, internet factory = protocol.ServerFactory() factory.protocol = MyChat diff -Nru twisted-20.3.0/docs/core/examples/courier.py twisted-22.1.0/docs/core/examples/courier.py --- twisted-20.3.0/docs/core/examples/courier.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/examples/courier.py 2022-02-07 13:12:15.000000000 +0000 @@ -7,34 +7,42 @@ Example of an interface to Courier's mail filter. """ -LOGFILE = '/tmp/filter.log' +LOGFILE = "/tmp/filter.log" # Setup log file from twisted.python import log -log.startLogging(open(LOGFILE, 'a')) + +log.startLogging(open(LOGFILE, "a")) import sys + sys.stderr = log.logfile # Twisted imports from twisted.internet import reactor, stdio -from twisted.internet.protocol import Protocol, Factory +from twisted.internet.protocol import Factory, Protocol from twisted.protocols import basic -FILTERS='/var/lib/courier/filters' -ALLFILTERS='/var/lib/courier/allfilters' -FILTERNAME='twistedfilter' +FILTERS = "/var/lib/courier/filters" +ALLFILTERS = "/var/lib/courier/allfilters" +FILTERNAME = "twistedfilter" -import email.parser import email.message -import os, os.path -from syslog import syslog, openlog, LOG_MAIL +import email.parser +import os +import os.path +from syslog import LOG_MAIL, openlog, syslog + def trace_dump(): - t,v,tb = sys.exc_info() + t, v, tb = sys.exc_info() openlog(FILTERNAME, 0, LOG_MAIL) - syslog('Unhandled exception: {} - {}'.format(v, t)) + syslog(f"Unhandled exception: {v} - {t}") while tb: - syslog('Trace: {}:{} {}'.format(tb.tb_frame.f_code.co_filename,tb.tb_frame.f_code.co_name,tb.tb_lineno)) + syslog( + "Trace: {}:{} {}".format( + tb.tb_frame.f_code.co_filename, tb.tb_frame.f_code.co_name, tb.tb_lineno + ) + ) tb = tb.tb_next # just to be safe del tb @@ -50,33 +58,31 @@ pass - class DieWhenLost(Protocol): def connectionLost(self, reason=None): reactor.stop() - class MailProcessor(basic.LineReceiver): """ I process a mail message. Override filterMessage to do any filtering you want. """ + messageFilename = None - delimiter = '\n' + delimiter = "\n" def connectionMade(self): - log.msg('Connection from {}'.format(self.transport)) - self.state = 'connected' + log.msg(f"Connection from {self.transport}") + self.state = "connected" self.metaInfo = [] - def lineReceived(self, line): - if self.state == 'connected': + if self.state == "connected": self.messageFilename = line - self.state = 'gotMessageFilename' - if self.state == 'gotMessageFilename': + self.state = "gotMessageFilename" + if self.state == "gotMessageFilename": if line: self.metaInfo.append(line) else: @@ -85,7 +91,6 @@ return self.filterMessage() - def filterMessage(self): """Override this. @@ -95,18 +100,18 @@ emailParser = email.parser.Parser() with open(self.messageFilename) as f: emailParser.parse(f) - self.sendLine(b'200 Ok') - except: + self.sendLine(b"200 Ok") + except BaseException: trace_dump() - self.sendLine(b'435 ' + FILTERNAME.encode("ascii") + b' processing error') + self.sendLine(b"435 " + FILTERNAME.encode("ascii") + b" processing error") def main(): # Listen on the UNIX socket f = Factory() f.protocol = MailProcessor - safe_del('{}/{}'.format(ALLFILTERS, FILTERNAME)) - reactor.listenUNIX('{}/{}'.format(ALLFILTERS, FILTERNAME), f, 10) + safe_del(f"{ALLFILTERS}/{FILTERNAME}") + reactor.listenUNIX(f"{ALLFILTERS}/{FILTERNAME}", f, 10) # Once started, close fd 3 to let Courier know we're ready reactor.callLater(0, os.close, 3) @@ -117,5 +122,6 @@ # Go! reactor.run() -if __name__ == '__main__': + +if __name__ == "__main__": main() diff -Nru twisted-20.3.0/docs/core/examples/cred.py twisted-22.1.0/docs/core/examples/cred.py --- twisted-20.3.0/docs/core/examples/cred.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/examples/cred.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,21 +1,16 @@ - # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. -from __future__ import print_function - import sys -from zope.interface import implementer, Interface -from twisted.protocols import basic +from zope.interface import Interface, implementer + +from twisted.cred import checkers, credentials, error, portal from twisted.internet import protocol +from twisted.protocols import basic from twisted.python import log -from twisted.cred import error -from twisted.cred import portal -from twisted.cred import checkers -from twisted.cred import credentials class IProtocolUser(Interface): def getPrivileges(): @@ -24,6 +19,7 @@ def logout(): """Cleanup per-login resources allocated to this avatar""" + @implementer(IProtocolUser) class AnonymousUser: def getPrivileges(self): @@ -32,6 +28,7 @@ def logout(self): print("Cleaning up anonymous user resources") + @implementer(IProtocolUser) class RegularUser: def getPrivileges(self): @@ -40,6 +37,7 @@ def logout(self): print("Cleaning up regular user resources") + @implementer(IProtocolUser) class Administrator: def getPrivileges(self): @@ -48,6 +46,7 @@ def logout(self): print("Cleaning up administrator resources") + class Protocol(basic.LineReceiver): user = None portal = None @@ -63,29 +62,29 @@ self.logout() self.avatar = None self.logout = None - + def lineReceived(self, line): - f = getattr(self, 'cmd_' + line.decode("ascii").upper().split()[0]) + f = getattr(self, "cmd_" + line.decode("ascii").upper().split()[0]) if f: try: f(*line.split()[1:]) except TypeError: self.sendLine(b"Wrong number of arguments.") - except: + except BaseException: self.sendLine(b"Server error (probably your fault)") def cmd_ANON(self): if self.portal: - self.portal.login(credentials.Anonymous(), None, IProtocolUser - ).addCallbacks(self._cbLogin, self._ebLogin - ) + self.portal.login( + credentials.Anonymous(), None, IProtocolUser + ).addCallbacks(self._cbLogin, self._ebLogin) else: self.sendLine(b"DENIED") - + def cmd_USER(self, name): self.user = name self.sendLine(b"Alright. Now PASS?") - + def cmd_PASS(self, password): if not self.user: self.sendLine(b"USER required before PASS") @@ -94,15 +93,18 @@ self.portal.login( credentials.UsernamePassword(self.user, password), None, - IProtocolUser - ).addCallbacks(self._cbLogin, self._ebLogin - ) + IProtocolUser, + ).addCallbacks(self._cbLogin, self._ebLogin) else: self.sendLine(b"DENIED") def cmd_PRIVS(self): self.sendLine(b"You have the following privileges: ") - self.sendLine(b" ".join([str(priv).encode("ascii") for priv in self.avatar.getPrivileges()])) + self.sendLine( + b" ".join( + [str(priv).encode("ascii") for priv in self.avatar.getPrivileges()] + ) + ) def _cbLogin(self, result): (interface, avatar, logout) = result @@ -110,22 +112,24 @@ self.avatar = avatar self.logout = logout self.sendLine(b"Login successful. Available commands: PRIVS") - + def _ebLogin(self, failure): failure.trap(error.UnauthorizedLogin) self.sendLine(b"Login denied! Go away.") + class ServerFactory(protocol.ServerFactory): protocol = Protocol - + def __init__(self, portal): self.portal = portal - + def buildProtocol(self, addr): p = protocol.ServerFactory.buildProtocol(self, addr) p.portal = self.portal return p + @implementer(portal.IRealm) class Realm: def requestAvatar(self, avatarId, mind, *interfaces): @@ -138,7 +142,10 @@ else: av = RegularUser() return IProtocolUser, av, av.logout - raise NotImplementedError("Only IProtocolUser interface is supported by this realm") + raise NotImplementedError( + "Only IProtocolUser interface is supported by this realm" + ) + def main(): r = Realm() @@ -148,14 +155,16 @@ c.addUser(b"SECONDUSER", b"secret") p.registerChecker(c) p.registerChecker(checkers.AllowAnonymousAccess()) - + f = ServerFactory(p) log.startLogging(sys.stdout) from twisted.internet import reactor + reactor.listenTCP(4738, f) reactor.run() -if __name__ == '__main__': + +if __name__ == "__main__": main() diff -Nru twisted-20.3.0/docs/core/examples/dbcred.py twisted-22.1.0/docs/core/examples/dbcred.py --- twisted-20.3.0/docs/core/examples/dbcred.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/examples/dbcred.py 2022-02-07 13:12:15.000000000 +0000 @@ -8,23 +8,28 @@ that deals with a database backend to authenticate a user. """ +from zope.interface import implementer + from twisted.cred import error -from twisted.cred.credentials import IUsernameHashedPassword, IUsernamePassword from twisted.cred.checkers import ICredentialsChecker +from twisted.cred.credentials import IUsernameHashedPassword, IUsernamePassword from twisted.internet.defer import Deferred -from zope.interface import implementer - @implementer(ICredentialsChecker) -class DBCredentialsChecker(object): +class DBCredentialsChecker: """ This class checks the credentials of incoming connections against a user table in a database. """ - def __init__(self, runQuery, + + def __init__( + self, + runQuery, query="SELECT username, password FROM user WHERE username = %s", - customCheckFunc=None, caseSensitivePasswords=True): + customCheckFunc=None, + caseSensitivePasswords=True, + ): """ @param runQuery: This will be called to get the info from the db. Generally you'd want to create a @@ -56,7 +61,9 @@ self.credentialInterfaces = (IUsernamePassword,) else: self.credentialInterfaces = ( - IUsernamePassword, IUsernameHashedPassword,) + IUsernamePassword, + IUsernameHashedPassword, + ) self.sql = query @@ -75,9 +82,12 @@ dbDeferred = self.runQuery(self.sql, (credentials.username,)) # Setup our deferred result deferred = Deferred() - dbDeferred.addCallbacks(self._cbAuthenticate, self._ebAuthenticate, - callbackArgs=(credentials, deferred), - errbackArgs=(credentials, deferred)) + dbDeferred.addCallbacks( + self._cbAuthenticate, + self._ebAuthenticate, + callbackArgs=(credentials, deferred), + errbackArgs=(credentials, deferred), + ) return deferred def _cbAuthenticate(self, result, credentials, deferred): @@ -87,17 +97,15 @@ """ if len(result) == 0: # Username not found in db - deferred.errback(error.UnauthorizedLogin('Username unknown')) + deferred.errback(error.UnauthorizedLogin("Username unknown")) else: username, password = result[0] if self.customCheckFunc: # Let the owner do the checking - if self.customCheckFunc( - username, credentials.password, password): + if self.customCheckFunc(username, credentials.password, password): deferred.callback(credentials.username) else: - deferred.errback( - error.UnauthorizedLogin('Password mismatch')) + deferred.errback(error.UnauthorizedLogin("Password mismatch")) else: # It's up to us or the credentials object to do the checking # now @@ -106,22 +114,19 @@ if credentials.checkPassword(password): deferred.callback(credentials.username) else: - deferred.errback( - error.UnauthorizedLogin('Password mismatch')) + deferred.errback(error.UnauthorizedLogin("Password mismatch")) elif IUsernamePassword.providedBy(credentials): # Compare the passwords, deciging whether or not to use # case sensitivity if self.caseSensitivePasswords: - passOk = ( - password.lower() == credentials.password.lower()) + passOk = password.lower() == credentials.password.lower() else: passOk = password == credentials.password # See if they match if passOk: deferred.callback(credentials.username) else: - deferred.errback( - error.UnauthorizedLogin('Password mismatch')) + deferred.errback(error.UnauthorizedLogin("Password mismatch")) else: # OK, we don't know how to check this deferred.errback(error.UnhandledCredentials()) @@ -142,13 +147,17 @@ You can test it running C{pbechoclient.py}. """ import sys + from twisted.python import log + log.startLogging(sys.stdout) import os - if os.path.isfile('testcred'): - os.remove('testcred') + + if os.path.isfile("testcred"): + os.remove("testcred") from twisted.enterprise import adbapi - pool = adbapi.ConnectionPool('pysqlite2.dbapi2', 'testcred') + + pool = adbapi.ConnectionPool("pysqlite2.dbapi2", "testcred") # Create the table that will be used query1 = """CREATE TABLE user ( username string, @@ -156,16 +165,20 @@ )""" # Insert a test user query2 = """INSERT INTO user VALUES ('guest', 'guest')""" + def cb(res): pool.runQuery(query2) - pool.runQuery(query1).addCallback(cb) - checker = DBCredentialsChecker(pool.runQuery, - query="SELECT username, password FROM user WHERE username = ?") - from twisted.cred.portal import Portal + pool.runQuery(query1).addCallback(cb) + checker = DBCredentialsChecker( + pool.runQuery, query="SELECT username, password FROM user WHERE username = ?" + ) import pbecho + + from twisted.cred.portal import Portal from twisted.spread import pb + portal = Portal(pbecho.SimpleRealm()) portal.registerChecker(checker) reactor.listenTCP(pb.portno, pb.PBServerFactory(portal)) @@ -173,6 +186,6 @@ if __name__ == "__main__": from twisted.internet import reactor + reactor.callWhenRunning(main) reactor.run() - diff -Nru twisted-20.3.0/docs/core/examples/echoclient.py twisted-22.1.0/docs/core/examples/echoclient.py --- twisted-20.3.0/docs/core/examples/echoclient.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/examples/echoclient.py 2022-02-07 13:12:15.000000000 +0000 @@ -2,7 +2,6 @@ # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. -from __future__ import print_function from twisted.internet import task from twisted.internet.defer import Deferred @@ -10,7 +9,6 @@ from twisted.protocols.basic import LineReceiver - class EchoClient(LineReceiver): end = b"Bye-bye!" @@ -19,38 +17,32 @@ self.sendLine(b"What a fine day it is.") self.sendLine(self.end) - def lineReceived(self, line): print("receive:", line) if line == self.end: self.transport.loseConnection() - class EchoClientFactory(ClientFactory): protocol = EchoClient def __init__(self): self.done = Deferred() - def clientConnectionFailed(self, connector, reason): - print('connection failed:', reason.getErrorMessage()) + print("connection failed:", reason.getErrorMessage()) self.done.errback(reason) - def clientConnectionLost(self, connector, reason): - print('connection lost:', reason.getErrorMessage()) + print("connection lost:", reason.getErrorMessage()) self.done.callback(None) - def main(reactor): factory = EchoClientFactory() - reactor.connectTCP('localhost', 8000, factory) + reactor.connectTCP("localhost", 8000, factory) return factory.done - -if __name__ == '__main__': +if __name__ == "__main__": task.react(main) diff -Nru twisted-20.3.0/docs/core/examples/echoclient_ssl.py twisted-22.1.0/docs/core/examples/echoclient_ssl.py --- twisted-20.3.0/docs/core/examples/echoclient_ssl.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/examples/echoclient_ssl.py 2022-02-07 13:12:15.000000000 +0000 @@ -2,25 +2,27 @@ # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. -from twisted.internet import ssl, task, protocol, endpoints, defer +import echoclient + +from twisted.internet import defer, endpoints, protocol, ssl, task from twisted.python.modules import getModule -import echoclient @defer.inlineCallbacks def main(reactor): factory = protocol.Factory.forProtocol(echoclient.EchoClient) - certData = getModule(__name__).filePath.sibling('public.pem').getContent() + certData = getModule(__name__).filePath.sibling("public.pem").getContent() authority = ssl.Certificate.loadPEM(certData) - options = ssl.optionsForClientTLS(u'example.com', authority) - endpoint = endpoints.SSL4ClientEndpoint(reactor, 'localhost', 8000, - options) + options = ssl.optionsForClientTLS("example.com", authority) + endpoint = endpoints.SSL4ClientEndpoint(reactor, "localhost", 8000, options) echoClient = yield endpoint.connect(factory) done = defer.Deferred() echoClient.connectionLost = lambda reason: done.callback(None) yield done -if __name__ == '__main__': + +if __name__ == "__main__": import echoclient_ssl + task.react(echoclient_ssl.main) diff -Nru twisted-20.3.0/docs/core/examples/echoclient_udp.py twisted-22.1.0/docs/core/examples/echoclient_udp.py --- twisted-20.3.0/docs/core/examples/echoclient_udp.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/examples/echoclient_udp.py 2022-02-07 13:12:15.000000000 +0000 @@ -3,22 +3,17 @@ # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. -from __future__ import print_function - -from twisted.internet.protocol import DatagramProtocol from twisted.internet import reactor +from twisted.internet.protocol import DatagramProtocol + class EchoClientDatagramProtocol(DatagramProtocol): - strings = [ - b"Hello, world!", - b"What a fine day it is.", - b"Bye-bye!" - ] - + strings = [b"Hello, world!", b"What a fine day it is.", b"Bye-bye!"] + def startProtocol(self): - self.transport.connect('127.0.0.1', 8000) + self.transport.connect("127.0.0.1", 8000) self.sendDatagram() - + def sendDatagram(self): if len(self.strings): datagram = self.strings.pop(0) @@ -27,13 +22,15 @@ reactor.stop() def datagramReceived(self, datagram, host): - print('Datagram received: ', repr(datagram)) + print("Datagram received: ", repr(datagram)) self.sendDatagram() + def main(): protocol = EchoClientDatagramProtocol() - t = reactor.listenUDP(0, protocol) + reactor.listenUDP(0, protocol) reactor.run() -if __name__ == '__main__': + +if __name__ == "__main__": main() diff -Nru twisted-20.3.0/docs/core/examples/echoserv.py twisted-22.1.0/docs/core/examples/echoserv.py --- twisted-20.3.0/docs/core/examples/echoserv.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/examples/echoserv.py 2022-02-07 13:12:15.000000000 +0000 @@ -3,12 +3,14 @@ # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. -from twisted.internet.protocol import Protocol, Factory from twisted.internet import reactor +from twisted.internet.protocol import Factory, Protocol ### Protocol Implementation # This is just about the simplest possible protocol + + class Echo(Protocol): def dataReceived(self, data): """ @@ -23,5 +25,6 @@ reactor.listenTCP(8000, f) reactor.run() -if __name__ == '__main__': + +if __name__ == "__main__": main() diff -Nru twisted-20.3.0/docs/core/examples/echoserv_ssl.py twisted-22.1.0/docs/core/examples/echoserv_ssl.py --- twisted-20.3.0/docs/core/examples/echoserv_ssl.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/examples/echoserv_ssl.py 2022-02-07 13:12:15.000000000 +0000 @@ -4,20 +4,23 @@ import sys -from twisted.internet import ssl, protocol, task, defer +import echoserv + +from twisted.internet import defer, protocol, ssl, task from twisted.python import log from twisted.python.modules import getModule -import echoserv def main(reactor): log.startLogging(sys.stdout) - certData = getModule(__name__).filePath.sibling('server.pem').getContent() + certData = getModule(__name__).filePath.sibling("server.pem").getContent() certificate = ssl.PrivateCertificate.loadPEM(certData) factory = protocol.Factory.forProtocol(echoserv.Echo) reactor.listenSSL(8000, factory, certificate.options()) return defer.Deferred() -if __name__ == '__main__': + +if __name__ == "__main__": import echoserv_ssl + task.react(echoserv_ssl.main) diff -Nru twisted-20.3.0/docs/core/examples/echoserv_udp.py twisted-22.1.0/docs/core/examples/echoserv_udp.py --- twisted-20.3.0/docs/core/examples/echoserv_udp.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/examples/echoserv_udp.py 2022-02-07 13:12:15.000000000 +0000 @@ -3,17 +3,21 @@ # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. -from twisted.internet.protocol import DatagramProtocol from twisted.internet import reactor +from twisted.internet.protocol import DatagramProtocol # Here's a UDP version of the simplest possible protocol + + class EchoUDP(DatagramProtocol): def datagramReceived(self, datagram, address): self.transport.write(datagram, address) + def main(): reactor.listenUDP(8000, EchoUDP()) reactor.run() -if __name__ == '__main__': + +if __name__ == "__main__": main() diff -Nru twisted-20.3.0/docs/core/examples/filewatch.py twisted-22.1.0/docs/core/examples/filewatch.py --- twisted-20.3.0/docs/core/examples/filewatch.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/examples/filewatch.py 2022-02-07 13:12:15.000000000 +0000 @@ -4,13 +4,17 @@ # from twisted.application import internet + def watch(fp): fp.seek(fp.tell()) for line in fp.readlines(): - sys.stdout.write(line) + sys.stdout.write(line) + import sys + from twisted.internet import reactor + s = internet.TimerService(0.1, watch, open(sys.argv[1])) s.startService() reactor.run() diff -Nru twisted-20.3.0/docs/core/examples/ftpclient.py twisted-22.1.0/docs/core/examples/ftpclient.py --- twisted-20.3.0/docs/core/examples/ftpclient.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/examples/ftpclient.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,4 +1,3 @@ - # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. @@ -6,90 +5,105 @@ """ An example of using the FTP client """ -from __future__ import print_function +# Standard library imports +from io import BytesIO + +from twisted.internet import reactor +from twisted.internet.protocol import ClientCreator, Protocol # Twisted imports from twisted.protocols.ftp import FTPClient, FTPFileListProtocol -from twisted.internet.protocol import Protocol, ClientCreator from twisted.python import usage -from twisted.internet import reactor - -# Standard library imports -import sys -from io import BytesIO class BufferingProtocol(Protocol): """Simple utility class that holds all data written to it in a buffer.""" + def __init__(self): self.buffer = BytesIO() def dataReceived(self, data): self.buffer.write(data) + # Define some callbacks + def success(response): - print('Success! Got response:') - print('---') + print("Success! Got response:") + print("---") if response is None: print(None) else: print("\n".join(response)) - print('---') + print("---") def fail(error): - print('Failed. Error was:') + print("Failed. Error was:") print(error) + def showFiles(result, fileListProtocol): - print('Processed file listing:') + print("Processed file listing:") for file in fileListProtocol.files: - print(' {}: {} bytes, {}'.format( - file['filename'], file['size'], file['date'])) - print('Total: {} files'.format(len(fileListProtocol.files))) + print( + " {}: {} bytes, {}".format(file["filename"], file["size"], file["date"]) + ) + print(f"Total: {len(fileListProtocol.files)} files") + def showBuffer(result, bufferProtocol): - print('Got data:') + print("Got data:") print(bufferProtocol.buffer.getvalue()) class Options(usage.Options): - optParameters = [['host', 'h', 'localhost'], - ['port', 'p', 21], - ['username', 'u', 'anonymous'], - ['password', None, 'twisted@'], - ['passive', None, 0], - ['debug', 'd', 1], - ] + optParameters = [ + ["host", "h", "localhost"], + ["port", "p", 21], + ["username", "u", "anonymous"], + ["password", None, "twisted@"], + ["passive", None, 0], + ["debug", "d", 1], + ] + def run(): # Get config config = Options() config.parseOptions() - config.opts['port'] = int(config.opts['port']) - config.opts['passive'] = int(config.opts['passive']) - config.opts['debug'] = int(config.opts['debug']) + config.opts["port"] = int(config.opts["port"]) + config.opts["passive"] = int(config.opts["passive"]) + config.opts["debug"] = int(config.opts["debug"]) # Create the client - FTPClient.debug = config.opts['debug'] - creator = ClientCreator(reactor, FTPClient, config.opts['username'], - config.opts['password'], passive=config.opts['passive']) - creator.connectTCP(config.opts['host'], config.opts['port']).addCallback(connectionMade).addErrback(connectionFailed) + FTPClient.debug = config.opts["debug"] + creator = ClientCreator( + reactor, + FTPClient, + config.opts["username"], + config.opts["password"], + passive=config.opts["passive"], + ) + creator.connectTCP(config.opts["host"], config.opts["port"]).addCallback( + connectionMade + ).addErrback(connectionFailed) reactor.run() + def connectionFailed(f): print("Connection Failed:", f) reactor.stop() + def connectionMade(ftpClient): # Get the current working directory ftpClient.pwd().addCallbacks(success, fail) # Get a detailed listing of the current directory fileList = FTPFileListProtocol() - d = ftpClient.list('.', fileList) + d = ftpClient.list(".", fileList) d.addCallbacks(showFiles, fail, callbackArgs=(fileList,)) # Change to the parent directory @@ -99,12 +113,11 @@ proto = BufferingProtocol() # Get short listing of current directory, and quit when done - d = ftpClient.nlst('.', proto) + d = ftpClient.nlst(".", proto) d.addCallbacks(showBuffer, fail, callbackArgs=(proto,)) d.addCallback(lambda result: reactor.stop()) # this only runs if the module was *not* imported -if __name__ == '__main__': +if __name__ == "__main__": run() - diff -Nru twisted-20.3.0/docs/core/examples/ftpserver.py twisted-22.1.0/docs/core/examples/ftpserver.py --- twisted-20.3.0/docs/core/examples/ftpserver.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/examples/ftpserver.py 2022-02-07 13:12:15.000000000 +0000 @@ -5,10 +5,10 @@ An example FTP server with minimal user authentication. """ -from twisted.protocols.ftp import FTPFactory, FTPRealm -from twisted.cred.portal import Portal from twisted.cred.checkers import AllowAnonymousAccess, FilePasswordDB +from twisted.cred.portal import Portal from twisted.internet import reactor +from twisted.protocols.ftp import FTPFactory, FTPRealm # # First, set up a portal (twisted.cred.portal.Portal). This will be used @@ -37,8 +37,7 @@ # grimmtooth:bozo2 # ===================== # -p = Portal(FTPRealm('./'), - [AllowAnonymousAccess(), FilePasswordDB("pass.dat")]) +p = Portal(FTPRealm("./"), [AllowAnonymousAccess(), FilePasswordDB("pass.dat")]) # # Once the portal is set up, start up the FTPFactory and pass the portal to diff -Nru twisted-20.3.0/docs/core/examples/longex2.py twisted-22.1.0/docs/core/examples/longex2.py --- twisted-20.3.0/docs/core/examples/longex2.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/examples/longex2.py 2022-02-07 13:12:15.000000000 +0000 @@ -45,8 +45,9 @@ which errors out when trying to do this. """ -from twisted.protocols import basic from twisted.internet import defer, protocol +from twisted.protocols import basic + def runIterator(reactor, iterator): try: @@ -56,16 +57,20 @@ else: reactor.callLater(0, runIterator, reactor, iterator) + def multiply(numbers): d = defer.Deferred() + def _(): acc = 1 while numbers: acc *= numbers.pop() yield None d.callback(acc) + return d, _() + class Numbers(basic.LineReceiver): """Protocol for reading lists of numbers and manipulating them. @@ -73,11 +78,12 @@ writes back the answer. The exact algorithm to use depends on the factory. It should return an str-able Deferred. """ + def lineReceived(self, line): try: numbers = [int(num) for num in line.split()] except ValueError: - self.sendLine(b'Error.') + self.sendLine(b"Error.") return deferred = self.factory.calc(numbers) @@ -87,6 +93,7 @@ deferred.addCallback(encodeNumber) deferred.addCallback(self.sendLine) + class Multiplication(protocol.ServerFactory): """Factory for multiplying numbers. @@ -96,14 +103,19 @@ for transmitting the number lists, as long as they set correct protoocl values. """ + protocol = Numbers + def calc(self, numbers): deferred, iterator = multiply(numbers) from twisted.internet import reactor + runIterator(reactor, iterator) return deferred -if __name__ == '__main__': + +if __name__ == "__main__": from twisted.internet import reactor + reactor.listenTCP(1234, Multiplication()) reactor.run() diff -Nru twisted-20.3.0/docs/core/examples/longex.py twisted-22.1.0/docs/core/examples/longex.py --- twisted-20.3.0/docs/core/examples/longex.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/examples/longex.py 2022-02-07 13:12:15.000000000 +0000 @@ -3,9 +3,10 @@ This is also a simple demonstration of twisted.protocols.basic.LineReceiver. """ -from twisted.protocols import basic from twisted.internet import reactor from twisted.internet.protocol import ServerFactory +from twisted.protocols import basic + class LongMultiplicationProtocol(basic.LineReceiver): """A protocol for doing long multiplications. @@ -14,6 +15,7 @@ writes back the answer. The answer is calculated in chunks, so no one calculation should block for long enough to matter. """ + def connectionMade(self): self.workQueue = [] @@ -21,11 +23,11 @@ try: numbers = [int(num) for num in line.split()] except ValueError: - self.sendLine(b'Error.') + self.sendLine(b"Error.") return if len(numbers) <= 1: - self.sendLine(b'Error.') + self.sendLine(b"Error.") return self.workQueue.append(numbers) @@ -57,10 +59,11 @@ protocol = LongMultiplicationProtocol -if __name__ == '__main__': - from twisted.python import log +if __name__ == "__main__": import sys + + from twisted.python import log + log.startLogging(sys.stdout) reactor.listenTCP(1234, LongMultiplicationFactory()) reactor.run() - diff -Nru twisted-20.3.0/docs/core/examples/pbbenchclient.py twisted-22.1.0/docs/core/examples/pbbenchclient.py --- twisted-20.3.0/docs/core/examples/pbbenchclient.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/examples/pbbenchclient.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,12 +1,12 @@ -from __future__ import print_function +import time -from twisted.spread import pb -from twisted.internet import defer, reactor from twisted.cred.credentials import UsernamePassword -import time +from twisted.internet import defer, reactor +from twisted.spread import pb + class PBBenchClient: - hostname = 'localhost' + hostname = "localhost" portno = pb.portno calledThisSecond = 0 @@ -19,7 +19,7 @@ if thisSecond != self.lastSecond: if thisSecond - self.lastSecond > 1: print("WARNING it took more than one second") - print('cps:', self.calledThisSecond) + print("cps:", self.calledThisSecond) self.calledThisSecond = 0 self.lastSecond = thisSecond @@ -31,13 +31,17 @@ def runTest(self): factory = pb.PBClientFactory() reactor.connectTCP(self.hostname, self.portno, factory) - factory.login(UsernamePassword(b"benchmark", b"benchmark")).addCallback(self._cbPerspective) + factory.login(UsernamePassword(b"benchmark", b"benchmark")).addCallback( + self._cbPerspective + ) def main(): PBBenchClient().runTest() from twisted.internet import reactor + reactor.run() -if __name__ == '__main__': + +if __name__ == "__main__": main() diff -Nru twisted-20.3.0/docs/core/examples/pbbenchserver.py twisted-22.1.0/docs/core/examples/pbbenchserver.py --- twisted-20.3.0/docs/core/examples/pbbenchserver.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/examples/pbbenchserver.py 2022-02-07 13:12:15.000000000 +0000 @@ -3,30 +3,31 @@ """Server for PB benchmark.""" -from __future__ import print_function from zope.interface import implementer -from twisted.spread import pb -from twisted.internet import reactor from twisted.cred.portal import IRealm +from twisted.internet import reactor +from twisted.spread import pb + class PBBenchPerspective(pb.Avatar): callsPerSec = 0 + def __init__(self): pass - + def perspective_simple(self): self.callsPerSec = self.callsPerSec + 1 return None def printCallsPerSec(self): - print('(s) cps:', self.callsPerSec) + print("(s) cps:", self.callsPerSec) self.callsPerSec = 0 reactor.callLater(1, self.printCallsPerSec) def perspective_complexTypes(self): - return ['a', 1, 1, 1.0, [], ()] + return ["a", 1, 1, 1.0, [], ()] @implementer(IRealm) @@ -35,14 +36,15 @@ if pb.IPerspective in interfaces: p = PBBenchPerspective() p.printCallsPerSec() - return pb.IPerspective, p, lambda : None + return pb.IPerspective, p, lambda: None else: raise NotImplementedError("no interface") def main(): - from twisted.cred.portal import Portal from twisted.cred.checkers import InMemoryUsernamePasswordDatabaseDontUse + from twisted.cred.portal import Portal + portal = Portal(SimpleRealm()) checker = InMemoryUsernamePasswordDatabaseDontUse() checker.addUser(b"benchmark", b"benchmark") @@ -50,5 +52,6 @@ reactor.listenTCP(8787, pb.PBServerFactory(portal)) reactor.run() -if __name__ == '__main__': + +if __name__ == "__main__": main() diff -Nru twisted-20.3.0/docs/core/examples/pbechoclient.py twisted-22.1.0/docs/core/examples/pbechoclient.py --- twisted-20.3.0/docs/core/examples/pbechoclient.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/examples/pbechoclient.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,33 +1,33 @@ - # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. -from __future__ import print_function +from pbecho import DefinedError + +from twisted.cred.credentials import UsernamePassword from twisted.internet import reactor from twisted.spread import pb -from twisted.cred.credentials import UsernamePassword -from pbecho import DefinedError def success(message): - print("Message received:",message) + print("Message received:", message) # reactor.stop() + def failure(error): t = error.trap(DefinedError) print("error received:", t) reactor.stop() + def connected(perspective): - perspective.callRemote('echo', "hello world").addCallbacks(success, failure) - perspective.callRemote('error').addCallbacks(success, failure) + perspective.callRemote("echo", "hello world").addCallbacks(success, failure) + perspective.callRemote("error").addCallbacks(success, failure) print("connected.") factory = pb.PBClientFactory() reactor.connectTCP("localhost", pb.portno, factory) -factory.login( - UsernamePassword("guest", "guest")).addCallbacks(connected, failure) +factory.login(UsernamePassword("guest", "guest")).addCallbacks(connected, failure) reactor.run() diff -Nru twisted-20.3.0/docs/core/examples/pbecho.py twisted-22.1.0/docs/core/examples/pbecho.py --- twisted-20.3.0/docs/core/examples/pbecho.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/examples/pbecho.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,25 +1,25 @@ # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. -from __future__ import print_function -if __name__ == '__main__': +if __name__ == "__main__": # Avoid using any names defined in the "__main__" module. from pbecho import main + raise SystemExit(main()) from zope.interface import implementer -from twisted.spread import pb from twisted.cred.portal import IRealm +from twisted.spread import pb + class DefinedError(pb.Error): pass class SimplePerspective(pb.Avatar): - def perspective_echo(self, text): - print('echoing',text) + print("echoing", text) return text def perspective_error(self): @@ -34,15 +34,16 @@ def requestAvatar(self, avatarId, mind, *interfaces): if pb.IPerspective in interfaces: avatar = SimplePerspective() - return pb.IPerspective, avatar, avatar.logout + return pb.IPerspective, avatar, avatar.logout else: raise NotImplementedError("no interface") def main(): - from twisted.internet import reactor - from twisted.cred.portal import Portal from twisted.cred.checkers import InMemoryUsernamePasswordDatabaseDontUse + from twisted.cred.portal import Portal + from twisted.internet import reactor + portal = Portal(SimpleRealm()) checker = InMemoryUsernamePasswordDatabaseDontUse() checker.addUser("guest", "guest") diff -Nru twisted-20.3.0/docs/core/examples/pb_exceptions.py twisted-22.1.0/docs/core/examples/pb_exceptions.py --- twisted-20.3.0/docs/core/examples/pb_exceptions.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/examples/pb_exceptions.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,24 +1,27 @@ -from __future__ import print_function - +from twisted.cred import checkers, credentials, portal from twisted.python import util from twisted.spread import pb -from twisted.cred import portal, checkers, credentials + class Avatar(pb.Avatar): def perspective_exception(self, x): return x / 0 + class Realm: def requestAvatar(self, interface, mind, *interfaces): if pb.IPerspective in interfaces: return pb.IPerspective, Avatar(), lambda: None + def cbLogin(avatar): avatar.callRemote(b"exception", 10).addCallback(str).addCallback(util.println) + def ebLogin(failure): print(failure) + def main(): c = checkers.InMemoryUsernamePasswordDatabaseDontUse(user=b"pass") p = portal.Portal(Realm(), [c]) @@ -29,9 +32,11 @@ login.addCallback(cbLogin).addErrback(ebLogin).addBoth(lambda: reactor.stop()) from twisted.internet import reactor + p = reactor.listenTCP(0, server) - c = reactor.connectTCP('127.0.0.1', p.getHost().port, client) + c = reactor.connectTCP("127.0.0.1", p.getHost().port, client) reactor.run() -if __name__ == '__main__': + +if __name__ == "__main__": main() diff -Nru twisted-20.3.0/docs/core/examples/pbgtk2.py twisted-22.1.0/docs/core/examples/pbgtk2.py --- twisted-20.3.0/docs/core/examples/pbgtk2.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/examples/pbgtk2.py 2022-02-07 13:12:15.000000000 +0000 @@ -2,16 +2,16 @@ # See LICENSE for details. from twisted.internet import gtk2reactor + gtk2reactor.install() import gtk from gtk import glade -from twisted import copyright -from twisted.internet import reactor, defer -from twisted.python import failure, log, util -from twisted.spread import pb + from twisted.cred.credentials import UsernamePassword -from twisted.internet import error as netError +from twisted.internet import defer, reactor +from twisted.python import failure, util +from twisted.spread import pb class LoginDialog: @@ -27,8 +27,14 @@ self._loginDialog.show() def setWidgetsFromGladefile(self): - widgets = ("hostEntry", "portEntry", "userNameEntry", "passwordEntry", - "statusBar", "loginDialog") + widgets = ( + "hostEntry", + "portEntry", + "userNameEntry", + "passwordEntry", + "statusBar", + "loginDialog", + ) gw = self.glade.get_widget for widgetName in widgets: setattr(self, "_" + widgetName, gw(widgetName)) @@ -36,10 +42,12 @@ self._statusContext = self._statusBar.get_context_id("Login dialog.") def on_loginDialog_response(self, widget, response): - handlers = {gtk.RESPONSE_NONE: self.windowClosed, - gtk.RESPONSE_DELETE_EVENT: self.windowClosed, - gtk.RESPONSE_OK: self.doLogin, - gtk.RESPONSE_CANCEL: self.cancelled} + handlers = { + gtk.RESPONSE_NONE: self.windowClosed, + gtk.RESPONSE_DELETE_EVENT: self.windowClosed, + gtk.RESPONSE_OK: self.doLogin, + gtk.RESPONSE_CANCEL: self.cancelled, + } handlers.get(response)() def on_loginDialog_close(self, widget, userdata=None): @@ -51,7 +59,7 @@ self._loginDialog.destroy() def windowClosed(self, reason=None): - if not self.deferredResult.called: + if not self.deferredResult.called: self.deferredResult.errback() def doLogin(self): @@ -63,7 +71,9 @@ client_factory = pb.PBClientFactory() reactor.connectTCP(host, port, client_factory) creds = UsernamePassword(userName, password) - client_factory.login(creds).addCallbacks(self._cbGotPerspective, self._ebFailedLogin) + client_factory.login(creds).addCallbacks( + self._cbGotPerspective, self._ebFailedLogin + ) self.statusMsg("Contacting server...") @@ -79,11 +89,13 @@ text = str(reason) self.statusMsg(text) - msg = gtk.MessageDialog(self._loginDialog, - gtk.DIALOG_DESTROY_WITH_PARENT, - gtk.MESSAGE_ERROR, - gtk.BUTTONS_CLOSE, - text) + msg = gtk.MessageDialog( + self._loginDialog, + gtk.DIALOG_DESTROY_WITH_PARENT, + gtk.MESSAGE_ERROR, + gtk.BUTTONS_CLOSE, + text, + ) msg.show_all() msg.connect("response", lambda *a: msg.destroy()) @@ -95,25 +107,27 @@ def __init__(self, echoer): self.echoer = echoer w = gtk.Window(gtk.WINDOW_TOPLEVEL) - vb = gtk.VBox(); b = gtk.Button("Echo:") - self.entry = gtk.Entry(); self.outry = gtk.Entry() + vb = gtk.VBox() + b = gtk.Button("Echo:") + self.entry = gtk.Entry() + self.outry = gtk.Entry() w.add(vb) map(vb.add, [b, self.entry, self.outry]) - b.connect('clicked', self.clicked) - w.connect('destroy', self.stop) + b.connect("clicked", self.clicked) + w.connect("destroy", self.stop) w.show_all() def clicked(self, b): txt = self.entry.get_text() self.entry.set_text("") - self.echoer.callRemote('echo',txt).addCallback(self.outry.set_text) + self.echoer.callRemote("echo", txt).addCallback(self.outry.set_text) def stop(self, b): reactor.stop() + d = defer.Deferred() LoginDialog(d) -d.addCallbacks(EchoClient, - lambda _: reactor.stop()) +d.addCallbacks(EchoClient, lambda _: reactor.stop()) reactor.run() diff -Nru twisted-20.3.0/docs/core/examples/pbinterop.py twisted-22.1.0/docs/core/examples/pbinterop.py --- twisted-20.3.0/docs/core/examples/pbinterop.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/examples/pbinterop.py 2022-02-07 13:12:15.000000000 +0000 @@ -4,8 +4,8 @@ """PB interop server.""" -from twisted.spread import pb, flavors from twisted.internet import reactor +from twisted.spread import flavors, pb class Interop(pb.Root): @@ -21,7 +21,7 @@ return "string" def remote_unicode(self): - return u"string" + return "string" def remote_float(self): return 1.5 @@ -35,7 +35,7 @@ return l def remote_dict(self): - return {1 : 2} + return {1: 2} def remote_reference(self): return self.o @@ -49,13 +49,13 @@ raise ValueError("{} != {}".format(result, "hello, world")) def remote_receive(self, obj): - expected = [1, 1.5, "hi", u"hi", {1 : 2}] + expected = [1, 1.5, "hi", "hi", {1: 2}] if obj != expected: - raise ValueError("{} != {}".format(obj, expected)) + raise ValueError(f"{obj} != {expected}") def remote_self(self, obj): if obj != self: - raise ValueError("{} != {}".format(obj, self)) + raise ValueError(f"{obj} != {self}") def remote_copy(self, x): o = flavors.Copyable() @@ -63,9 +63,6 @@ return o -if __name__ == '__main__': +if __name__ == "__main__": reactor.listenTCP(8789, pb.PBServerFactory(Interop())) reactor.run() - - - diff -Nru twisted-20.3.0/docs/core/examples/pbsimpleclient.py twisted-22.1.0/docs/core/examples/pbsimpleclient.py --- twisted-20.3.0/docs/core/examples/pbsimpleclient.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/examples/pbsimpleclient.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,18 +1,17 @@ - # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. -from twisted.spread import pb from twisted.internet import reactor from twisted.python import util +from twisted.spread import pb factory = pb.PBClientFactory() reactor.connectTCP("localhost", 8789, factory) d = factory.getRootObject() d.addCallback(lambda object: object.callRemote("echo", "hello network")) -d.addCallback(lambda echo: 'server echoed: '+echo) -d.addErrback(lambda reason: 'error: '+str(reason.value)) +d.addCallback(lambda echo: "server echoed: " + echo) +d.addErrback(lambda reason: "error: " + str(reason.value)) d.addCallback(util.println) d.addCallback(lambda _: reactor.stop()) reactor.run() diff -Nru twisted-20.3.0/docs/core/examples/pbsimple.py twisted-22.1.0/docs/core/examples/pbsimple.py --- twisted-20.3.0/docs/core/examples/pbsimple.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/examples/pbsimple.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,17 +1,17 @@ - # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. -from __future__ import print_function -from twisted.spread import pb from twisted.internet import reactor +from twisted.spread import pb + class Echoer(pb.Root): def remote_echo(self, st): - print('echoing:', st) + print("echoing:", st) return st -if __name__ == '__main__': + +if __name__ == "__main__": reactor.listenTCP(8789, pb.PBServerFactory(Echoer())) reactor.run() diff -Nru twisted-20.3.0/docs/core/examples/postfix.py twisted-22.1.0/docs/core/examples/postfix.py --- twisted-20.3.0/docs/core/examples/postfix.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/examples/postfix.py 2022-02-07 13:12:15.000000000 +0000 @@ -18,12 +18,12 @@ d = {} for arg in sys.argv[1:]: try: - k,v = arg.split('=', 1) + k, v = arg.split("=", 1) except ValueError: k = arg - v = '' + v = "" d[k] = v - + f = postfix.PostfixTCPMapDictServerFactory(d) -port = reactor.listenTCP(4242, f, interface='127.0.0.1') +port = reactor.listenTCP(4242, f, interface="127.0.0.1") reactor.run() diff -Nru twisted-20.3.0/docs/core/examples/ptyserv.py twisted-22.1.0/docs/core/examples/ptyserv.py --- twisted-20.3.0/docs/core/examples/ptyserv.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/examples/ptyserv.py 2022-02-07 13:12:15.000000000 +0000 @@ -10,36 +10,46 @@ Telnet to the server once you start it by typing in: > telnet localhost 5823 """ -from __future__ import print_function -from twisted.internet import reactor, protocol +from twisted.internet import protocol, reactor + class FakeTelnet(protocol.Protocol): - commandToRun = ['/bin/sh'] # could have args too - dirToRunIn = '/tmp' + commandToRun = ["/bin/sh"] # could have args too + dirToRunIn = "/tmp" + def connectionMade(self): - print('connection made') + print("connection made") self.propro = ProcessProtocol(self) - reactor.spawnProcess(self.propro, self.commandToRun[0], self.commandToRun, {}, - self.dirToRunIn, usePTY=1) + reactor.spawnProcess( + self.propro, + self.commandToRun[0], + self.commandToRun, + {}, + self.dirToRunIn, + usePTY=1, + ) + def dataReceived(self, data): self.propro.transport.write(data) + def connectionLost(self, reason): - print('connection lost') + print("connection lost") self.propro.tranport.loseConnection() -class ProcessProtocol(protocol.ProcessProtocol): +class ProcessProtocol(protocol.ProcessProtocol): def __init__(self, pr): self.pr = pr def outReceived(self, data): self.pr.transport.write(data) - + def processEnded(self, reason): - print('protocol connection lost') + print("protocol connection lost") self.pr.transport.loseConnection() + f = protocol.Factory() f.protocol = FakeTelnet reactor.listenTCP(5823, f) diff -Nru twisted-20.3.0/docs/core/examples/pyuidemo.py twisted-22.1.0/docs/core/examples/pyuidemo.py --- twisted-20.3.0/docs/core/examples/pyuidemo.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/examples/pyuidemo.py 2022-02-07 13:12:15.000000000 +0000 @@ -11,24 +11,27 @@ Select "Quit" button to exit demo. """ -from __future__ import print_function import pyui -from twisted.internet import reactor, pyuisupport + +from twisted.internet import pyuisupport, reactor + def onButton(self): print("got a button") + def onQuit(self): reactor.stop() + def main(): - pyuisupport.install(args=(640, 480), kw={'renderer': '2d'}) + pyuisupport.install(args=(640, 480), kw={"renderer": "2d"}) w = pyui.widgets.Frame(50, 50, 400, 400, "clipme") b = pyui.widgets.Button("A button is here", onButton) q = pyui.widgets.Button("Quit!", onQuit) - + w.addChild(b) w.addChild(q) w.pack() @@ -36,5 +39,6 @@ w.setBackImage("pyui_bg.png") reactor.run() -if __name__ == '__main__': + +if __name__ == "__main__": main() diff -Nru twisted-20.3.0/docs/core/examples/recvfd.py twisted-22.1.0/docs/core/examples/recvfd.py --- twisted-20.3.0/docs/core/examples/recvfd.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/examples/recvfd.py 2022-02-07 13:12:15.000000000 +0000 @@ -14,24 +14,26 @@ See sendfd.py for the server side of this example. """ -from __future__ import print_function -if __name__ == '__main__': +if __name__ == "__main__": import recvfd + raise SystemExit(recvfd.main()) -import os, sys +import os +import sys from zope.interface import implementer -from twisted.python.log import startLogging -from twisted.python.filepath import FilePath +from twisted.internet import reactor from twisted.internet.defer import Deferred +from twisted.internet.endpoints import UNIXClientEndpoint from twisted.internet.interfaces import IFileDescriptorReceiver from twisted.internet.protocol import Factory from twisted.protocols.basic import LineOnlyReceiver -from twisted.internet.endpoints import UNIXClientEndpoint -from twisted.internet import reactor +from twisted.python.filepath import FilePath +from twisted.python.log import startLogging + @implementer(IFileDescriptorReceiver) class ReceiveFDProtocol(LineOnlyReceiver): @@ -41,31 +43,27 @@ def __init__(self): self.whenDisconnected = Deferred() - def fileDescriptorReceived(self, descriptor): # Record the descriptor sent to us self.descriptor = descriptor - def lineReceived(self, line): if self.descriptor is None: - print("Received {} without receiving descriptor!".format(line)) + print(f"Received {line} without receiving descriptor!") else: # Use the previously received descriptor, along with the newly # provided information about which file it is, to present some # information to the user. data = os.read(self.descriptor, 80) - print("Received {} from the server.".format(line)) - print("First 80 bytes are:\n{}\n".format(data)) + print(f"Received {line} from the server.") + print(f"First 80 bytes are:\n{data}\n") os.close(self.descriptor) self.transport.loseConnection() - def connectionLost(self, reason): self.whenDisconnected.callback(None) - def main(): address = FilePath(sys.argv[1]) @@ -80,8 +78,10 @@ def succeeded(client): return client.whenDisconnected + def failed(reason): print("Could not connect:", reason.getErrorMessage()) + def disconnected(ignored): reactor.stop() diff -Nru twisted-20.3.0/docs/core/examples/rotatinglog.py twisted-22.1.0/docs/core/examples/rotatinglog.py --- twisted-20.3.0/docs/core/examples/rotatinglog.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/examples/rotatinglog.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,4 +1,3 @@ - # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. @@ -7,8 +6,7 @@ An example of using the rotating log. """ -from twisted.python import log -from twisted.python import logfile +from twisted.python import log, logfile # rotate every 100 bytes f = logfile.LogFile("test.log", "/tmp", rotateLength=100) @@ -18,7 +16,7 @@ # print a few message for i in range(10): - log.msg("this is a test of the logfile: {}".format(i)) + log.msg(f"this is a test of the logfile: {i}") # rotate the logfile manually f.rotate() diff -Nru twisted-20.3.0/docs/core/examples/sendfd.py twisted-22.1.0/docs/core/examples/sendfd.py --- twisted-20.3.0/docs/core/examples/sendfd.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/examples/sendfd.py 2022-02-07 13:12:15.000000000 +0000 @@ -18,17 +18,19 @@ See recvfd.py for the client side of this example. """ -if __name__ == '__main__': +if __name__ == "__main__": import sendfd + raise SystemExit(sendfd.main()) import sys -from twisted.python.log import startLogging -from twisted.python.filepath import FilePath +from twisted.internet import reactor from twisted.internet.protocol import Factory from twisted.protocols.basic import LineOnlyReceiver -from twisted.internet import reactor +from twisted.python.filepath import FilePath +from twisted.python.log import startLogging + class SendFDProtocol(LineOnlyReceiver): def connectionMade(self): @@ -52,7 +54,6 @@ # the connection by then, we will do it for them. self.timeoutCall = reactor.callLater(60, self.transport.loseConnection) - def connectionLost(self, reason): # Clean up the file object, it is no longer needed. self.fObj.close() @@ -80,5 +81,5 @@ serverFactory.content = content serverFactory.protocol = SendFDProtocol - port = reactor.listenUNIX(address.path, serverFactory) + reactor.listenUNIX(address.path, serverFactory) reactor.run() diff -Nru twisted-20.3.0/docs/core/examples/shaper.py twisted-22.1.0/docs/core/examples/shaper.py --- twisted-20.3.0/docs/core/examples/shaper.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/examples/shaper.py 2022-02-07 13:12:15.000000000 +0000 @@ -10,10 +10,11 @@ costing you more bandwidth than you are saving by limiting the rate! """ -from twisted.protocols import htb # for picklability import shaper +from twisted.protocols import htb + serverFilter = htb.HierarchicalBucketFilter() serverBucket = htb.Bucket() @@ -24,29 +25,34 @@ serverFilter.buckets[None] = serverBucket # Web service is also limited per-host: + + class WebClientBucket(htb.Bucket): # Your first 10k is free maxburst = 10000 # One kB/s thereafter. rate = 1000 + webFilter = htb.FilterByHost(serverFilter) webFilter.bucketFactory = shaper.WebClientBucket -servertype = "web" # "chargen" +servertype = "web" # "chargen" if servertype == "web": from twisted.web import server, static + site = server.Site(static.File("/var/www")) site.protocol = htb.ShapedProtocolFactory(site.protocol, webFilter) elif servertype == "chargen": - from twisted.protocols import wire from twisted.internet import protocol + from twisted.protocols import wire site = protocol.ServerFactory() site.protocol = htb.ShapedProtocolFactory(wire.Chargen, webFilter) - #site.protocol = wire.Chargen + # site.protocol = wire.Chargen from twisted.internet import reactor + reactor.listenTCP(8000, site) reactor.run() diff -Nru twisted-20.3.0/docs/core/examples/shoutcast.py twisted-22.1.0/docs/core/examples/shoutcast.py --- twisted-20.3.0/docs/core/examples/shoutcast.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/examples/shoutcast.py 2022-02-07 13:12:15.000000000 +0000 @@ -6,13 +6,13 @@ python shoutcast.py localhost 8080 """ -from __future__ import print_function import sys from twisted.internet import protocol, reactor from twisted.protocols.shoutcast import ShoutcastClient + class Test(ShoutcastClient): def gotMetaData(self, data): print("meta:", data) @@ -20,6 +20,7 @@ def gotMP3Data(self, data): pass + host = sys.argv[1] port = int(sys.argv[2]) diff -Nru twisted-20.3.0/docs/core/examples/simpleclient.py twisted-22.1.0/docs/core/examples/simpleclient.py --- twisted-20.3.0/docs/core/examples/simpleclient.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/examples/simpleclient.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,4 +1,3 @@ - # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. @@ -6,34 +5,34 @@ """ An example client. Run simpleserv.py first before running this. """ -from __future__ import print_function - -from twisted.internet import reactor, protocol +from twisted.internet import protocol, reactor # a client protocol + class EchoClient(protocol.Protocol): """Once connected, send a message, then print the result.""" - + def connectionMade(self): self.transport.write(b"hello, world!") - + def dataReceived(self, data): "As soon as any data is received, write it back." print("Server said:", data) self.transport.loseConnection() - + def connectionLost(self, reason): print("connection lost") + class EchoFactory(protocol.ClientFactory): protocol = EchoClient def clientConnectionFailed(self, connector, reason): print("Connection failed - goodbye!") reactor.stop() - + def clientConnectionLost(self, connector, reason): print("Connection lost - goodbye!") reactor.stop() @@ -45,6 +44,7 @@ reactor.connectTCP("localhost", 8000, f) reactor.run() + # this only runs if the module was *not* imported -if __name__ == '__main__': +if __name__ == "__main__": main() diff -Nru twisted-20.3.0/docs/core/examples/simpleserv.py twisted-22.1.0/docs/core/examples/simpleserv.py --- twisted-20.3.0/docs/core/examples/simpleserv.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/examples/simpleserv.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,14 +1,13 @@ - # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. -from twisted.internet import reactor, protocol +from twisted.internet import protocol, reactor class Echo(protocol.Protocol): """This is just about the simplest possible protocol""" - + def dataReceived(self, data): "As soon as any data is received, write it back." self.transport.write(data) @@ -18,9 +17,10 @@ """This runs the protocol on port 8000""" factory = protocol.ServerFactory() factory.protocol = Echo - reactor.listenTCP(8000,factory) + reactor.listenTCP(8000, factory) reactor.run() + # this only runs if the module was *not* imported -if __name__ == '__main__': +if __name__ == "__main__": main() diff -Nru twisted-20.3.0/docs/core/examples/simple.tac twisted-22.1.0/docs/core/examples/simple.tac --- twisted-20.3.0/docs/core/examples/simple.tac 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/examples/simple.tac 2022-02-07 13:12:15.000000000 +0000 @@ -1,14 +1,13 @@ # You can run this .tac file directly with: # twistd -ny simple.tac -from __future__ import print_function -from twisted.application import service, internet -from twisted.protocols import wire +from twisted.application import internet, service from twisted.internet import protocol +from twisted.protocols import wire from twisted.python import util -application = service.Application('test') +application = service.Application("test") s = service.IServiceCollection(application) factory = protocol.ServerFactory() factory.protocol = wire.Echo @@ -17,25 +16,31 @@ internet.TCPServer(8081, factory).setServiceParent(s) internet.TimerService(5, util.println, "--MARK--").setServiceParent(s) + class Foo(protocol.Protocol): def connectionMade(self): - self.transport.write(b'lalala\n') + self.transport.write(b"lalala\n") + def dataReceived(self, data): print(data) + factory = protocol.ClientFactory() factory.protocol = Foo -internet.TCPClient('localhost', 8081, factory).setServiceParent(s) +internet.TCPClient("localhost", 8081, factory).setServiceParent(s) + class FooService(service.Service): def startService(self): service.Service.startService(self) - print('lala, starting') + print("lala, starting") + def stopService(self): service.Service.stopService(self) - print('lala, stopping') + print("lala, stopping") print(self.parent.getServiceNamed(self.name) is self) + foo = FooService() -foo.setName('foo') +foo.setName("foo") foo.setServiceParent(s) diff -Nru twisted-20.3.0/docs/core/examples/ssl_clientauth_client.py twisted-22.1.0/docs/core/examples/ssl_clientauth_client.py --- twisted-20.3.0/docs/core/examples/ssl_clientauth_client.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/examples/ssl_clientauth_client.py 2022-02-07 13:12:15.000000000 +0000 @@ -2,28 +2,29 @@ # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. -from twisted.internet import ssl, task, protocol, endpoints, defer +import echoclient + +from twisted.internet import defer, endpoints, protocol, ssl, task from twisted.python.modules import getModule -import echoclient @defer.inlineCallbacks def main(reactor): factory = protocol.Factory.forProtocol(echoclient.EchoClient) - certData = getModule(__name__).filePath.sibling('public.pem').getContent() - authData = getModule(__name__).filePath.sibling('server.pem').getContent() + certData = getModule(__name__).filePath.sibling("public.pem").getContent() + authData = getModule(__name__).filePath.sibling("server.pem").getContent() clientCertificate = ssl.PrivateCertificate.loadPEM(authData) authority = ssl.Certificate.loadPEM(certData) - options = ssl.optionsForClientTLS(u'example.com', authority, - clientCertificate) - endpoint = endpoints.SSL4ClientEndpoint(reactor, 'localhost', 8000, - options) + options = ssl.optionsForClientTLS("example.com", authority, clientCertificate) + endpoint = endpoints.SSL4ClientEndpoint(reactor, "localhost", 8000, options) echoClient = yield endpoint.connect(factory) done = defer.Deferred() echoClient.connectionLost = lambda reason: done.callback(None) yield done -if __name__ == '__main__': + +if __name__ == "__main__": import ssl_clientauth_client + task.react(ssl_clientauth_client.main) diff -Nru twisted-20.3.0/docs/core/examples/ssl_clientauth_server.py twisted-22.1.0/docs/core/examples/ssl_clientauth_server.py --- twisted-20.3.0/docs/core/examples/ssl_clientauth_server.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/examples/ssl_clientauth_server.py 2022-02-07 13:12:15.000000000 +0000 @@ -4,22 +4,25 @@ import sys -from twisted.internet import ssl, protocol, task, defer +import echoserv + +from twisted.internet import defer, protocol, ssl, task from twisted.python import log from twisted.python.modules import getModule -import echoserv def main(reactor): log.startLogging(sys.stdout) - certData = getModule(__name__).filePath.sibling('public.pem').getContent() - authData = getModule(__name__).filePath.sibling('server.pem').getContent() + certData = getModule(__name__).filePath.sibling("public.pem").getContent() + authData = getModule(__name__).filePath.sibling("server.pem").getContent() authority = ssl.Certificate.loadPEM(certData) certificate = ssl.PrivateCertificate.loadPEM(authData) factory = protocol.Factory.forProtocol(echoserv.Echo) reactor.listenSSL(8000, factory, certificate.options(authority)) return defer.Deferred() -if __name__ == '__main__': + +if __name__ == "__main__": import ssl_clientauth_server + task.react(ssl_clientauth_server.main) diff -Nru twisted-20.3.0/docs/core/examples/starttls_client.py twisted-22.1.0/docs/core/examples/starttls_client.py --- twisted-20.3.0/docs/core/examples/starttls_client.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/examples/starttls_client.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,9 +1,8 @@ -from __future__ import print_function - -from twisted.internet import ssl, endpoints, task, protocol, defer +from twisted.internet import defer, endpoints, protocol, ssl, task from twisted.protocols.basic import LineReceiver from twisted.python.modules import getModule + class StartTLSClient(LineReceiver): def connectionMade(self): self.sendLine(b"plain text") @@ -16,20 +15,23 @@ self.sendLine(b"secure text") self.transport.loseConnection() + @defer.inlineCallbacks def main(reactor): factory = protocol.Factory.forProtocol(StartTLSClient) - certData = getModule(__name__).filePath.sibling('server.pem').getContent() + certData = getModule(__name__).filePath.sibling("server.pem").getContent() factory.options = ssl.optionsForClientTLS( - u"example.com", ssl.PrivateCertificate.loadPEM(certData) + "example.com", ssl.PrivateCertificate.loadPEM(certData) ) - endpoint = endpoints.HostnameEndpoint(reactor, 'localhost', 8000) + endpoint = endpoints.HostnameEndpoint(reactor, "localhost", 8000) startTLSClient = yield endpoint.connect(factory) done = defer.Deferred() startTLSClient.connectionLost = lambda reason: done.callback(None) yield done + if __name__ == "__main__": import starttls_client + task.react(starttls_client.main) diff -Nru twisted-20.3.0/docs/core/examples/starttls_server.py twisted-22.1.0/docs/core/examples/starttls_server.py --- twisted-20.3.0/docs/core/examples/starttls_server.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/examples/starttls_server.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,19 +1,19 @@ -from __future__ import print_function - -from twisted.internet import ssl, protocol, defer, task, endpoints +from twisted.internet import defer, endpoints, protocol, ssl, task from twisted.protocols.basic import LineReceiver from twisted.python.modules import getModule + class TLSServer(LineReceiver): def lineReceived(self, line): print("received: ", line) if line == b"STARTTLS": print("-- Switching to TLS") - self.sendLine(b'READY') + self.sendLine(b"READY") self.transport.startTLS(self.factory.options) + def main(reactor): - certData = getModule(__name__).filePath.sibling('server.pem').getContent() + certData = getModule(__name__).filePath.sibling("server.pem").getContent() cert = ssl.PrivateCertificate.loadPEM(certData) factory = protocol.Factory.forProtocol(TLSServer) factory.options = cert.options() @@ -21,6 +21,8 @@ endpoint.listen(factory) return defer.Deferred() -if __name__ == '__main__': + +if __name__ == "__main__": import starttls_server + task.react(starttls_server.main) diff -Nru twisted-20.3.0/docs/core/examples/stdin.py twisted-22.1.0/docs/core/examples/stdin.py --- twisted-20.3.0/docs/core/examples/stdin.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/examples/stdin.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,4 +1,3 @@ - # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. @@ -8,24 +7,29 @@ without blocking the reactor. """ +from os import linesep + from twisted.internet import stdio from twisted.protocols import basic -from os import linesep + class Echo(basic.LineReceiver): delimiter = linesep.encode("ascii") def connectionMade(self): - self.transport.write(b'>>> ') + self.transport.write(b">>> ") def lineReceived(self, line): - self.sendLine(b'Echo: ' + line) - self.transport.write(b'>>> ') + self.sendLine(b"Echo: " + line) + self.transport.write(b">>> ") + def main(): stdio.StandardIO(Echo()) from twisted.internet import reactor + reactor.run() -if __name__ == '__main__': + +if __name__ == "__main__": main() diff -Nru twisted-20.3.0/docs/core/examples/stdiodemo.py twisted-22.1.0/docs/core/examples/stdiodemo.py --- twisted-20.3.0/docs/core/examples/stdiodemo.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/examples/stdiodemo.py 2022-02-07 13:12:15.000000000 +0000 @@ -12,20 +12,22 @@ Based on an example by Abe Fettig. """ -from twisted.internet import stdio, reactor +from twisted.internet import reactor, stdio from twisted.protocols import basic from twisted.web import client + class WebCheckerCommandProtocol(basic.LineReceiver): - delimiter = b'\n' # unix terminal style newlines. remove this line - # for use with Telnet + delimiter = b"\n" # unix terminal style newlines. remove this line + # for use with Telnet def connectionMade(self): self.sendLine(b"Web checker console. Type 'help' for help.") def lineReceived(self, line): # Ignore blank lines - if not line: return + if not line: + return line = line.decode("ascii") # Parse the command @@ -36,41 +38,40 @@ # Dispatch the command to the appropriate method. Note that all you # need to do to implement a new command is add another do_* method. try: - method = getattr(self, 'do_' + command) - except AttributeError as e: - self.sendLine(b'Error: no such command.') + method = getattr(self, "do_" + command) + except AttributeError: + self.sendLine(b"Error: no such command.") else: try: method(*args) except Exception as e: - self.sendLine(b'Error: ' + str(e).encode("ascii")) + self.sendLine(b"Error: " + str(e).encode("ascii")) def do_help(self, command=None): """help [command]: List commands, or show help on the given command""" if command: - doc = getattr(self, 'do_' + command).__doc__ + doc = getattr(self, "do_" + command).__doc__ self.sendLine(doc.encode("ascii")) else: - commands = [cmd[3:].encode("ascii") - for cmd in dir(self) - if cmd.startswith('do_')] + commands = [ + cmd[3:].encode("ascii") for cmd in dir(self) if cmd.startswith("do_") + ] self.sendLine(b"Valid commands: " + b" ".join(commands)) def do_quit(self): """quit: Quit this session""" - self.sendLine(b'Goodbye.') + self.sendLine(b"Goodbye.") self.transport.loseConnection() - + def do_check(self, url): """check : Attempt to download the given web page""" url = url.encode("ascii") - client.Agent(reactor).request(b'GET', url).addCallback( - client.readBody).addCallback( - self.__checkSuccess).addErrback( - self.__checkFailure) + client.Agent(reactor).request(b"GET", url).addCallback( + client.readBody + ).addCallback(self.__checkSuccess).addErrback(self.__checkFailure) def __checkSuccess(self, pageData): - msg = "Success: got {} bytes.".format(len(pageData)) + msg = f"Success: got {len(pageData)} bytes." self.sendLine(msg.encode("ascii")) def __checkFailure(self, failure): @@ -81,6 +82,7 @@ # stop the reactor, only because this is meant to be run in Stdio. reactor.stop() + if __name__ == "__main__": stdio.StandardIO(WebCheckerCommandProtocol()) reactor.run() diff -Nru twisted-20.3.0/docs/core/examples/streaming.py twisted-22.1.0/docs/core/examples/streaming.py --- twisted-20.3.0/docs/core/examples/streaming.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/examples/streaming.py 2022-02-07 13:12:15.000000000 +0000 @@ -10,20 +10,20 @@ and finally closes the connection. """ -from __future__ import print_function -from sys import stdout from random import randrange +from sys import stdout from zope.interface import implementer -from twisted.python.log import startLogging + from twisted.internet import interfaces, reactor from twisted.internet.protocol import Factory from twisted.protocols.basic import LineReceiver +from twisted.python.log import startLogging @implementer(interfaces.IPushProducer) -class Producer(object): +class Producer: """ Send back the requested number of random integers to the client. """ @@ -41,7 +41,7 @@ likely), so set a flag that causes production to pause temporarily. """ self._paused = True - print('Pausing connection from {}'.format(self._proto.transport.getPeer())) + print(f"Pausing connection from {self._proto.transport.getPeer()}") def resumeProducing(self): """ @@ -55,7 +55,7 @@ while not self._paused and self._produced < self._goal: next_int = randrange(0, 10000) - line = "{}".format(next_int) + line = f"{next_int}" self._proto.sendLine(line.encode("ascii")) self._produced += 1 @@ -80,8 +80,8 @@ Once the connection is made we ask the client how many random integers the producer should return. """ - print('Connection made from {}'.format(self.transport.getPeer())) - self.sendLine(b'How many random integers do you want?') + print(f"Connection made from {self.transport.getPeer()}") + self.sendLine(b"How many random integers do you want?") def lineReceived(self, line): """ @@ -89,13 +89,13 @@ tells the producer to start generating the data. """ count = int(line.strip()) - print('Client requested {} random integers!'.format(count)) + print(f"Client requested {count} random integers!") producer = Producer(self, count) self.transport.registerProducer(producer, True) producer.resumeProducing() def connectionLost(self, reason): - print('Connection lost from {}'.format(self.transport.getPeer())) + print(f"Connection lost from {self.transport.getPeer()}") startLogging(stdout) diff -Nru twisted-20.3.0/docs/core/examples/testlogging.py twisted-22.1.0/docs/core/examples/testlogging.py --- twisted-20.3.0/docs/core/examples/testlogging.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/examples/testlogging.py 2022-02-07 13:12:15.000000000 +0000 @@ -7,28 +7,32 @@ Message should only be printed second time around. """ -from __future__ import print_function -from twisted.python import log +import sys +import warnings + from twisted.internet import reactor +from twisted.python import log -import sys, warnings def test(i): print("printed", i) - log.msg("message {}".format(i)) - warnings.warn("warning {}".format(i)) + log.msg(f"message {i}") + warnings.warn(f"warning {i}") try: - raise RuntimeError("error {}".format(i)) - except: + raise RuntimeError(f"error {i}") + except BaseException: log.err() + def startlog(): log.startLogging(sys.stdout) + def end(): reactor.stop() + # pre-reactor run test(1) diff -Nru twisted-20.3.0/docs/core/examples/threadedselect/blockingdemo.py twisted-22.1.0/docs/core/examples/threadedselect/blockingdemo.py --- twisted-20.3.0/docs/core/examples/threadedselect/blockingdemo.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/examples/threadedselect/blockingdemo.py 2022-02-07 13:12:15.000000000 +0000 @@ -2,24 +2,26 @@ # See LICENSE for details. -from __future__ import print_function - from twisted.internet import _threadedselect + _threadedselect.install() +from itertools import count + +from twisted.internet import reactor from twisted.internet.defer import Deferred from twisted.python.failure import Failure -from twisted.internet import reactor from twisted.python.runtime import seconds -from itertools import count + try: # Python 3 - from queue import Queue, Empty + from queue import Empty, Queue except ImportError: # Python 2 - from Queue import Queue, Empty + from Queue import Empty, Queue -class TwistedManager(object): + +class TwistedManager: def __init__(self): self.twistedQueue = Queue() self.key = count() @@ -39,8 +41,9 @@ def stop(self): # stop the reactor key = self.getKey() - reactor.addSystemEventTrigger('after', 'shutdown', - self._stopIterating, True, key) + reactor.addSystemEventTrigger( + "after", "shutdown", self._stopIterating, True, key + ) reactor.stop() self.iterate(key) @@ -52,7 +55,7 @@ if isinstance(res, Failure): res.raiseException() return res - + def poll(self, noLongerThan=1.0): # poll the reactor for up to noLongerThan seconds base = seconds() @@ -62,7 +65,7 @@ callback() except Empty: pass - + def iterate(self, key=None): # iterate the reactor until it has the result we're looking for while key not in self.results: @@ -70,17 +73,22 @@ callback() return self.results.pop(key) + def fakeDeferred(msg): d = Deferred() + def cb(): print("deferred called back") d.callback(msg) + reactor.callLater(2, cb) return d + def fakeCallback(): print("twisted is still running") + def main(): m = TwistedManager() print("starting") @@ -95,5 +103,5 @@ print("stopped") -if __name__ == '__main__': +if __name__ == "__main__": main() diff -Nru twisted-20.3.0/docs/core/examples/threadedselect/Cocoa/SimpleWebClient/setup.py twisted-22.1.0/docs/core/examples/threadedselect/Cocoa/SimpleWebClient/setup.py --- twisted-20.3.0/docs/core/examples/threadedselect/Cocoa/SimpleWebClient/setup.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/examples/threadedselect/Cocoa/SimpleWebClient/setup.py 2022-02-07 13:12:15.000000000 +0000 @@ -6,9 +6,10 @@ """ from distutils.core import setup + import py2app - + setup( - app = ['Twistzilla.py'], - data_files = ["English.lproj"], + app=["Twistzilla.py"], + data_files=["English.lproj"], ) diff -Nru twisted-20.3.0/docs/core/examples/threadedselect/Cocoa/SimpleWebClient/Twistzilla.py twisted-22.1.0/docs/core/examples/threadedselect/Cocoa/SimpleWebClient/Twistzilla.py --- twisted-20.3.0/docs/core/examples/threadedselect/Cocoa/SimpleWebClient/Twistzilla.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/examples/threadedselect/Cocoa/SimpleWebClient/Twistzilla.py 2022-02-07 13:12:15.000000000 +0000 @@ -2,60 +2,72 @@ # See LICENSE for details. +from AppKit import * + # import needed classes/functions from Cocoa from Foundation import * -from AppKit import * # import Nib loading functionality from AppKit -from PyObjCTools import NibClassBuilder, AppHelper +from PyObjCTools import AppHelper, NibClassBuilder from twisted.internet import _threadedselect + _threadedselect.install() -from twisted.internet import reactor, protocol -from twisted.web import http +import sys + +import urlparse + +from twisted.internet import protocol, reactor from twisted.python import log -import sys, urlparse +from twisted.web import http # create ObjC classes as defined in MainMenu.nib NibClassBuilder.extractClasses("MainMenu") + + class TwistzillaClient(http.HTTPClient): def __init__(self, delegate, urls): self.urls = urls self.delegate = delegate def connectionMade(self): - self.sendCommand('GET', str(self.urls[2])) - self.sendHeader('Host', '%s:%d' % (self.urls[0], self.urls[1])) - self.sendHeader('User-Agent', 'CocoaTwistzilla') + self.sendCommand("GET", str(self.urls[2])) + self.sendHeader("Host", "%s:%d" % (self.urls[0], self.urls[1])) + self.sendHeader("User-Agent", "CocoaTwistzilla") self.endHeaders() def handleResponse(self, data): self.delegate.gotResponse_(data) + class MyAppDelegate(NibClassBuilder.AutoBaseClass): def gotResponse_(self, html): s = self.resultTextField.textStorage() s.replaceCharactersInRange_withString_((0, s.length()), html) self.progressIndicator.stopAnimation_(self) - + def doTwistzillaFetch_(self, sender): s = self.resultTextField.textStorage() s.deleteCharactersInRange_((0, s.length())) self.progressIndicator.startAnimation_(self) u = urlparse.urlparse(self.messageTextField.stringValue()) - pos = u[1].find(':') + pos = u[1].find(":") if pos == -1: host, port = u[1], 80 else: - host, port = u[1][:pos], int(u[1][pos+1:]) - if u[2] == '': - fname = '/' + host, port = u[1][:pos], int(u[1][pos + 1 :]) + if u[2] == "": + fname = "/" else: fname = u[2] - host = host.encode('utf8') - fname = fname.encode('utf8') - protocol.ClientCreator(reactor, TwistzillaClient, self, (host, port, fname)).connectTCP(host, port).addErrback(lambda f:self.gotResponse_(f.getBriefTraceback())) + host = host.encode("utf8") + fname = fname.encode("utf8") + protocol.ClientCreator( + reactor, TwistzillaClient, self, (host, port, fname) + ).connectTCP(host, port).addErrback( + lambda f: self.gotResponse_(f.getBriefTraceback()) + ) def applicationDidFinishLaunching_(self, aNotification): """ @@ -68,12 +80,12 @@ def applicationShouldTerminate_(self, sender): if reactor.running: - reactor.addSystemEventTrigger( - 'after', 'shutdown', AppHelper.stopEventLoop) + reactor.addSystemEventTrigger("after", "shutdown", AppHelper.stopEventLoop) reactor.stop() return False return True - -if __name__ == '__main__': + + +if __name__ == "__main__": log.startLogging(sys.stdout) AppHelper.runEventLoop() diff -Nru twisted-20.3.0/docs/core/examples/threadedselect/pygamedemo.py twisted-22.1.0/docs/core/examples/threadedselect/pygamedemo.py --- twisted-20.3.0/docs/core/examples/threadedselect/pygamedemo.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/examples/threadedselect/pygamedemo.py 2022-02-07 13:12:15.000000000 +0000 @@ -5,18 +5,15 @@ Demonstration of how L{twisted.internet._threadedselect} might be used (this is not an example showing the best way to integrate Twisted with pygame). """ -from __future__ import print_function - # import Twisted and install from twisted.internet import _threadedselect -_threadedselect.install() -from twisted.internet import reactor - -import os +_threadedselect.install() import pygame from pygame.locals import * +from twisted.internet import reactor + try: import pygame.fastevent as eventmodule except ImportError: @@ -30,21 +27,29 @@ # TWISTEDEVENT = USEREVENT + def postTwistedEvent(func): # if not using pygame.fastevent, this can explode if the queue # fills up.. so that's bad. Use pygame.fastevent, in pygame CVS # as of 2005-04-18. eventmodule.post(eventmodule.Event(TWISTEDEVENT, iterateTwisted=func)) + def helloWorld(): print("hello, world") reactor.callLater(1, helloWorld) + + reactor.callLater(1, helloWorld) + def twoSecondsPassed(): print("two seconds passed") + + reactor.callLater(2, twoSecondsPassed) + def eventIterator(): while True: yield eventmodule.wait() @@ -55,9 +60,10 @@ else: yield event + def main(): pygame.init() - if hasattr(eventmodule, 'init'): + if hasattr(eventmodule, "init"): eventmodule.init() screen = pygame.display.set_mode((300, 300)) @@ -67,7 +73,7 @@ # by appending a value to it. This ensures that # Twisted gets to shut down properly. shouldQuit = [] - reactor.addSystemEventTrigger('after', 'shutdown', shouldQuit.append, True) + reactor.addSystemEventTrigger("after", "shutdown", shouldQuit.append, True) for event in eventIterator(): if event.type == TWISTEDEVENT: @@ -81,5 +87,6 @@ pygame.quit() -if __name__ == '__main__': + +if __name__ == "__main__": main() diff -Nru twisted-20.3.0/docs/core/examples/tkinterdemo.py twisted-22.1.0/docs/core/examples/tkinterdemo.py --- twisted-20.3.0/docs/core/examples/tkinterdemo.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/examples/tkinterdemo.py 2022-02-07 13:12:15.000000000 +0000 @@ -13,19 +13,12 @@ """ -from __future__ import print_function +from tkinter import LEFT, Button, Frame, Tk -import sys - -if sys.version_info >= (3,): - from tkinter import Tk, Frame, Button, LEFT -else: - from Tkinter import Tk, Frame, Button, LEFT from twisted.internet import reactor, tksupport -class App(object): - +class App: def onQuit(self): print("Quit!") reactor.stop() @@ -44,7 +37,7 @@ b.pack(side=LEFT) -if __name__ == '__main__': +if __name__ == "__main__": root = Tk() tksupport.install(root) app = App(root) diff -Nru twisted-20.3.0/docs/core/examples/tls_alpn_npn_client.py twisted-22.1.0/docs/core/examples/tls_alpn_npn_client.py --- twisted-20.3.0/docs/core/examples/tls_alpn_npn_client.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/examples/tls_alpn_npn_client.py 2022-02-07 13:12:15.000000000 +0000 @@ -19,13 +19,12 @@ It assumes that you have a self-signed server certificate, named `server-cert.pem` and located in the working directory. """ -from __future__ import print_function -from twisted.internet import ssl, protocol, endpoints, task, defer +from twisted.internet import defer, endpoints, protocol, ssl, task from twisted.python.filepath import FilePath # The hostname the remote server to contact. -TARGET_HOST = u'localhost' +TARGET_HOST = "localhost" # The port to contact. TARGET_PORT = 8080 @@ -40,7 +39,7 @@ # ambiguity about text encodings. # Try changing this list by adding, removing, and reordering protocols to see # how it affects the result. -ACCEPTABLE_PROTOCOLS = [b'h2', b'http/1.1'] +ACCEPTABLE_PROTOCOLS = [b"h2", b"http/1.1"] # Some safe initial data to send. This data is specific to HTTP/2: it is part # of the HTTP/2 client preface (see RFC 7540 Section 3.5). This is used to @@ -52,11 +51,11 @@ # handshake is done. Instead, we wait for one that is implicitly after the # TLS handshake is done: dataReceived. To trigger the remote peer to send data, # we send some ourselves. -TLS_TRIGGER_DATA = b'PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n' +TLS_TRIGGER_DATA = b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n" def main(reactor): - certData = FilePath('server-cert.pem').getContent() + certData = FilePath("server-cert.pem").getContent() serverCertificate = ssl.Certificate.loadPEM(certData) options = ssl.optionsForClientTLS( hostname=TARGET_HOST, @@ -81,7 +80,7 @@ # the TLS handshake is over. This is generally *not* in the call to # connectionMade, but instead only when we've received some data # back. - print('Next protocol is: {}'.format(self.transport.negotiatedProtocol)) + print(f"Next protocol is: {self.transport.negotiatedProtocol}") self.transport.loseConnection() # If this is the first data write, we can tell the reactor we're @@ -94,19 +93,15 @@ # If we haven't received any data, an error occurred. Otherwise, # we lost the connection on purpose. if self.complete is not None: - print("Connection lost due to error {}".format(reason)) + print(f"Connection lost due to error {reason}") self.complete.callback(None) else: print("Connection closed cleanly") return endpoints.connectProtocol( - endpoints.SSL4ClientEndpoint( - reactor, - TARGET_HOST, - TARGET_PORT, - options - ), - BasicH2Request() + endpoints.SSL4ClientEndpoint(reactor, TARGET_HOST, TARGET_PORT, options), + BasicH2Request(), ).addCallback(lambda protocol: protocol.complete) + task.react(main) diff -Nru twisted-20.3.0/docs/core/examples/tls_alpn_npn_server.py twisted-22.1.0/docs/core/examples/tls_alpn_npn_server.py --- twisted-20.3.0/docs/core/examples/tls_alpn_npn_server.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/examples/tls_alpn_npn_server.py 2022-02-07 13:12:15.000000000 +0000 @@ -33,16 +33,14 @@ Alternatively, use the tls_alpn_npn_client.py script found in the examples directory. """ -from __future__ import print_function from OpenSSL import crypto -from twisted.internet.endpoints import SSL4ServerEndpoint -from twisted.internet.protocol import Protocol, Factory from twisted.internet import reactor, ssl +from twisted.internet.endpoints import SSL4ServerEndpoint +from twisted.internet.protocol import Factory, Protocol from twisted.python.filepath import FilePath - # The list of protocols we'd be prepared to speak after the TLS negotiation is # complete. # The order of the protocols here is an order of preference. This indicates @@ -52,39 +50,36 @@ # ambiguity about text encodings. # Try changing this list by adding, removing, and reordering protocols to see # how it affects the result. -ACCEPTABLE_PROTOCOLS = [b'h2', b'http/1.1'] +ACCEPTABLE_PROTOCOLS = [b"h2", b"http/1.1"] # The port that the server will listen on. LISTEN_PORT = 8080 - class NPNPrinterProtocol(Protocol): """ This protocol accepts incoming connections and waits for data. When received, it prints what the negotiated protocol is, echoes the data back, and then terminates the connection. """ + def connectionMade(self): self.complete = False print("Connection made") - def dataReceived(self, data): print(self.transport.negotiatedProtocol) self.transport.write(data) self.complete = True self.transport.loseConnection() - def connectionLost(self, reason): # If we haven't received any data, an error occurred. Otherwise, # we lost the connection on purpose. if self.complete: print("Connection closed cleanly") else: - print("Connection lost due to error {}".format(reason)) - + print(f"Connection lost due to error {reason}") class ResponderFactory(Factory): @@ -92,10 +87,9 @@ return NPNPrinterProtocol() - -privateKeyData = FilePath('server-key.pem').getContent() +privateKeyData = FilePath("server-key.pem").getContent() privateKey = crypto.load_privatekey(crypto.FILETYPE_PEM, privateKeyData) -certData = FilePath('server-cert.pem').getContent() +certData = FilePath("server-cert.pem").getContent() certificate = crypto.load_certificate(crypto.FILETYPE_PEM, certData) options = ssl.CertificateOptions( diff -Nru twisted-20.3.0/docs/core/examples/twistd-logging.tac twisted-22.1.0/docs/core/examples/twistd-logging.tac --- twisted-20.3.0/docs/core/examples/twistd-logging.tac 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/examples/twistd-logging.tac 2022-02-07 13:12:15.000000000 +0000 @@ -7,10 +7,9 @@ # structure of each log message. from twisted.application.service import Application +from twisted.internet.task import LoopingCall from twisted.python.log import ILogObserver, msg from twisted.python.util import untilConcludes -from twisted.internet.task import LoopingCall - logfile = open("twistd-logging.log", "a") @@ -18,7 +17,7 @@ def log(eventDict): # untilConcludes is necessary to retry the operation when the system call # has been interrupted. - untilConcludes(logfile.write, "Got a log! {}\n".format(eventDict)) + untilConcludes(logfile.write, f"Got a log! {eventDict}\n") untilConcludes(logfile.flush) @@ -30,4 +29,3 @@ application = Application("twistd-logging") application.setComponent(ILogObserver, log) - diff -Nru twisted-20.3.0/docs/core/examples/udpbroadcast.py twisted-22.1.0/docs/core/examples/udpbroadcast.py --- twisted-20.3.0/docs/core/examples/udpbroadcast.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/examples/udpbroadcast.py 2022-02-07 13:12:15.000000000 +0000 @@ -23,29 +23,25 @@ from twisted.python import log - class PingPongProtocol(DatagramProtocol): noisy = False def __init__(self, controller, port): self.port = port - def startProtocol(self): self.transport.setBroadcastAllowed(True) - def sendPing(self): - pingMsg = "PING {0}".format(uuid4().hex) + pingMsg = f"PING {uuid4().hex}" pingMsg = pingMsg.encode("ascii") self.transport.write(pingMsg, ("", self.port)) log.msg(b"SEND " + pingMsg) - def datagramReceived(self, datagram, addr): if datagram[:4] == b"PING": uuid = datagram[5:] - pongMsg = "PONG {0}".format(uuid) + pongMsg = f"PONG {uuid}" pongMsg = pongMsg.encode("ascii") self.transport.write(pongMsg, ("", self.port)) log.msg(b"RECV " + datagram) @@ -53,15 +49,12 @@ log.msg(b"RECV " + datagram) - -class Broadcaster(object): - +class Broadcaster: def ping(self, proto): proto.sendPing() - def makeService(self): - application = service.Application('Broadcaster') + application = service.Application("Broadcaster") root = service.MultiService() root.setServiceParent(application) diff -Nru twisted-20.3.0/docs/core/examples/wxacceptance.py twisted-22.1.0/docs/core/examples/wxacceptance.py --- twisted-20.3.0/docs/core/examples/wxacceptance.py 2019-06-15 21:59:27.000000000 +0000 +++ twisted-22.1.0/docs/core/examples/wxacceptance.py 2022-02-07 13:12:15.000000000 +0000 @@ -14,37 +14,50 @@ (use dialog menu item), when menus are held down, when window is being dragged. """ -from __future__ import print_function - -import sys, time +import sys +import time try: - from wx import Frame as wxFrame, DefaultPosition as wxDefaultPosition, \ - Size as wxSize, Menu as wxMenu, MenuBar as wxMenuBar, \ - EVT_MENU, MessageDialog as wxMessageDialog, App as wxApp -except ImportError as e: + from wx import ( + EVT_MENU, + App as wxApp, + DefaultPosition as wxDefaultPosition, + Frame as wxFrame, + Menu as wxMenu, + MenuBar as wxMenuBar, + MessageDialog as wxMessageDialog, + Size as wxSize, + ) +except ImportError: from wxPython.wx import * -from twisted.python import log from twisted.internet import wxreactor -wxreactor.install() -from twisted.internet import reactor, defer +from twisted.python import log +wxreactor.install() +from twisted.internet import defer, reactor # set up so that "hello, world" is printed continuously dc = None + + def helloWorld(): global dc print("hello, world", time.time()) dc = reactor.callLater(0.1, helloWorld) + + dc = reactor.callLater(0.1, helloWorld) + def twoSecondsPassed(): print("two seconds passed") + def printer(s): print(s) + def shutdown(): print("shutting down in 3 seconds") if dc.active(): @@ -56,17 +69,20 @@ reactor.callLater(3, d.callback, 1) return d + def startup(): print("Start up event!") + reactor.callLater(2, twoSecondsPassed) reactor.addSystemEventTrigger("after", "startup", startup) reactor.addSystemEventTrigger("before", "shutdown", shutdown) -ID_EXIT = 101 +ID_EXIT = 101 ID_DIALOG = 102 + class MyFrame(wxFrame): def __init__(self, parent, ID, title): wxFrame.__init__(self, parent, ID, title, wxDefaultPosition, wxSize(300, 200)) @@ -76,17 +92,19 @@ menuBar = wxMenuBar() menuBar.Append(menu, "&File") self.SetMenuBar(menuBar) - EVT_MENU(self, ID_EXIT, self.DoExit) - EVT_MENU(self, ID_DIALOG, self.DoDialog) + EVT_MENU(self, ID_EXIT, self.DoExit) + EVT_MENU(self, ID_DIALOG, self.DoDialog) # you really ought to do this instead of reactor.stop() in # DoExit, but for the sake of testing we'll let closing the # window shutdown wx without reactor.stop(), to make sure that # still does the right thing. - #EVT_CLOSE(self, lambda evt: reactor.stop()) + # EVT_CLOSE(self, lambda evt: reactor.stop()) def DoDialog(self, event): - dl = wxMessageDialog(self, "Check terminal to see if messages are still being " - "printed by Twisted.") + dl = wxMessageDialog( + self, + "Check terminal to see if messages are still being " "printed by Twisted.", + ) dl.ShowModal() dl.Destroy() @@ -95,7 +113,6 @@ class MyApp(wxApp): - def OnInit(self): frame = MyFrame(None, -1, "Hello, world") frame.Show(True) @@ -110,5 +127,5 @@ reactor.run() -if __name__ == '__main__': +if __name__ == "__main__": demo() diff -Nru twisted-20.3.0/docs/core/examples/wxdemo.py twisted-22.1.0/docs/core/examples/wxdemo.py --- twisted-20.3.0/docs/core/examples/wxdemo.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/examples/wxdemo.py 2022-02-07 13:12:15.000000000 +0000 @@ -3,22 +3,21 @@ """Demo of wxPython integration with Twisted.""" -from __future__ import print_function import sys -from wx import Frame, DefaultPosition, Size, Menu, MenuBar, App -from wx import EVT_MENU, EVT_CLOSE +from wx import EVT_CLOSE, EVT_MENU, App, DefaultPosition, Frame, Menu, MenuBar, Size -from twisted.python import log from twisted.internet import wxreactor +from twisted.python import log + wxreactor.install() # import t.i.reactor only after installing wxreactor: from twisted.internet import reactor +ID_EXIT = 101 -ID_EXIT = 101 class MyFrame(Frame): def __init__(self, parent, ID, title): @@ -28,8 +27,8 @@ menuBar = MenuBar() menuBar.Append(menu, "&File") self.SetMenuBar(menuBar) - EVT_MENU(self, ID_EXIT, self.DoExit) - + EVT_MENU(self, ID_EXIT, self.DoExit) + # make sure reactor.stop() is used to stop event loop: EVT_CLOSE(self, lambda evt: reactor.stop()) @@ -38,7 +37,6 @@ class MyApp(App): - def twoSecondsPassed(self): print("two seconds passed") @@ -62,5 +60,5 @@ reactor.run() -if __name__ == '__main__': +if __name__ == "__main__": demo() diff -Nru twisted-20.3.0/docs/core/howto/amp.rst twisted-22.1.0/docs/core/howto/amp.rst --- twisted-20.3.0/docs/core/howto/amp.rst 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/amp.rst 2022-02-07 13:12:15.000000000 +0000 @@ -9,7 +9,7 @@ ======================================== -The purpose of this guide is to describe the uses for and usage of :api:`twisted.protocols.amp ` beyond what is explained in the API documentation. It will show you how to implement an AMP server which can respond to commands or interact directly with individual messages. It will also show you how to implement an AMP client which can issue commands to a server. +The purpose of this guide is to describe the uses for and usage of :py:mod:`twisted.protocols.amp` beyond what is explained in the API documentation. It will show you how to implement an AMP server which can respond to commands or interact directly with individual messages. It will also show you how to implement an AMP client which can issue commands to a server. @@ -25,7 +25,7 @@ -AMP runs over a stream-oriented connection-based protocol, such as TCP or SSL. Before you can use any features of the AMP protocol, you need a connection. The protocol class to use to establish an AMP connection is :api:`twisted.protocols.amp.AMP ` . Connection setup works as it does for almost all protocols in Twisted. For example, you can set up a listening AMP server using a server endpoint: +AMP runs over a stream-oriented connection-based protocol, such as TCP or SSL. Before you can use any features of the AMP protocol, you need a connection. The protocol class to use to establish an AMP connection is :py:class:`AMP ` . Connection setup works as it does for almost all protocols in Twisted. For example, you can set up a listening AMP server using a server endpoint: @@ -53,7 +53,7 @@ -Either side of an AMP connection can issue a command to the other side. Each kind of command is represented as a subclass of :api:`twisted.protocols.amp.Command ` . A ``Command`` defines arguments, response values, and error conditions. +Either side of an AMP connection can issue a command to the other side. Each kind of command is represented as a subclass of :py:class:`Command ` . A ``Command`` defines arguments, response values, and error conditions. diff -Nru twisted-20.3.0/docs/core/howto/application.rst twisted-22.1.0/docs/core/howto/application.rst --- twisted-20.3.0/docs/core/howto/application.rst 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/application.rst 2022-02-07 13:12:15.000000000 +0000 @@ -33,19 +33,19 @@ The major tool that manages Twisted applications is a command-line utility called ``twistd``. ``twistd`` is cross platform, and is the recommended tool for running Twisted applications. -The core component of the Twisted Application infrastructure is the :api:`twisted.application.service.Application ` object -- an object which represents your application. +The core component of the Twisted Application infrastructure is the :py:func:`twisted.application.service.Application` object -- an object which represents your application. However, Application doesn't provide anything that you'd want to manipulate directly. -Instead, Application acts as a container of any "Services" (objects implementing :api:`twisted.application.service.IService `) that your application provides. +Instead, Application acts as a container of any "Services" (objects implementing :py:class:`IService `) that your application provides. Most of your interaction with the Application infrastructure will be done through Services. By "Service", we mean anything in your application that can be started and stopped. Typical services include web servers, FTP servers and SSH clients. -Your Application object can contain many services, and can even contain structured hierarchies of Services using :api:`twisted.application.service.MultiService ` or your own custom :api:`twisted.application.service.IServiceCollection ` implementations. +Your Application object can contain many services, and can even contain structured hierarchies of Services using :py:class:`MultiService ` or your own custom :py:class:`IServiceCollection ` implementations. You will most likely want to use these to manage Services which are dependent on other Services. For example, a proxying Twisted application might want its server Service to only start up after the associated Client service. -An :api:`twisted.application.service.IService ` has two basic methods, ``startService()`` which is used to start the service, and ``stopService()`` which is used to stop the service. -The latter can return a :api:`twisted.internet.defer.Deferred `, indicating service shutdown is not over until the result fires. +An :py:class:`IService ` has two basic methods, ``startService()`` which is used to start the service, and ``stopService()`` which is used to stop the service. +The latter can return a :py:class:`Deferred `, indicating service shutdown is not over until the result fires. For example: .. code-block:: python @@ -77,7 +77,7 @@ To handle start-up and configuration of your Twisted application, the Twisted Application infrastructure uses ``.tac`` files. -``.tac`` are Python files which configure an :api:`twisted.application.service.Application ` object and assign this object to the top-level variable "``application``" . +``.tac`` are Python files which configure an :py:func:`Application ` object and assign this object to the top-level variable "``application``" . The following is a simple example of a ``.tac`` file: @@ -113,9 +113,9 @@ Invoking ``twistd --logger my.logger ...`` will log to a file named ``/tmp/my.log`` (this simple example could easily be replaced with use of the ``--logfile`` parameter to twistd). Alternatively, the logging behavior can be customized through an API accessible from ``.tac`` files. -The :api:`twisted.python.log.ILogObserver ` component can be set on an Application in order to customize the default log observer that ``twistd`` will use. +The :py:class:`ILogObserver ` component can be set on an Application in order to customize the default log observer that ``twistd`` will use. -Here is an example of how to use :api:`twisted.python.logfile.DailyLogFile `, which rotates the log once per day. +Here is an example of how to use :py:class:`DailyLogFile `, which rotates the log once per day. .. code-block:: python @@ -134,7 +134,7 @@ Services provided by Twisted ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Twisted also provides pre-written :api:`twisted.application.service.IService ` implementations for common cases like listening on a TCP port, in the :api:`twisted.application.internet ` module. +Twisted also provides pre-written :py:class:`IService ` implementations for common cases like listening on a TCP port, in the :py:mod:`twisted.application.internet` module. Here's a simple example of constructing a service that runs an echo server on TCP port 7001: .. code-block:: python @@ -159,8 +159,8 @@ Services which allow you to make connections and listen for connections on TCP ports. - - :api:`twisted.internet.interfaces.IReactorTCP.listenTCP ` - - :api:`twisted.internet.interfaces.IReactorTCP.connectTCP ` + - :py:meth:`listenTCP ` + - :py:meth:`connectTCP ` ``UNIXServer`` @@ -169,8 +169,8 @@ Services which listen and make connections over UNIX sockets. - - :api:`twisted.internet.interfaces.IReactorUNIX.listenUNIX ` - - :api:`twisted.internet.interfaces.IReactorUNIX.connectUNIX ` + - :py:meth:`listenUNIX ` + - :py:meth:`connectUNIX ` ``SSLServer`` @@ -179,15 +179,15 @@ Services which allow you to make SSL connections and run SSL servers. - - :api:`twisted.internet.interfaces.IReactorSSL.listenSSL ` - - :api:`twisted.internet.interfaces.IReactorSSL.connectSSL ` + - :py:meth:`listenSSL ` + - :py:meth:`connectSSL ` ``UDPServer`` A service which allows you to send and receive data over UDP. - - :api:`twisted.internet.interfaces.IReactorUDP.listenUDP ` + - :py:meth:`listenUDP ` See also the :doc:`UDP documentation `. @@ -198,31 +198,31 @@ Services which send and receive data over UNIX datagram sockets. - - :api:`twisted.internet.interfaces.IReactorUNIXDatagram.listenUNIXDatagram ` - - :api:`twisted.internet.interfaces.IReactorUNIXDatagram.connectUNIXDatagram ` + - :py:meth:`listenUNIXDatagram ` + - :py:meth:`connectUNIXDatagram ` ``MulticastServer`` A server for UDP socket methods that support multicast. - - :api:`twisted.internet.interfaces.IReactorMulticast.listenMulticast ` + - :py:meth:`listenMulticast ` ``TimerService`` A service to periodically call a function. - - :api:`twisted.application.internet.TimerService ` + - :py:class:`TimerService ` Service Collection ~~~~~~~~~~~~~~~~~~ -:api:`twisted.application.service.IServiceCollection ` objects contain :api:`twisted.application.service.IService ` objects. -IService objects can be added to IServiceCollection by calling :api:`twisted.application.service.IService.setServiceParent ` and detached by using :api:`twisted.application.service.IService.disownServiceParent `. +:py:class:`IServiceCollection ` objects contain :py:class:`IService ` objects. +IService objects can be added to IServiceCollection by calling :py:meth:`setServiceParent ` and detached by using :py:meth:`disownServiceParent `. -The standard implementation of IServiceCollection is :api:`twisted.application.service.MultiService `, which also implements IService. +The standard implementation of IServiceCollection is :py:class:`MultiService `, which also implements IService. MultiService is useful for creating a new Service which combines two or more existing Services. For example, you could create a DNS Service as a MultiService which has a TCP and a UDP Service as children. diff -Nru twisted-20.3.0/docs/core/howto/basics.rst twisted-22.1.0/docs/core/howto/basics.rst --- twisted-20.3.0/docs/core/howto/basics.rst 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/basics.rst 2022-02-07 13:12:15.000000000 +0000 @@ -9,15 +9,15 @@ Application ----------- -Twisted programs usually work with :api:`twisted.application.service.Application`. +Twisted programs usually work with :py:func:`twisted.application.service.Application`. This class usually holds all persistent configuration of a running server, such as: - ports to bind to, - places where connections to must be kept or attempted, - periodic actions to do, -- and almost everything else to do with your :api:`twisted.application.service.Application `. +- and almost everything else to do with your :py:func:`Application `. -It is the root object in a tree of services implementing :api:`twisted.application.service.IService`. +It is the root object in a tree of services implementing :py:class:`twisted.application.service.IService`. Other howtos describe how to write custom code for ``Application``\ s, but this one describes how to use already written code (which can be part of Twisted or from a third-party Twisted plugin developer). The Twisted distribution comes with an important tool to deal with ``Application``\ s: ``twistd(1)``. @@ -28,7 +28,7 @@ twistd ------ -The Twisted Daemon is a program that knows how to run :api:`twisted.application.service.Application `\ s. +The Twisted Daemon is a program that knows how to run :py:func:`Application `\ s. Strictly speaking, ``twistd`` is not necessary. Fetching the application, getting the ``IService`` component, calling ``startService()``, scheduling ``stopService()`` when the reactor shuts down, and then calling ``reactor.run()`` could be done manually. diff -Nru twisted-20.3.0/docs/core/howto/choosing-reactor.rst twisted-22.1.0/docs/core/howto/choosing-reactor.rst --- twisted-20.3.0/docs/core/howto/choosing-reactor.rst 2019-06-15 21:59:27.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/choosing-reactor.rst 2022-02-07 13:12:15.000000000 +0000 @@ -16,7 +16,7 @@ -Twisted provides a variety of implementations of the :api:`twisted.internet.reactor ` . The specialized +Twisted provides a variety of implementations of the :py:mod:`twisted.internet.reactor` . The specialized implementations are suited for different purposes and are designed to integrate better with particular platforms. @@ -212,7 +212,7 @@ The KQueue Reactor allows Twisted to use FreeBSD's kqueue mechanism for -event scheduling. See instructions in the :api:`twisted.internet.kqreactor ` 's +event scheduling. See instructions in the :py:mod:`twisted.internet.kqreactor` 's docstring for installation notes. @@ -603,7 +603,7 @@ .. code-block:: python - from Tkinter import * + from tkinter import * from twisted.internet import tksupport, reactor root = Tk() diff -Nru twisted-20.3.0/docs/core/howto/clients.rst twisted-22.1.0/docs/core/howto/clients.rst --- twisted-20.3.0/docs/core/howto/clients.rst 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/clients.rst 2022-02-07 13:12:15.000000000 +0000 @@ -16,7 +16,7 @@ At the base, the place where you actually implement the protocol parsing and handling, is the ``Protocol`` class. This class will usually be descended -from :api:`twisted.internet.protocol.Protocol ` . Most +from :py:class:`twisted.internet.protocol.Protocol` . Most protocol handlers inherit either from this class or from one of its convenience children. An instance of the protocol class will be instantiated when you connect to the server and will go away when the connection is @@ -24,8 +24,8 @@ ``Protocol`` . The persistent configuration is kept in a ``Factory`` class, -which usually inherits from :api:`twisted.internet.protocol.Factory ` -(or :api:`twisted.internet.protocol.ClientFactory ` : see below). +which usually inherits from :py:class:`twisted.internet.protocol.Factory` +(or :py:class:`twisted.internet.protocol.ClientFactory` : see below). The default factory class just instantiates the ``Protocol`` and then sets the protocol's ``factory`` attribute to point to itself (the factory). This lets the ``Protocol`` access, and possibly modify, the persistent configuration. @@ -64,18 +64,18 @@ This protocol connects to the server, sends it a welcome message, and then terminates the connection. -The :api:`twisted.internet.protocol.BaseProtocol.connectionMade ` event is +The :py:meth:`connectionMade ` event is usually where set up of the ``Protocol`` object happens, as well as any initial greetings (as in the ``WelcomeMessage`` protocol above). Any tearing down of -``Protocol`` -specific objects is done in :api:`twisted.internet.protocol.Protocol.connectionLost ` . +``Protocol`` -specific objects is done in :py:meth:`connectionLost ` . Simple, single-use clients -------------------------- In many cases, the protocol only needs to connect to the server once, and the code just wants to get a connected instance of the protocol. In -those cases :api:`twisted.internet.endpoints ` provides -the appropriate API, and in particular :api:`twisted.internet.endpoints.connectProtocol ` which takes a +those cases :py:mod:`twisted.internet.endpoints` provides +the appropriate API, and in particular :py:func:`connectProtocol ` which takes a protocol instance rather than a factory. .. code-block:: python @@ -99,12 +99,12 @@ reactor.run() Regardless of the type of client endpoint, the way to set up a new -connection is simply pass it to :api:`twisted.internet.endpoints.connectProtocol ` along with a +connection is simply pass it to :py:func:`connectProtocol ` along with a protocol instance. This means it's easy to change the mechanism you're using to connect, without changing the rest of your program. For example, to run the greeter example over SSL, the only change required is to instantiate an -:api:`twisted.internet.endpoints.SSL4ClientEndpoint ` instead of a +:py:class:`SSL4ClientEndpoint ` instead of a ``TCP4ClientEndpoint`` . To take advantage of this, functions and methods which initiates a new connection should generally accept an endpoint as an argument and let the caller construct it, rather than taking @@ -114,7 +114,7 @@ to different types of endpoints, as well as parsing strings into endpoints, see :doc:`the documentation for the endpoints API ` . -You may come across code using :api:`twisted.internet.protocol.ClientCreator ` , an older API which is not as flexible as +You may come across code using :py:class:`ClientCreator ` , an older API which is not as flexible as the endpoint API. Rather than calling ``connect`` on an endpoint, such code will look like this: @@ -140,7 +140,7 @@ convenient to use. To use the lower-level connection APIs, you will need to call one of the *reactor.connect** methods directly. -For these cases, you need a :api:`twisted.internet.protocol.ClientFactory ` . +For these cases, you need a :py:class:`ClientFactory ` . The ``ClientFactory`` is in charge of creating the ``Protocol`` and also receives events relating to the connection state. This allows it to do things like reconnect in the event of a connection error. Here is an example of a simple ``ClientFactory`` that uses the ``Echo`` protocol (above) and also prints what state the connection is in. @@ -177,8 +177,8 @@ reactor.connectTCP(host, port, EchoClientFactory()) reactor.run() -Note that :api:`twisted.internet.protocol.ClientFactory.clientConnectionFailed ` is called when a connection could not be established, -and that :api:`twisted.internet.protocol.ClientFactory.clientConnectionLost ` is called when a connection was made and then disconnected. +Note that :py:meth:`clientConnectionFailed ` is called when a connection could not be established, +and that :py:meth:`clientConnectionLost ` is called when a connection was made and then disconnected. Reactor Client APIs ~~~~~~~~~~~~~~~~~~~ @@ -186,7 +186,7 @@ connectTCP '''''''''' -:api:`twisted.internet.interfaces.IReactorTCP.connectTCP ` provides support for IPv4 and IPv6 TCP clients. +:py:meth:`IReactorTCP.connectTCP ` provides support for IPv4 and IPv6 TCP clients. The ``host`` argument it accepts can be either a hostname or an IP address literal. In the case of a hostname, the reactor will automatically resolve the name to an IP address before attempting the connection. This means that for a hostname with multiple address records, reconnection attempts may not always go to the same server (see below). @@ -214,7 +214,7 @@ from scratch. However, most programs that want this functionality should -implement :api:`twisted.internet.protocol.ReconnectingClientFactory ` instead, +implement :py:class:`ReconnectingClientFactory ` instead, which tries to reconnect if a connection is lost or fails and which exponentially delays repeated reconnect attempts. @@ -321,13 +321,13 @@ Further Reading --------------- -The :api:`twisted.internet.protocol.Protocol ` class used throughout this document is a base implementation of :api:`twisted.internet.interfaces.IProtocol ` used in most Twisted applications for convenience. -To learn about the complete ``IProtocol`` interface, see the API documentation for :api:`twisted.internet.interfaces.IProtocol ` . +The :py:class:`Protocol ` class used throughout this document is a base implementation of :py:class:`IProtocol ` used in most Twisted applications for convenience. +To learn about the complete ``IProtocol`` interface, see the API documentation for :py:class:`IProtocol ` . The ``transport`` attribute used in some examples in this -document provides the :api:`twisted.internet.interfaces.ITCPTransport ` interface. To learn +document provides the :py:class:`ITCPTransport ` interface. To learn about the complete interface, see the API documentation -for :api:`twisted.internet.interfaces.ITCPTransport ` . +for :py:class:`ITCPTransport ` . Interface classes are a way of specifying what methods and attributes an object has and how they behave. See the :doc:`Components: Interfaces and Adapters ` document for more information on diff -Nru twisted-20.3.0/docs/core/howto/components.rst twisted-22.1.0/docs/core/howto/components.rst --- twisted-20.3.0/docs/core/howto/components.rst 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/components.rst 2022-02-07 13:12:15.000000000 +0000 @@ -165,7 +165,7 @@ Adapters are a useful way of using multiple classes to factor code into discrete chunks. However, they are not very interesting without some more infrastructure. If each piece of code which wished to use an adapted object had to explicitly construct the adapter itself, the coupling between components would be too tight. -We would like to achieve "loose coupling", and this is where :api:`twisted.python.components ` comes in. +We would like to achieve "loose coupling", and this is where :py:mod:`twisted.python.components` comes in. First, we need to discuss Interfaces in more detail. As we mentioned earlier, an Interface is nothing more than a class which is used as a marker. @@ -333,7 +333,7 @@ If you inherit from a class which implements some interface, and your new subclass declares that it implements another interface, the implements will be inherited by default. -For example, :api:`twisted.spread.pb.Root ` is a class which implements :api:`twisted.spread.pb.IPBRoot `. +For example, :py:class:`pb.Root ` is a class which implements :py:class:`IPBRoot `. This interface indicates that an object has remotely-invokable methods and can be used as the initial object served by a new Broker instance. It has an ``implements`` setting like: diff -Nru twisted-20.3.0/docs/core/howto/constants.rst twisted-22.1.0/docs/core/howto/constants.rst --- twisted-20.3.0/docs/core/howto/constants.rst 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/constants.rst 2022-02-07 13:12:15.000000000 +0000 @@ -5,396 +5,3 @@ ``twisted.python.constants`` has been deprecated in Twisted, and the same code has been spun out into `Constantly `_\. The API is identical, just install it from PyPI and replace ``import twisted.python.constants`` with ``import constantly`` in your code. - - -Overview --------- - -It is often useful to define names which will be treated as constants. -:api:`twisted.python.constants ` provides APIs for defining such symbolic constants with minimal overhead and some useful features beyond those afforded by the common Python idioms for this task. - -This document will explain how to use these APIs and what circumstances they might be helpful in. - - -Constant Names --------------- - -Constants which have no value apart from their name and identity can be defined by subclassing :api:`twisted.python.constants.Names ` . -Consider this example, in which some HTTP request method constants are defined. - -.. code-block:: python - - from twisted.python.constants import NamedConstant, Names - class METHOD(Names): - """ - Constants representing various HTTP request methods. - """ - GET = NamedConstant() - PUT = NamedConstant() - POST = NamedConstant() - DELETE = NamedConstant() - -Only direct subclasses of ``Names`` are supported (i.e., you cannot subclass ``METHOD`` to add new constants the collection). - -Given this definition, constants can be looked up by name using attribute access on the ``METHOD`` object: - -.. code-block:: console - - >>> METHOD.GET - - >>> METHOD.PUT - - >>> - -If it's necessary to look up constants from a string (e.g. based on user input of some sort), a safe way to do it is using ``lookupByName`` : - -.. code-block:: console - - >>> METHOD.lookupByName('GET') - - >>> METHOD.lookupByName('__doc__') - Traceback (most recent call last): - File "", line 1, in - File "twisted/python/constants.py", line 145, in lookupByName - raise ValueError(name) - ValueError: __doc__ - >>> - -As demonstrated, it is safe because any name not associated with a constant (even those special names initialized by Python itself) will result in ``ValueError`` being raised, not some other object not intended to be used the way the constants are used. - -The constants can also be enumerated using the ``iterconstants`` method: - -.. code-block:: console - - >>> list(METHOD.iterconstants()) - [, , , ] - >>> - -Constants can be compared for equality or identity: - -.. code-block:: console - - >>> METHOD.GET is METHOD.GET - True - >>> METHOD.GET == METHOD.GET - True - >>> METHOD.GET is METHOD.PUT - False - >>> METHOD.GET == METHOD.PUT - False - >>> - -Ordered comparisons (and therefore sorting) also work. -The order is defined to be the same as the instantiation order of the constants: - -.. code-block:: python - - >>> from twisted.python.constants import NamedConstant, Names - >>> class Letters(Names): - ... a = NamedConstant() - ... b = NamedConstant() - ... c = NamedConstant() - ... - >>> Letters.a < Letters.b < Letters.c - True - >>> Letters.a > Letters.b - False - >>> sorted([Letters.b, Letters.a, Letters.c]) - [, , ] - >>> - -A subclass of ``Names`` may define class methods to implement custom functionality. -Consider this definition of ``METHOD`` : - -.. code-block:: python - - from twisted.python.constants import NamedConstant, Names - class METHOD(Names): - """ - Constants representing various HTTP request methods. - """ - GET = NamedConstant() - PUT = NamedConstant() - POST = NamedConstant() - DELETE = NamedConstant() - - @classmethod - def isIdempotent(cls, method): - """ - Return True if the given method is side-effect free, False otherwise. - """ - return method is cls.GET - -This functionality can be used as any class methods are used: - -.. code-block:: console - - >>> METHOD.isIdempotent(METHOD.GET) - True - >>> METHOD.isIdempotent(METHOD.POST) - False - >>> - - -Constants With Values ---------------------- - -Constants with a particular associated value are supported by the :api:`twisted.python.constants.Values ` base class. -Consider this example, in which some HTTP status code constants are defined. - -.. code-block:: python - - from twisted.python.constants import ValueConstant, Values - class STATUS(Values): - """ - Constants representing various HTTP status codes. - """ - OK = ValueConstant("200") - FOUND = ValueConstant("302") - NOT_FOUND = ValueConstant("404") - -As with ``Names`` , constants are accessed as attributes of the class object: - -.. code-block:: console - - >>> STATUS.OK - - >>> STATUS.FOUND - - >>> - -Additionally, the values of the constants can be accessed using the ``value`` attribute of one these objects: - -.. code-block:: console - - >>> STATUS.OK.value - '200' - >>> - -As with ``Names`` , constants can be looked up by name: - -.. code-block:: console - - >>> STATUS.lookupByName('NOT_FOUND') - - >>> - -Constants on a ``Values`` subclass can also be looked up by value: - -.. code-block:: console - - >>> STATUS.lookupByValue('404') - - >>> STATUS.lookupByValue('500') - Traceback (most recent call last): - File "", line 1, in - File "twisted/python/constants.py", line 244, in lookupByValue - raise ValueError(value) - ValueError: 500 - >>> - -Multiple constants may have the same value. -If they do, ``lookupByValue`` will find the one which is defined first. - -Iteration is also supported: - -.. code-block:: console - - >>> list(STATUS.iterconstants()) - [, , ] - >>> - -Constants can be compared for equality, identity and ordering: - -.. code-block:: console - - >>> STATUS.OK == STATUS.OK - True - >>> STATUS.OK is STATUS.OK - True - >>> STATUS.OK is STATUS.NOT_FOUND - False - >>> STATUS.OK == STATUS.NOT_FOUND - False - >>> STATUS.NOT_FOUND > STATUS.OK - True - >>> STATUS.FOUND < STATUS.OK - False - >>> - -Note that like ``Names`` , ``Values`` are ordered by instantiation order, not by value, though either order is the same in the above example. - -As with ``Names`` , a subclass of ``Values`` can define custom methods: - -.. code-block:: python - - from twisted.python.constants import ValueConstant, Values - class STATUS(Values): - """ - Constants representing various HTTP status codes. - """ - OK = ValueConstant("200") - NO_CONTENT = ValueConstant("204") - NOT_MODIFIED = ValueConstant("304") - NOT_FOUND = ValueConstant("404") - - @classmethod - def hasBody(cls, status): - """ - Return True if the given status is associated with a response body, - False otherwise. - """ - return status not in (cls.NO_CONTENT, cls.NOT_MODIFIED) - -This functionality can be used as any class methods are used: - -.. code-block:: console - - >>> STATUS.hasBody(STATUS.OK) - True - >>> STATUS.hasBody(STATUS.NO_CONTENT) - False - >>> - - -Constants As Flags ------------------- - -Integers are often used as a simple set for constants. -The values for these constants are assigned as powers of two so that bits in the integer can be set to represent them. -Individual bits are often called *flags* . -:api:`twisted.python.constants.Flags ` supports this use-case, including allowing constants with particular bits to be set, for interoperability with other tools. - -POSIX filesystem access control is traditionally done using a bitvector defining which users and groups may perform which operations on a file. -This state might be represented using ``Flags`` as follows: - -.. code-block:: python - - from twisted.python.constants import FlagConstant, Flags - class Permission(Flags): - """ - Constants representing user, group, and other access bits for reading, - writing, and execution. - """ - OTHER_EXECUTE = FlagConstant() - OTHER_WRITE = FlagConstant() - OTHER_READ = FlagConstant() - GROUP_EXECUTE = FlagConstant() - GROUP_WRITE = FlagConstant() - GROUP_READ = FlagConstant() - USER_EXECUTE = FlagConstant() - USER_WRITE = FlagConstant() - USER_READ = FlagConstant() - -As for the previous types of constants, these can be accessed as attributes of the class object: - -.. code-block:: console - - >>> Permission.USER_READ - - >>> Permission.USER_WRITE - - >>> Permission.USER_EXECUTE - - >>> - -These constant objects also have a ``value`` attribute giving their integer value: - -.. code-block:: console - - >>> Permission.USER_READ.value - 256 - >>> - -These constants can be looked up by name or value: - -.. code-block:: console - - >>> Permission.lookupByName('USER_READ') is Permission.USER_READ - True - >>> Permission.lookupByValue(256) is Permission.USER_READ - True - >>> - -Constants can also be combined using the logical operators ``&`` (*and* ), ``|`` (*or* ), and ``^`` (*exclusive or* ). - -.. code-block:: console - - >>> Permission.USER_READ | Permission.USER_WRITE - - >>> (Permission.USER_READ | Permission.USER_WRITE) & Permission.USER_WRITE - - >>> (Permission.USER_READ | Permission.USER_WRITE) ^ Permission.USER_WRITE - - >>> - -These combined constants can be deconstructed via iteration: - -.. code-block:: console - - >>> mode = Permission.USER_READ | Permission.USER_WRITE - >>> list(mode) - [, ] - >>> Permission.USER_READ in mode - True - >>> Permission.USER_EXECUTE in mode - False - >>> - -They can also be inspected via boolean operations: - -.. code-block:: console - - >>> Permission.USER_READ & mode - - >>> bool(Permission.USER_READ & mode) - True - >>> Permission.USER_EXECUTE & mode - - >>> bool(Permission.USER_EXECUTE & mode) - False - >>> - -The unary operator ``~`` (*not* ) is also defined: - -.. code-block:: console - - >>> ~Permission.USER_READ - - >>> - -Constants created using these operators also have a ``value`` attribute. - -.. code-block:: console - - >>> (~Permission.USER_WRITE).value - 383 - >>> - -Note the care taken to ensure the ``~`` operator is applied first and the ``value`` attribute is looked up second. - -A ``Flags`` subclass can also define methods, just as a ``Names`` or ``Values`` subclass may. -For example, ``Permission`` might benefit from a method to format a flag as a string in the traditional style. -Consider this addition to that class: - -.. code-block:: python - - from twisted.python import filepath - from twisted.python.constants import FlagConstant, Flags - class Permission(Flags): - ... - - @classmethod - def format(cls, permissions): - """ - Format permissions flags in the traditional 'rwxr-xr-x' style. - """ - return filepath.Permissions(permissions.value).shorthand() - -Use this like any other class method: - -.. code-block:: console - - >>> Permission.format(Permission.USER_READ | Permission.USER_WRITE | Permission.GROUP_READ | Permission.OTHER_READ) - 'rw-r--r--' - >>> diff -Nru twisted-20.3.0/docs/core/howto/cred.rst twisted-22.1.0/docs/core/howto/cred.rst --- twisted-20.3.0/docs/core/howto/cred.rst 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/cred.rst 2022-02-07 13:12:15.000000000 +0000 @@ -40,16 +40,16 @@ To sketch out how this works - a "Realm" corresponds to an application domain and is in charge of avatars, which are network-accessible business logic objects. To connect this to an authentication database, a top-level object -called a :api:`twisted.cred.portal.Portal ` stores a +called a :py:class:`Portal ` stores a realm, and a number of credential checkers. Something that wishes to log in, -such as a :api:`twisted.internet.protocol.Protocol ` , +such as a :py:class:`Protocol ` , stores a reference to the portal. Login consists of passing credentials and a -request interface (e.g. POP3's :api:`twisted.mail.pop3.IMailbox ` ) to the portal. The portal passes +request interface (e.g. POP3's :py:class:`IMailboxPOP3 ` ) to the portal. The portal passes the credentials to the appropriate credential checker, which returns an avatar ID. The ID is passed to the realm, which returns the appropriate avatar. For a Portal that has a realm that creates mailbox objects and a credential checker that checks /etc/passwd, login consists of passing in a username/password and -the IMailbox interface to the portal. The portal passes this to the /etc/passwd +the IMailboxPOP3 interface to the portal. The portal passes this to the /etc/passwd credential checker, gets back a avatar ID corresponding to an email account, passes that to the realm and gets back a mailbox object for that email account. @@ -82,7 +82,7 @@ This is the core of login, the point of integration between all the objects in the cred system. There is one concrete implementation of Portal, and no interface - it does a very -simple task. A :api:`twisted.cred.portal.Portal ` +simple task. A :py:class:`Portal ` associates one (1) Realm with a collection of CredentialChecker instances. (More on those later.) @@ -97,9 +97,9 @@ -- :api:`twisted.cred.portal.Portal.login ` ``(credentials, mind, *interfaces)`` +- :py:meth:`login ` ``(credentials, mind, *interfaces)`` - The docstring is quite expansive (see :api:`twisted.cred.portal ` ), but in + The docstring is quite expansive (see :py:mod:`twisted.cred.portal` ), but in brief, this is what you call when you need to call in order to connect a user to the system. Typically you only pass in one interface, and the mind is ``None`` . The interfaces are the possible interfaces the returned @@ -121,7 +121,7 @@ The logout method has to be called when the avatar is logged out. For POP3 this means when the protocol is disconnected or logged out, etc.. -- :api:`twisted.cred.portal.Portal.registerChecker ` ``(checker, *credentialInterfaces)`` +- :py:meth:`registerChecker ` ``(checker, *credentialInterfaces)`` which adds a CredentialChecker to the portal. The optional list of interfaces are interfaces of credentials that the checker is able to check. @@ -137,7 +137,7 @@ -This is an object implementing :api:`twisted.cred.checkers.ICredentialsChecker ` which resolves some +This is an object implementing :py:class:`ICredentialsChecker ` which resolves some credentials to an avatar ID. Whether the credentials are stored in an in-memory data structure, an @@ -185,9 +185,9 @@ Twisted comes with a number of credentials interfaces and implementations -in the :api:`twisted.cred.credentials ` module, -such as :api:`twisted.cred.credentials.IUsernamePassword ` -and :api:`twisted.cred.credentials.IUsernameHashedPassword ` . +in the :py:mod:`twisted.cred.credentials` module, +such as :py:class:`IUsernamePassword ` +and :py:class:`IUsernameHashedPassword ` . @@ -202,13 +202,13 @@ -:api:`twisted.cred.portal.IRealm ` is another one-method interface: +:py:class:`IRealm ` is another one-method interface: -- :api:`twisted.cred.portal.IRealm.requestAvatar ` ``(avatarId, mind, *interfaces)`` +- :py:meth:`requestAvatar ` ``(avatarId, mind, *interfaces)`` This method will typically be called from 'Portal.login'. The avatarId is the one returned by a CredentialChecker. @@ -460,7 +460,7 @@ To build a plugin for cred, you should first define an ``authType`` , a short one-word string that defines your plugin to the command-line. Once you have this, the convention is -to create a file named ``myapp_plugins.py`` in the :api:`twisted.plugins ` module path. +to create a file named ``myapp_plugins.py`` in the :py:mod:`twisted.plugins` module path. @@ -512,7 +512,7 @@ Once you have created this structure within your application, you can create the code for your cred plugin by building a factory class which -implements :api:`twisted.cred.strcred.ICheckerFactory ` . +implements :py:class:`ICheckerFactory ` . These factory classes should not consist of a tremendous amount of code. Most of the real application logic should reside in the cred checker itself. (For help on building those, scroll up.) diff -Nru twisted-20.3.0/docs/core/howto/defer-intro.rst twisted-22.1.0/docs/core/howto/defer-intro.rst --- twisted-20.3.0/docs/core/howto/defer-intro.rst 2019-06-15 22:22:24.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/defer-intro.rst 2022-02-07 13:12:15.000000000 +0000 @@ -6,18 +6,18 @@ Introduction to Deferreds ========================= -This document introduces :api:`twisted.internet.defer.Deferred `\s, Twisted's preferred mechanism for controlling the flow of asynchronous code. +This document introduces :py:class:`Deferred `\s, Twisted's preferred mechanism for controlling the flow of asynchronous code. Don't worry if you don't know what that means yet -- that's why you are here! -It is intended for newcomers to Twisted, and was written particularly to help people read and understand code that already uses :api:`twisted.internet.defer.Deferred `\s. +It is intended for newcomers to Twisted, and was written particularly to help people read and understand code that already uses :py:class:`Deferred `\s. This document assumes you have a good working knowledge of Python. It assumes no knowledge of Twisted. -By the end of the document, you should understand what :api:`twisted.internet.defer.Deferred `\s are and how they can be used to coordinate asynchronous code. +By the end of the document, you should understand what :py:class:`Deferred `\s are and how they can be used to coordinate asynchronous code. In particular, you should be able to: -- Read and understand code that uses :api:`twisted.internet.defer.Deferred `\s +- Read and understand code that uses :py:class:`Deferred `\s - Translate from synchronous code to asynchronous code and back again - Implement any sort of error-handling for asynchronous code that you wish @@ -94,7 +94,7 @@ We would still need a way of saying "do *this* only when *that* has finished". -We would need a way of distinguishing between successful completion and interrupted processing, normally modeled with ``try``, ``expect``, ``else``, and ``finally``. +We would need a way of distinguishing between successful completion and interrupted processing, normally modeled with ``try``, ``except``, ``else``, and ``finally``. We need a mechanism for getting return failures and exception information from the thing that just executed to the thing that needs to happen next. @@ -112,7 +112,7 @@ One solution: Deferred ---------------------- -Twisted tackles this problem with :api:`twisted.internet.defer.Deferred `\s, a type of object designed to do one thing, and one thing only: encode an order of execution separately from the order of lines in Python source code. +Twisted tackles this problem with :py:class:`Deferred `\s, a type of object designed to do one thing, and one thing only: encode an order of execution separately from the order of lines in Python source code. It doesn't deal with threads, parallelism, signals, or subprocesses. It doesn't know anything about an event loop, greenlets, or scheduling. @@ -133,7 +133,7 @@ That introduced a dozen new concepts in a couple of lines of code, so let's break it down. If you think you've got it, you might want to skip to the next section. -Here, ``pod_bay_doors.open()`` is returning a :api:`twisted.internet.defer.Deferred `, which we assign to ``d``. +Here, ``pod_bay_doors.open()`` is returning a :py:class:`Deferred `, which we assign to ``d``. We can think of ``d`` as a placeholder, representing the value that ``open()`` will eventually return when it finally gets around to finishing. To "do this next", we add a *callback* to ``d``. @@ -148,9 +148,9 @@ Getting it right: The failure cases ----------------------------------- -In what follows, we are going to take each way of expressing order of operations in normal Python (using lines of code and ``try``/``except``) and translate them into an equivalent code built with :api:`twisted.internet.defer.Deferred ` objects. +In what follows, we are going to take each way of expressing order of operations in normal Python (using lines of code and ``try``/``except``) and translate them into an equivalent code built with :py:class:`Deferred ` objects. -This is going to be a bit painstaking, but if you want to really understand how to use :api:`twisted.internet.defer.Deferred `\s and maintain code that uses them, it is worth understanding each example below. +This is going to be a bit painstaking, but if you want to really understand how to use :py:class:`Deferred `\s and maintain code that uses them, it is worth understanding each example below. One thing, then another, then another @@ -169,7 +169,7 @@ What if neither ``get_names`` nor ``sorted`` can be relied on to finish before they return? That is, if both are asynchronous operations? -Well, in Twisted-speak they would return :api:`twisted.internet.defer.Deferred `\s and so we would write:: +Well, in Twisted-speak they would return :py:class:`Deferred `\s and so we would write:: d = x.get_names() d.addCallback(sorted) @@ -195,7 +195,7 @@ except Exception as e: report_error(e) -How would we write this with :api:`twisted.internet.defer.Deferred `\s? +How would we write this with :py:class:`Deferred `\s? .. code-block:: python @@ -205,7 +205,7 @@ *errback* is the Twisted name for a callback that is called when an error is received. This glosses over an important detail. -Instead of getting the exception object ``e``, ``report_error`` would get a :api:`twisted.python.failure.Failure ` object, which has all of the useful information that ``e`` does, but is optimized for use with :api:`twisted.internet.defer.Deferred `\s. +Instead of getting the exception object ``e``, ``report_error`` would get a :py:class:`Failure ` object, which has all of the useful information that ``e`` does, but is optimized for use with :py:class:`Deferred `\s. We'll dig into that a bit later, after we've dealt with all of the other combinations of exceptions. @@ -223,7 +223,7 @@ else: h(y) -Well, we'd write it like this with :api:`twisted.internet.defer.Deferred `\s:: +Well, we'd write it like this with :py:class:`Deferred `\s:: d = f() d.addCallbacks(h, g) @@ -247,7 +247,7 @@ y = g(e) h(y) -And with :api:`twisted.internet.defer.Deferred `\s:: +And with :py:class:`Deferred `\s:: d = f() d.addErrback(g) @@ -274,7 +274,7 @@ except Exception as e: g(e) -With :api:`twisted.internet.defer.Deferred `\s, it would look like this:: +With :py:class:`Deferred `\s, it would look like this:: d = f() d.addCallback(h) @@ -308,13 +308,80 @@ d.addCallbacks(g, g) Why "roughly"? -Because if ``f`` raises, ``g`` will be passed a :api:`twisted.python.failure.Failure ` object representing the exception. +Because if ``f`` raises, ``g`` will be passed a :py:class:`Failure ` object representing the exception. Otherwise, ``g`` will be passed the asynchronous equivalent of the return value of ``f()`` (i.e. ``y``). +Coroutines with async/await +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. note:: + + .. versionadded:: 16.4 + +Python 3.5 introduced :pep:`492` ("Coroutines with async and await syntax") and native coroutines. +:py:meth:`Deferred.fromCoroutine ` allows you to write coroutines with the ``async def`` syntax and ``await`` on Deferreds, similar to ``inlineCallbacks``. +Rather than decorating every function that may ``await`` a Deferred (as you would with functions that ``yield`` Deferreds with ``inlineCallbacks``), you only need to call ``fromCoroutine`` with the outer-most coroutine object to schedule it for execution. +Coroutines can ``await`` other coroutines once running without needing to use this function themselves. + +.. note:: + + The :py:func:`ensureDeferred ` function also provides a way to convert a coroutine to a Deferred, but it's interface is more type-ambiguous; ``Deferred.fromCoroutine`` is meant to replace it. + +Awaiting on a Deferred which fires with a Failure will raise the exception inside your coroutine as if it were regular Python. +If your coroutine raises an exception, it will be translated into a Failure fired on the Deferred that ``Deferred.fromCoroutine`` returns for you. +Calling ``return`` will cause the Deferred that ``Deferred.fromCoroutine`` returned for you to fire with a result. + +.. code-block:: python3 + + import json + from twisted.internet.defer import Deferred + from twisted.logger import Logger + log = Logger() + + async def getUsers(): + try: + return json.loads(await makeRequest("GET", "/users")) + except ConnectionError: + log.failure("makeRequest failed due to connection error") + return [] + + def do(): + d = Deferred.fromCoroutine(getUsers()) + d.addCallback(print) + return d + + +When writing coroutines, you do not need to use :py:meth:`Deferred.fromCoroutine ` when you are writing a coroutine which calls other coroutines which await on Deferreds; you can just ``await`` on it directly. +For example: + +.. code-block:: python3 + + async def foo(): + res = await someFunctionThatReturnsADeferred() + return res + + async def bar(): + baz = await someOtherDeferredFunction() + fooResult = await foo() + return baz + fooResult + + def myDeferredReturningFunction(): + coro = bar() + return Deferred.fromCoroutine(coro) + + +Even though Deferreds were used in both coroutines, only ``bar`` had to be wrapped in :py:meth:`Deferred.fromCoroutine ` to return a Deferred. + + Inline callbacks - using 'yield' ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. note:: + + Unless your code supports Python 2 (and therefore needs compatibility with older versions of Twisted), writing coroutines with the functionality described in "Coroutines with async/await" is preferred over ``inlineCallbacks``. + Coroutines are supported by dedicated Python syntax, are compatible with ``asyncio``, and provide higher performance. + Twisted features a decorator named ``inlineCallbacks`` which allows you to work with Deferreds without writing callback functions. This is done by writing your code as generators, which *yield* ``Deferred``\ s instead of attaching callbacks. @@ -392,71 +459,10 @@ Our exception handling is simplified because we can use Python's familiar ``try`` / ``except`` syntax for handling ``ConnectionError``\ s. -Coroutines with async/await -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. note:: - - Only available on Python 3.5 and higher. - - .. versionadded:: 16.4 - -On Python 3.5 and higher, the :pep:`492` ("Coroutines with async and await syntax") "await" functionality can be used with Deferreds by the use of :api:`twisted.internet.defer.ensureDeferred `. -It is similar to ``inlineCallbacks``, except that it uses the ``await`` keyword instead of ``yield``, the ``return`` keyword instead of ``returnValue``, and is a function rather than a decorator. - -Calling a coroutine (that is, the result of a function defined by ``async def funcname():``) with :api:`twisted.internet.defer.ensureDeferred ` will allow you to "await" on Deferreds inside it, and will return a standard Deferred. -You can mix and match code which uses regular Deferreds, ``inlineCallbacks``, and ``ensureDeferred`` freely. - -Awaiting on a Deferred which fires with a Failure will raise the exception inside your coroutine as if it were regular Python. -If your coroutine raises an exception, it will be translated into a Failure fired on the Deferred that ``ensureDeferred`` returns for you. -Calling ``return`` will cause the Deferred that ``ensureDeferred`` returned for you to fire with a result. - -.. code-block:: python3 - - import json - from twisted.internet.defer import ensureDeferred - from twisted.logger import Logger - log = Logger() - - async def getUsers(): - try: - return json.loads(await makeRequest("GET", "/users")) - except ConnectionError: - log.failure("makeRequest failed due to connection error") - return [] - - def do(): - d = ensureDeferred(getUsers()) - d.addCallback(print) - return d - - -When writing coroutines, you do not need to use :api:`twisted.internet.defer.ensureDeferred ` when you are writing a coroutine which calls other coroutines which await on Deferreds; you can just ``await`` on it directly. -For example: - -.. code-block:: python3 - - async def foo(): - res = await someFunctionThatReturnsADeferred() - return res - - async def bar(): - baz = await someOtherDeferredFunction() - fooResult = await foo() - return baz + fooResult - - def myDeferredReturningFunction(): - coro = bar() - return ensureDeferred(coro) - - -Even though Deferreds were used in both coroutines, only ``bar`` had to be wrapped in :api:`twisted.internet.defer.ensureDeferred ` to return a Deferred. - - Conclusion ---------- -You have been introduced to asynchronous code and have seen how to use :api:`twisted.internet.defer.Deferred `\s to: +You have been introduced to asynchronous code and have seen how to use :py:class:`Deferred `\s to: - Do something after an asynchronous operation completes successfully - Use the result of a successful asynchronous operation @@ -466,8 +472,8 @@ - Wrap multiple asynchronous operations with one error handler - Do something after an asynchronous operation, regardless of whether it succeeded or failed - Write code without callbacks using ``inlineCallbacks`` -- Write coroutines that interact with Deferreds using ``ensureDeferred`` +- Write coroutines that interact with Deferreds using ``Deferred.fromCoroutine`` -These are very basic uses of :api:`twisted.internet.defer.Deferred `. +These are very basic uses of :py:class:`Deferred `. For detailed information about how they work, how to combine multiple Deferreds, and how to write code that mixes synchronous and asynchronous APIs, see the :doc:`Deferred reference `. Alternatively, read about how to write functions that :doc:`generate Deferreds `. diff -Nru twisted-20.3.0/docs/core/howto/defer.rst twisted-22.1.0/docs/core/howto/defer.rst --- twisted-20.3.0/docs/core/howto/defer.rst 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/defer.rst 2022-02-07 13:12:15.000000000 +0000 @@ -1,7 +1,7 @@ Deferred Reference ================== -This document is a guide to the behaviour of the :api:`twisted.internet.defer.Deferred ` object, and to various ways you can use them when they are returned by functions. +This document is a guide to the behaviour of the :py:class:`twisted.internet.defer.Deferred` object, and to various ways you can use them when they are returned by functions. This document assumes that you are familiar with the basic principle that the Twisted framework is structured around: asynchronous, callback-based programming, where instead of having blocking code in your program or using threads to run blocking code, you have functions that return immediately and then begin a callback chain when data is available. @@ -17,7 +17,7 @@ Deferreds --------- -Twisted uses the :api:`twisted.internet.defer.Deferred ` object to manage the callback sequence. +Twisted uses the :py:class:`Deferred ` object to manage the callback sequence. The client application attaches a series of functions to the deferred to be called in order when the results of the asynchronous request are available (this series of functions is known as a series of **callbacks**, or a **callback chain**), together with a series of functions to be called if there is an error in the asynchronous request (known as a series of **errbacks** or an **errback chain**). The asynchronous library code calls the first callback when the result is available, or the first errback when an error occurs, and the ``Deferred`` object then hands the results of each callback or errback function to the next function in the chain. @@ -25,7 +25,7 @@ Callbacks --------- -A :api:`twisted.internet.defer.Deferred ` is a promise that a function will at some point have a result. +A :py:class:`twisted.internet.defer.Deferred` is a promise that a function will at some point have a result. We can attach callback functions to a Deferred, and once it gets a result these callbacks will be called. In addition Deferreds allow the developer to register a callback for an error, with the default behavior of logging the error. The deferred mechanism standardizes the application programmer's interface with all sorts of blocking or delayed operations. @@ -71,7 +71,7 @@ Multiple callbacks can be added to a Deferred. The first callback in the Deferred's callback chain will be called with the result, the second with the result of the first callback, and so on. Why do we need this? -Well, consider a Deferred returned by :api:`twisted.enterprise.adbapi ` - the result of a SQL query. +Well, consider a Deferred returned by :py:mod:`twisted.enterprise.adbapi` - the result of a SQL query. A web widget might add a callback that converts this result into HTML, and pass the Deferred onwards, where the callback will be used by twisted to return the result to the HTTP client. The callback chain will be bypassed in case of errors or exceptions. @@ -171,14 +171,14 @@ #. When the result is ready, give it to the Deferred object. ``.callback(result)`` if the operation succeeded, ``.errback(failure)`` if it failed. - Note that ``failure`` is typically an instance of a :api:`twisted.python.failure.Failure ` instance. + Note that ``failure`` is typically an instance of a :py:class:`twisted.python.failure.Failure` instance. #. Deferred object triggers previously-added (call/err)back with the ``result`` or ``failure``. Execution then follows the following rules, going down the chain of callbacks to be processed. - Result of the callback is always passed as the first argument to the next callback, creating a chain of processors. - If a callback raises an exception, switch to errback. - An unhandled failure gets passed down the line of errbacks, this creating an asynchronous analog to a series to a series of ``except:`` statements. - - If an errback doesn't raise an exception or return a :api:`twisted.python.failure.Failure ` instance, switch to callback. + - If an errback doesn't raise an exception or return a :py:class:`twisted.python.failure.Failure` instance, switch to callback. Errbacks @@ -187,7 +187,7 @@ Deferred's error handling is modeled after Python's exception handling. In the case that no errors occur, all the callbacks run, one after the other, as described above. -If the errback is called instead of the callback (e.g. because a DB query raised an error), then a :api:`twisted.python.failure.Failure ` is passed into the first errback (you can add multiple errbacks, just like with callbacks). +If the errback is called instead of the callback (e.g. because a DB query raised an error), then a :py:class:`twisted.python.failure.Failure` is passed into the first errback (you can add multiple errbacks, just like with callbacks). You can think of your errbacks as being like ``except`` blocks of ordinary Python code. Unless you explicitly ``raise`` an error in an except block, the ``Exception`` is caught and stops propagating, and normal execution continues. @@ -197,7 +197,7 @@ *Note:* If an errback doesn't return anything, then it effectively returns ``None``, meaning that callbacks will continue to be executed after this errback. This may not be what you expect to happen, so be careful. Make sure your errbacks return a ``Failure`` (probably the one that was passed to it), or a meaningful return value for the next callback. -Also, :api:`twisted.python.failure.Failure ` instances have a useful method called trap, allowing you to effectively do the equivalent of: +Also, :py:class:`twisted.python.failure.Failure` instances have a useful method called trap, allowing you to effectively do the equivalent of: .. code-block:: python @@ -224,7 +224,7 @@ If none of arguments passed to ``failure.trap`` match the error encapsulated in that ``Failure``, then it re-raises the error. There's another potential "gotcha" here. -There's a method :api:`twisted.internet.defer.Deferred.addCallbacks ` which is similar to, but not exactly the same as, ``addCallback`` followed by ``addErrback``. +There's a method :py:meth:`twisted.internet.defer.Deferred.addCallbacks` which is similar to, but not exactly the same as, ``addCallback`` followed by ``addErrback``. In particular, consider these two cases: .. code-block:: python @@ -311,7 +311,7 @@ return d Our original implementation of ``authenticateUser`` expected ``isValidUser`` to be synchronous, but now we need to change it to handle both synchronous and asynchronous implementations of ``isValidUser``. -For this, we use :api:`twisted.internet.defer.maybeDeferred ` to call ``isValidUser``, ensuring that the result of ``isValidUser`` is a Deferred, even if ``isValidUser`` is a synchronous function: +For this, we use :py:func:`maybeDeferred ` to call ``isValidUser``, ensuring that the result of ``isValidUser`` is a Deferred, even if ``isValidUser`` is a synchronous function: .. code-block:: python @@ -374,7 +374,7 @@ #. cause the underlying connection operation to be terminated, if it is still ongoing #. cause the connectionAttempt Deferred to be completed, one way or another, in a timely manner -#. *likely* cause the connectionAttempt Deferred to be errbacked with :api:`CancelledError ` +#. *likely* cause the connectionAttempt Deferred to be errbacked with :py:class:`CancelledError ` You may notice that that set of consequences is very heavily qualified. Although cancellation indicates the calling API's *desire* for the underlying operation to be stopped, the underlying operation cannot necessarily react immediately. @@ -463,12 +463,12 @@ -------- Timeouts are a special case of :ref:`Cancellation `. -Let's say we have a :api:`twisted.internet.defer.Deferred ` representing a task that may take a long time. -We want to put an upper bound on that task, so we want the :api:`twisted.internet.defer.Deferred ` to time +Let's say we have a :py:class:`Deferred ` representing a task that may take a long time. +We want to put an upper bound on that task, so we want the :py:class:`Deferred ` to time out X seconds in the future. -A convenient API to do so is :api:`twisted.internet.defer.Deferred.addTimeout `. -By default, it will fail with a :api:`twisted.internet.defer.TimeoutError ` if the :api:`twisted.internet.defer.Deferred ` hasn't fired (with either an errback or a callback) within ``timeout`` seconds. +A convenient API to do so is :py:meth:`Deferred.addTimeout `. +By default, it will fail with a :py:class:`TimeoutError ` if the :py:class:`Deferred ` hasn't fired (with either an errback or a callback) within ``timeout`` seconds. .. code-block:: python @@ -493,12 +493,12 @@ task.react(main) -:api:`twisted.internet.defer.Deferred.addTimeout ` uses the :api:`twisted.internet.defer.Deferred.cancel ` function under the hood, but can distinguish between a user's call to :api:`twisted.internet.defer.Deferred.cancel ` and a cancellation due to a timeout. -By default, :api:`twisted.internet.defer.Deferred.addTimeout ` translates a :api:`twisted.internet.defer.CancelledError ` produced by the timeout into a :api:`twisted.internet.error.TimeoutError `. +:py:meth:`Deferred.addTimeout ` uses the :py:meth:`Deferred.cancel ` function under the hood, but can distinguish between a user's call to :py:meth:`Deferred.cancel ` and a cancellation due to a timeout. +By default, :py:meth:`Deferred.addTimeout ` translates a :py:class:`CancelledError ` produced by the timeout into a :py:class:`TimeoutError `. -However, if you provided a custom :ref:`cancellation ` when creating the :api:`twisted.internet.defer.Deferred `, then cancelling it may not produce a :api:`twisted.internet.defer.CancelledError `. In this case, the default behavior of :api:`twisted.internet.defer.Deferred.addTimeout ` is to preserve whatever callback or errback value your custom cancellation function produced. This can be useful if, for instance, a cancellation or timeout should produce a default value instead of an error. +However, if you provided a custom :ref:`cancellation ` when creating the :py:class:`Deferred `, then cancelling it may not produce a :py:class:`CancelledError `. In this case, the default behavior of :py:meth:`Deferred.addTimeout ` is to preserve whatever callback or errback value your custom cancellation function produced. This can be useful if, for instance, a cancellation or timeout should produce a default value instead of an error. -:api:`twisted.internet.defer.Deferred.addTimeout ` also takes an optional callable ``onTimeoutCancel`` which is called immediately after the deferred times out. ``onTimeoutCancel`` is not called if it the deferred is otherwise cancelled before the timeout. It takes an arbitrary value, which is the value of the deferred at that exact time (probably a :api:`twisted.internet.defer.CancelledError ` :api:`twisted.python.failure.Failure `), and the ``timeout``. This can be useful if, for instance, the cancellation or timeout does not result in an error but you want to log the timeout anyway. It can also be used to alter the return value. +:py:meth:`Deferred.addTimeout ` also takes an optional callable ``onTimeoutCancel`` which is called immediately after the deferred times out. ``onTimeoutCancel`` is not called if it the deferred is otherwise cancelled before the timeout. It takes an arbitrary value, which is the value of the deferred at that exact time (probably a :py:class:`CancelledError ` :py:class:`Failure `), and the ``timeout``. This can be useful if, for instance, the cancellation or timeout does not result in an error but you want to log the timeout anyway. It can also be used to alter the return value. .. code-block:: python @@ -520,7 +520,7 @@ task.react(main) -Note that the exact place in the callback chain that :api:`twisted.internet.defer.Deferred.addTimeout ` is added determines how much of the callback chain should be timed out. The timeout encompasses all the callbacks and errbacks added to the :api:`twisted.internet.defer.Deferred ` before the call to :api:`twisted.internet.defer.Deferred.addTimeout `, and none of the callbacks and errbacks added after the call. The timeout also starts counting down as soon as soon as it's invoked. +Note that the exact place in the callback chain that :py:meth:`Deferred.addTimeout ` is added determines how much of the callback chain should be timed out. The timeout encompasses all the callbacks and errbacks added to the :py:class:`Deferred ` before the call to :py:meth:`addTimeout `, and none of the callbacks and errbacks added after the call. The timeout also starts counting down as soon as soon as it's invoked. .. _core-howto-defer-deferredlist: @@ -531,7 +531,7 @@ Sometimes you want to be notified after several different events have all happened, rather than waiting for each one individually. For example, you may want to wait for all the connections in a list to close. -:api:`twisted.internet.defer.DeferredList ` is the way to do this. +:py:class:`twisted.internet.defer.DeferredList` is the way to do this. To create a DeferredList from multiple Deferreds, you simply pass a list of the Deferreds you want it to wait for: @@ -642,7 +642,7 @@ ~~~~~~~~~~~~~ A common use for DeferredList is to "join" a number of parallel asynchronous operations, finishing successfully if all of the operations were successful, or failing if any one of the operations fails. -In this case, :api:`twisted.internet.defer.gatherResults ` is a useful shortcut: +In this case, :py:func:`twisted.internet.defer.gatherResults` is a useful shortcut: .. code-block:: python diff -Nru twisted-20.3.0/docs/core/howto/design.rst twisted-22.1.0/docs/core/howto/design.rst --- twisted-20.3.0/docs/core/howto/design.rst 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/design.rst 2022-02-07 13:12:15.000000000 +0000 @@ -88,7 +88,7 @@ they also follow this approach. You can use them independently because they are not stuck to each other. They communicate in well-defined ways, and only when that -communication provides some additional feature. Thus, you can use :api:`twisted.web ` with :api:`twisted.enterprise ` , but neither requires the other, because +communication provides some additional feature. Thus, you can use :py:mod:`twisted.web` with :py:mod:`twisted.enterprise` , but neither requires the other, because they are integrated around the concept of :doc:`Deferreds ` . diff -Nru twisted-20.3.0/docs/core/howto/dirdbm.rst twisted-22.1.0/docs/core/howto/dirdbm.rst --- twisted-20.3.0/docs/core/howto/dirdbm.rst 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/dirdbm.rst 2022-02-07 13:12:15.000000000 +0000 @@ -16,17 +16,17 @@ -:api:`twisted.persisted.dirdbm.DirDBM ` is a DBM-like storage system. +:py:class:`twisted.persisted.dirdbm.DirDBM` is a DBM-like storage system. That is, it stores mappings between keys and values, like a Python dictionary, except that it stores the values in files in a directory - each entry is a different file. The keys must always be strings, -as are the values. Other than that, :api:`twisted.persisted.dirdbm.DirDBM ` +as are the values. Other than that, :py:class:`DirDBM ` objects act just like Python dictionaries. -:api:`twisted.persisted.dirdbm.DirDBM ` is useful for cases +:py:class:`DirDBM ` is useful for cases when you want to store small amounts of data in an organized fashion, without having to deal with the complexity of a RDBMS or other sophisticated database. It is simple, easy to use, cross-platform, and doesn't require any external C libraries, unlike @@ -60,7 +60,7 @@ Sometimes it is necessary to persist more complicated objects than strings. -With some care, :api:`twisted.persisted.dirdbm.Shelf ` +With some care, :py:class:`dirdbm.Shelf ` can transparently persist them. ``Shelf`` works exactly like ``DirDBM`` , except that the values (but not the keys) can be arbitrary picklable objects. However, diff -Nru twisted-20.3.0/docs/core/howto/endpoints.rst twisted-22.1.0/docs/core/howto/endpoints.rst --- twisted-20.3.0/docs/core/howto/endpoints.rst 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/endpoints.rst 2022-02-07 13:12:15.000000000 +0000 @@ -12,7 +12,7 @@ On a network, one can think of any given connection as a long wire, stretched between two points. Lots of stuff can happen along the length of that wire - routers, switches, network address translation, and so on, but that is usually invisible to the application passing data across it. -Twisted strives to make the nature of the "wire" as transparent as possible, with highly abstract interfaces for passing and receiving data, such as :api:`twisted.internet.interfaces.ITransport ` and :api:`twisted.internet.interfaces.IProtocol `. +Twisted strives to make the nature of the "wire" as transparent as possible, with highly abstract interfaces for passing and receiving data, such as :py:class:`ITransport ` and :py:class:`IProtocol `. However, the application can't be completely ignorant of the wire. In particular, it must do something to *start* the connection, and @@ -22,7 +22,7 @@ common theme is that one side of the connection waits around for someone to connect to it, and the other side does the connecting. -In Twisted 10.1, several new interfaces were introduced to describe each of these roles for stream-oriented connections: :api:`twisted.internet.interfaces.IStreamServerEndpoint ` and :api:`twisted.internet.interfaces.IStreamClientEndpoint `. +In Twisted 10.1, several new interfaces were introduced to describe each of these roles for stream-oriented connections: :py:class:`IStreamServerEndpoint ` and :py:class:`IStreamClientEndpoint `. The word "stream", in this case, refers to endpoints which treat a connection as a continuous stream of bytes, rather than a sequence of discrete datagrams: TCP is a "stream" protocol whereas UDP is a "datagram" protocol. @@ -35,7 +35,7 @@ In both of those tutorials, we constructed specific types of endpoints directly. However, in most programs, you will want to allow the user to specify where to listen or connect, in a way which will allow the user to request different strategies, without having to adjust your program. -In order to allow this, you should use :api:`twisted.internet.endpoints.clientFromString ` or :api:`twisted.internet.endpoints.serverFromString `. +In order to allow this, you should use :py:func:`clientFromString ` or :py:func:`serverFromString `. There's Not Much To It @@ -52,14 +52,14 @@ Servers and Stopping ~~~~~~~~~~~~~~~~~~~~ -:api:`twisted.internet.interfaces.IStreamServerEndpoint.listen ` returns a :api:`twisted.internet.defer.Deferred ` that fires with an :api:`twisted.internet.interfaces.IListeningPort `. +:py:meth:`IStreamServerEndpoint.listen ` returns a :py:class:`Deferred ` that fires with an :py:class:`IListeningPort `. Note that this deferred may errback. The most common cause of such an error would be that another program is already using the requested port number, but the exact cause may vary depending on what type of endpoint you are listening on. If you receive such an error, it means that your application is not actually listening, and will not receive any incoming connections. It's important to somehow alert an administrator of your server, in this case, especially if you only have one listening port! Note also that once this has succeeded, it will continue listening forever. -If you need to *stop* listening for some reason, in response to anything other than a full server shutdown (``reactor.stop`` and / or ``twistd`` will usually handle that case for you), make sure you keep a reference around to that listening port object so you can call :api:`twisted.internet.interfaces.IListeningPort.stopListening ` on it. +If you need to *stop* listening for some reason, in response to anything other than a full server shutdown (``reactor.stop`` and / or ``twistd`` will usually handle that case for you), make sure you keep a reference around to that listening port object so you can call :py:meth:`IListeningPort.stopListening ` on it. Finally, keep in mind that ``stopListening`` itself returns a ``Deferred``, and the port may not have fully stopped listening until that ``Deferred`` has fired. Most server applications will not need to worry about these details. @@ -69,18 +69,18 @@ Clients and Cancelling ~~~~~~~~~~~~~~~~~~~~~~ -:api:`twisted.internet.endpoints.connectProtocol ` connects a :api:`twisted.internet.protocol.Protocol ` instance to a given :api:`twisted.internet.interfaces.IStreamClientEndpoint `. It returns a ``Deferred`` which fires with the ``Protocol`` once the connection has been made. -Connection attempts may fail, and so that :api:`twisted.internet.defer.Deferred ` may also errback. +:py:func:`connectProtocol ` connects a :py:class:`Protocol ` instance to a given :py:class:`IStreamClientEndpoint `. It returns a ``Deferred`` which fires with the ``Protocol`` once the connection has been made. +Connection attempts may fail, and so that :py:class:`Deferred ` may also errback. If it does so, you will have to try again; no further attempts will be made. See the :doc:`client documentation ` for an example use. -:api:`twisted.internet.endpoints.connectProtocol ` is a wrapper around a lower-level API: -:api:`twisted.internet.interfaces.IStreamClientEndpoint.connect ` will use a protocol factory for a new outgoing connection attempt. +:py:func:`connectProtocol ` is a wrapper around a lower-level API: +:py:meth:`IStreamClientEndpoint.connect ` will use a protocol factory for a new outgoing connection attempt. It returns a ``Deferred`` which fires with the ``IProtocol`` returned from the factory's ``buildProtocol`` method, or errbacks with the connection failure. Connection attempts may also take a long time, and your users may become bored and wander off. -If this happens, and your code decides, for whatever reason, that you've been waiting for the connection too long, you can call :api:`twisted.internet.defer.Deferred.cancel ` on the ``Deferred`` returned from :api:`twisted.internet.interfaces.IStreamClientEndpoint.connect ` or :api:`twisted.internet.endpoints.connectProtocol `, and the underlying machinery should give up on the connection. -This should cause the ``Deferred`` to errback, usually with :api:`twisted.internet.defer.CancelledError `; +If this happens, and your code decides, for whatever reason, that you've been waiting for the connection too long, you can call :py:meth:`Deferred.cancel ` on the ``Deferred`` returned from :py:meth:`connect ` or :py:func:`connectProtocol `, and the underlying machinery should give up on the connection. +This should cause the ``Deferred`` to errback, usually with :py:class:`CancelledError `; although you should consult the documentation for your particular endpoint type to see if it may do something different. Although some endpoint types may imply a built-in timeout, the @@ -105,7 +105,7 @@ Persistent Client Connections ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -:api:`twisted.application.internet.ClientService` can maintain a persistent outgoing connection to a server which can be started and stopped along with your application. +:py:class:`twisted.application.internet.ClientService` can maintain a persistent outgoing connection to a server which can be started and stopped along with your application. One popular protocol to maintain a long-lived client connection to is IRC, so for an example of ``ClientService``, here's how you would make a long-lived encrypted connection to an IRC server (other details, like how to authenticate, omitted for brevity): @@ -169,7 +169,7 @@ You can use 3 or more too, if you're feeling particularly patient. The default of ``None`` means it will wait forever for a successful connection. -Regardless of ``failAfterFailures``, the ``Deferred`` will always fail with :api:`twisted.internet.defer.CancelledError ` if the service is stopped before a connection is made. +Regardless of ``failAfterFailures``, the ``Deferred`` will always fail with :py:class:`CancelledError ` if the service is stopped before a connection is made. .. code-block:: python @@ -201,7 +201,7 @@ Of course, unless you have only one client and only one server and they're both on localhost, this sort of policy is likely to cause massive performance degradation and thundering herd resource contention in the event of your server's failure, so you probably want to take the second option... -2. You can tweak the default exponential backoff policy with a few parameters by passing the result of :api:`twisted.application.internet.backoffPolicy` to the ``retryPolicy`` argument. +2. You can tweak the default exponential backoff policy with a few parameters by passing the result of :py:func:`twisted.application.internet.backoffPolicy` to the ``retryPolicy`` argument. For example, if you want to make it triple the delay between attempts, but start with a faster connection interval (half a second instead of one second), you could do it like so: .. code-block:: python @@ -234,7 +234,7 @@ or to your objects' constructors. That way, application code that calls your library can provide whatever endpoints are appropriate. -If you are writing an application and you need to construct endpoints yourself, you can allow users to specify arbitrary endpoints described by a string using the :api:`twisted.internet.endpoints.clientFromString ` and :api:`twisted.internet.endpoints.serverFromString ` APIs. +If you are writing an application and you need to construct endpoints yourself, you can allow users to specify arbitrary endpoints described by a string using the :py:func:`clientFromString ` and :py:func:`serverFromString ` APIs. Since these APIs just take a string, they provide flexibility: if Twisted adds support for new types of endpoints (for example, IPv6 endpoints, or WebSocket endpoints), your application will automatically be able to take advantage of them with no changes to its code. @@ -243,10 +243,10 @@ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ For many use-cases, especially the common case of a ``twistd`` plugin which runs a long-running server that just binds a simple port, you might not want to use the endpoints APIs directly. -Instead, you may want to construct an :api:`twisted.application.service.IService `, using :api:`twisted.application.strports.service `, which will fit neatly into the required structure of :doc:`the twistd plugin API `. +Instead, you may want to construct an :py:class:`IService `, using :py:func:`strports.service `, which will fit neatly into the required structure of :doc:`the twistd plugin API `. This doesn't give your application much control - the port starts listening at startup and stops listening at shutdown - but it does provide the same flexibility in terms of what type of server endpoint your application will support. -It is, however, almost always preferable to use an endpoint rather than calling a lower-level APIs like :api:`twisted.internet.interfaces.IReactorTCP.connectTCP `, :api:`twisted.internet.interfaces.IReactorTCP.listenTCP `, etc, directly. +It is, however, almost always preferable to use an endpoint rather than calling a lower-level APIs like :py:meth:`connectTCP `, :py:meth:`listenTCP `, etc, directly. By accepting an arbitrary endpoint rather than requiring a specific reactor interface, you leave your application open to lots of interesting transport-layer extensibility for the future. diff -Nru twisted-20.3.0/docs/core/howto/gendefer.rst twisted-22.1.0/docs/core/howto/gendefer.rst --- twisted-20.3.0/docs/core/howto/gendefer.rst 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/gendefer.rst 2022-02-07 13:12:15.000000000 +0000 @@ -14,7 +14,7 @@ .. status of document: INCOMPLETE, DRAFT -:api:`twisted.internet.defer.Deferred ` objects are +:py:class:`Deferred ` objects are signals that a function you have called does not yet have the data you want available. When a function returns a Deferred object, your calling function attaches callbacks to it to handle the data when available. @@ -78,7 +78,7 @@ Run success callbacks with the given result. *This can only be run once.* Later calls to this or - ``errback`` will raise :api:`twisted.internet.defer.AlreadyCalledError ` . + ``errback`` will raise :py:class:`twisted.internet.defer.AlreadyCalledError` . If further callbacks or errbacks are added after this point, addCallbacks will run the callbacks immediately. @@ -88,7 +88,7 @@ Run error callbacks with the given failure. *This can only be run once.* Later calls to this or - ``callback`` will raise :api:`twisted.internet.defer.AlreadyCalledError ` . + ``callback`` will raise :py:class:`twisted.internet.defer.AlreadyCalledError` . If further callbacks or errbacks are added after this point, addCallbacks will run the callbacks immediately. @@ -263,8 +263,8 @@ While we can require that callers of our function wrap our synchronous -result in a Deferred using :api:`twisted.internet.defer.maybeDeferred ` , for the sake of API -compatibility it is better to return a Deferred ourselves using :api:`twisted.internet.defer.succeed ` : +result in a Deferred using :py:func:`maybeDeferred ` , for the sake of API +compatibility it is better to return a Deferred ourselves using :py:func:`defer.succeed ` : @@ -289,7 +289,7 @@ -There is an equivalent :api:`twisted.internet.defer.fail ` method to return a Deferred with the +There is an equivalent :py:func:`defer.fail ` method to return a Deferred with the errback chain already fired. @@ -312,7 +312,7 @@ In this case, Twisted provides the ability to run the blocking code in a -separate thread rather than letting it block your application. The :api:`twisted.internet.threads.deferToThread ` function will set up +separate thread rather than letting it block your application. The :py:func:`twisted.internet.threads.deferToThread` function will set up a thread to run your blocking function, return a Deferred and later fire that Deferred when the thread completes. diff -Nru twisted-20.3.0/docs/core/howto/glossary.rst twisted-22.1.0/docs/core/howto/glossary.rst --- twisted-20.3.0/docs/core/howto/glossary.rst 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/glossary.rst 2022-02-07 13:12:15.000000000 +0000 @@ -29,7 +29,7 @@ -:api:`twisted.python.components.Adapter ` +:py:class:`Adapter ` @@ -41,12 +41,12 @@ -:api:`twisted.application.service.Application ` +:py:func:`Application ` - A :api:`twisted.application.service.Application ` . There are + A :py:func:`twisted.application.service.Application` . There are HOWTOs on :doc:`creating and manipulating ` them as a system-administrator, as well as :doc:`using ` them in your code. @@ -68,24 +68,24 @@ -:api:`twisted.spread.banana.Banana ` +:py:class:`Banana ` The low-level data marshalling layer of :ref:`Twisted Spread ` . - See :api:`twisted.spread.banana ` . + See :py:mod:`twisted.spread.banana` . .. _core-howto-glossary-broker: -:api:`twisted.spread.pb.Broker ` +:py:class:`Broker ` - A :api:`twisted.spread.pb.Broker ` , the object request + A :py:class:`twisted.spread.pb.Broker` , the object request broker for :ref:`Twisted Spread ` . .. _core-howto-glossary-cache: @@ -110,13 +110,13 @@ - A special kind of (persistent) :api:`twisted.python.components.Adapter ` that works with a :api:`twisted.python.components.Componentized ` . See also :doc:`Interfaces and Adapters ` . + A special kind of (persistent) :py:class:`Adapter ` that works with a :py:class:`twisted.python.components.Componentized` . See also :doc:`Interfaces and Adapters ` . .. _core-howto-glossary-componentized: -:api:`twisted.python.components.Componentized ` +:py:class:`Componentized ` @@ -124,14 +124,14 @@ A Componentized object is a collection of information, separated into domain-specific or role-specific instances, that all stick together and refer to each other. - Each object is an :api:`twisted.python.components.Adapter ` , which, in the + Each object is an :py:class:`Adapter ` , which, in the context of Componentized, we call "components" . See also :doc:`Interfaces and Adapters ` . .. _core-howto-glossary-conch: -:api:`twisted.conch ` +:py:mod:`conch ` @@ -147,8 +147,8 @@ Object used to interface between client connections and protocols, usually - used with a :api:`twisted.internet.protocol.ClientFactory ` - to give you control over how a client connection reconnects. See :api:`twisted.internet.interfaces.IConnector ` and :doc:`Writing Clients ` . + used with a :py:class:`twisted.internet.protocol.ClientFactory` + to give you control over how a client connection reconnects. See :py:class:`twisted.internet.interfaces.IConnector` and :doc:`Writing Clients ` . .. _core-howto-glossary-consumer: @@ -160,7 +160,7 @@ An object that consumes data from a :ref:`Producer ` . See - :api:`twisted.internet.interfaces.IConsumer ` . + :py:class:`twisted.internet.interfaces.IConsumer` . .. _core-howto-glossary-cred: @@ -171,7 +171,7 @@ - Twisted's authentication API, :api:`twisted.cred ` . See + Twisted's authentication API, :py:mod:`twisted.cred` . See :doc:`Introduction to Twisted Cred ` and :doc:`Twisted Cred usage ` . @@ -197,7 +197,7 @@ Where authentication actually happens. See - :api:`twisted.cred.checkers.ICredentialsChecker ` . + :py:class:`ICredentialsChecker ` . .. _core-howto-glossary-cvstoys: @@ -225,12 +225,12 @@ -:api:`twisted.internet.defer.Deferred ` +:py:class:`Deferred ` - An instance of :api:`twisted.internet.defer.Deferred ` , an + An instance of :py:class:`twisted.internet.defer.Deferred` , an abstraction for handling chains of callbacks and error handlers ("errbacks" ). See the :doc:`Deferring Execution ` HOWTO. @@ -244,7 +244,7 @@ - Twisted's RDBMS support. It contains :api:`twisted.enterprise.adbapi ` for asynchronous access to any + Twisted's RDBMS support. It contains :py:mod:`twisted.enterprise.adbapi` for asynchronous access to any standard DB-API 2.0 module. See :doc:`Introduction to Twisted Enterprise ` for more details. .. _core-howto-glossary-errback: @@ -263,13 +263,13 @@ -:api:`twisted.internet.protocol.Factory ` +:py:class:`Factory ` In general, an object that constructs other objects. In Twisted, a Factory - usually refers to a :api:`twisted.internet.protocol.Factory ` , which constructs + usually refers to a :py:class:`twisted.internet.protocol.Factory` , which constructs :ref:`Protocol ` instances for incoming or outgoing connections. See :doc:`Writing Servers ` and :doc:`Writing Clients ` . @@ -277,7 +277,7 @@ -:api:`twisted.python.failure.Failure ` +:py:class:`Failure ` @@ -308,7 +308,7 @@ Instance Messenger is a multi-protocol chat program that comes with Twisted. It can communicate via TOC with the AOL servers, via IRC, as well as via :ref:`PB ` with - :ref:`Twisted Words ` . See :api:`twisted.words.im ` . + :ref:`Twisted Words ` . See :py:mod:`twisted.words.im` . .. _core-howto-glossary-interface: @@ -320,8 +320,8 @@ A class that defines and documents methods that a class conforming to that - interface needs to have. A collection of core :api:`twisted.internet ` interfaces can - be found in :api:`twisted.internet.interfaces ` . See also :doc:`Interfaces and Adapters ` . + interface needs to have. A collection of core :py:mod:`twisted.internet` interfaces can + be found in :py:mod:`twisted.internet.interfaces` . See also :doc:`Interfaces and Adapters ` . .. _core-howto-glossary-jelly: @@ -335,7 +335,7 @@ The serialization layer for :ref:`Twisted Spread ` , although it can be used separately from Twisted Spread as well. It is similar in purpose to Python's standard ``pickle`` module, but is more - network-friendly, and depends on a separate marshaller (:ref:`Banana ` , in most cases). See :api:`twisted.spread.jelly ` . + network-friendly, and depends on a separate marshaller (:ref:`Banana ` , in most cases). See :py:mod:`twisted.spread.jelly` . .. _core-howto-glossary-manhole: @@ -358,7 +358,7 @@ A partial DOM implementation using :ref:`SUX ` . It is simple and - pythonic, rather than strictly standards-compliant. See :api:`twisted.web.microdom ` . + pythonic, rather than strictly standards-compliant. See :py:mod:`twisted.web.microdom` . .. _core-howto-glossary-names: @@ -368,7 +368,7 @@ - Twisted's DNS server, found in :api:`twisted.names ` . + Twisted's DNS server, found in :py:mod:`twisted.names` . .. _core-howto-glossary-nevow: @@ -402,7 +402,7 @@ The high-level object layer of Twisted :ref:`Spread ` , implementing semantics for method calling and object copying, caching, and - referencing. See :api:`twisted.spread.pb ` . + referencing. See :py:mod:`twisted.spread.pb` . .. _core-howto-glossary-portal: @@ -427,20 +427,20 @@ An object that generates data a chunk at a time, usually to be processed by a :ref:`Consumer ` . See - :api:`twisted.internet.interfaces.IProducer ` . + :py:class:`twisted.internet.interfaces.IProducer` . .. _core-howto-glossary-protocol: -:api:`twisted.internet.protocol.Protocol ` +:py:class:`Protocol ` In general each network connection has its own Protocol instance to manage connection-specific state. There is a collection of standard - protocol implementations in :api:`twisted.protocols ` . See + protocol implementations in :py:mod:`twisted.protocols` . See also :doc:`Writing Servers ` and :doc:`Writing Clients ` . .. _core-howto-glossary-psu: @@ -486,18 +486,18 @@ (in :ref:`Twisted Cred ` ) stores :ref:`avatars ` and perhaps general business logic. See - :api:`twisted.cred.portal.IRealm ` . + :py:class:`IRealm ` . .. _core-howto-glossary-resource: -:api:`twisted.web.resource.Resource ` +:py:class:`Resource ` - A :api:`twisted.web.resource.Resource ` , which are served + A :py:class:`twisted.web.resource.Resource` , which are served by Twisted Web. Resources can be as simple as a static file on disk, or they can have dynamically generated content. @@ -510,7 +510,7 @@ - A :api:`twisted.application.service.Service ` . See :doc:`Application howto ` for a description of how they + A :py:class:`twisted.application.service.Service` . See :doc:`Application howto ` for a description of how they relate to :ref:`Applications ` . .. _core-howto-glossary-spread: @@ -534,7 +534,7 @@ *S* mall *U* ncomplicated *X* ML, Twisted's simple XML - parser written in pure Python. See :api:`twisted.web.sux ` . + parser written in pure Python. See :py:mod:`twisted.web.sux` . .. _core-howto-glossary-tac: @@ -567,7 +567,7 @@ - :api:`twisted.trial ` , Twisted's unit-testing framework, + :py:mod:`twisted.trial` , Twisted's unit-testing framework, based on the ``unittest`` standard library module. See also :doc:`Writing tests for Twisted code ` . .. _core-howto-glossary-twistedmatrixlaboratories: @@ -598,11 +598,11 @@ -:api:`twisted.python.usage ` +:py:mod:`usage ` - The :api:`twisted.python.usage ` module, a replacement for + The :py:mod:`twisted.python.usage` module, a replacement for the standard ``getopt`` module for parsing command-lines which is much easier to work with. See :doc:`Parsing command-lines ` . @@ -615,7 +615,7 @@ Twisted Words is a multi-protocol chat server that uses the :ref:`Perspective Broker ` protocol as its native - communication style. See :api:`twisted.words ` . + communication style. See :py:mod:`twisted.words` . .. _core-howto-glossary-woven: diff -Nru twisted-20.3.0/docs/core/howto/internet-overview.rst twisted-22.1.0/docs/core/howto/internet-overview.rst --- twisted-20.3.0/docs/core/howto/internet-overview.rst 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/internet-overview.rst 2022-02-07 13:12:15.000000000 +0000 @@ -22,29 +22,29 @@ Twisted Internet contains the various interfaces to the reactor API, whose usage is documented in the low-level chapter. Those APIs -are :api:`twisted.internet.interfaces.IReactorCore ` , -:api:`twisted.internet.interfaces.IReactorTCP ` , -:api:`twisted.internet.interfaces.IReactorSSL ` , -:api:`twisted.internet.interfaces.IReactorUNIX ` , -:api:`twisted.internet.interfaces.IReactorUDP ` , -:api:`twisted.internet.interfaces.IReactorTime ` , -:api:`twisted.internet.interfaces.IReactorProcess ` , -:api:`twisted.internet.interfaces.IReactorMulticast ` -and :api:`twisted.internet.interfaces.IReactorThreads ` . +are :py:class:`IReactorCore ` , +:py:class:`IReactorTCP ` , +:py:class:`IReactorSSL ` , +:py:class:`IReactorUNIX ` , +:py:class:`IReactorUDP ` , +:py:class:`IReactorTime ` , +:py:class:`IReactorProcess ` , +:py:class:`IReactorMulticast ` +and :py:class:`IReactorThreads ` . The reactor APIs allow non-persistent calls to be made. Twisted Internet also covers the interfaces for the various transports, -in :api:`twisted.internet.interfaces.ITransport ` +in :py:class:`ITransport ` and friends. These interfaces allow Twisted network code to be written without regard to the underlying implementation of the transport. -The :api:`twisted.internet.interfaces.IProtocolFactory ` +The :py:class:`IProtocolFactory ` dictates how factories, which are usually a large part of third party code, are written. diff -Nru twisted-20.3.0/docs/core/howto/listings/amp/basic_client.py twisted-22.1.0/docs/core/howto/listings/amp/basic_client.py --- twisted-20.3.0/docs/core/howto/listings/amp/basic_client.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/amp/basic_client.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,15 +1,16 @@ - -if __name__ == '__main__': +if __name__ == "__main__": import basic_client + raise SystemExit(basic_client.main()) from sys import stdout -from twisted.python.log import startLogging, err -from twisted.protocols.amp import AMP from twisted.internet import reactor -from twisted.internet.protocol import Factory from twisted.internet.endpoints import TCP4ClientEndpoint +from twisted.internet.protocol import Factory +from twisted.protocols.amp import AMP +from twisted.python.log import err, startLogging + def connect(): endpoint = TCP4ClientEndpoint(reactor, "127.0.0.1", 8750) @@ -21,8 +22,10 @@ d = connect() d.addErrback(err, "Connection failed") + def done(ignored): reactor.stop() + d.addCallback(done) reactor.run() diff -Nru twisted-20.3.0/docs/core/howto/listings/amp/basic_server.tac twisted-22.1.0/docs/core/howto/listings/amp/basic_server.tac --- twisted-20.3.0/docs/core/howto/listings/amp/basic_server.tac 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/amp/basic_server.tac 2022-02-07 13:12:15.000000000 +0000 @@ -1,9 +1,9 @@ -from twisted.protocols.amp import AMP +from twisted.application.internet import StreamServerEndpointService +from twisted.application.service import Application from twisted.internet import reactor -from twisted.internet.protocol import Factory from twisted.internet.endpoints import TCP4ServerEndpoint -from twisted.application.service import Application -from twisted.application.internet import StreamServerEndpointService +from twisted.internet.protocol import Factory +from twisted.protocols.amp import AMP application = Application("basic AMP server") diff -Nru twisted-20.3.0/docs/core/howto/listings/amp/command_client.py twisted-22.1.0/docs/core/howto/listings/amp/command_client.py --- twisted-20.3.0/docs/core/howto/listings/amp/command_client.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/amp/command_client.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,49 +1,53 @@ -from __future__ import print_function - -if __name__ == '__main__': +if __name__ == "__main__": import command_client + raise SystemExit(command_client.main()) from sys import stdout -from twisted.python.log import startLogging, err -from twisted.protocols.amp import Integer, String, Unicode, Command +from basic_client import connect + from twisted.internet import reactor +from twisted.protocols.amp import Command, Integer, String, Unicode +from twisted.python.log import err, startLogging -from basic_client import connect class UsernameUnavailable(Exception): pass class RegisterUser(Command): - arguments = [('username', Unicode()), - ('publickey', String())] + arguments = [("username", Unicode()), ("publickey", String())] - response = [('uid', Integer())] + response = [("uid", Integer())] - errors = {UsernameUnavailable: 'username-unavailable'} + errors = {UsernameUnavailable: "username-unavailable"} def main(): startLogging(stdout) d = connect() + def connected(protocol): return protocol.callRemote( RegisterUser, - username=u'alice', - publickey='ssh-rsa AAAAB3NzaC1yc2 alice@actinium') + username="alice", + publickey="ssh-rsa AAAAB3NzaC1yc2 alice@actinium", + ) + d.addCallback(connected) def registered(result): - print('Registration result:', result) + print("Registration result:", result) + d.addCallback(registered) d.addErrback(err, "Failed to register") def finished(ignored): reactor.stop() + d.addCallback(finished) reactor.run() diff -Nru twisted-20.3.0/docs/core/howto/listings/application/service.tac twisted-22.1.0/docs/core/howto/listings/application/service.tac --- twisted-20.3.0/docs/core/howto/listings/application/service.tac 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/application/service.tac 2022-02-07 13:12:15.000000000 +0000 @@ -11,8 +11,10 @@ """ import os -from twisted.application import service, internet -from twisted.web import static, server + +from twisted.application import internet, service +from twisted.web import server, static + def getWebService(): """ @@ -25,6 +27,7 @@ fileServer = server.Site(static.File(os.getcwd())) return internet.TCPServer(8080, fileServer) + # this is the core part of any tac file, the creation of the root-level # application object application = service.Application("Demo application") diff -Nru twisted-20.3.0/docs/core/howto/listings/cred/pop3_server.py twisted-22.1.0/docs/core/howto/listings/cred/pop3_server.py --- twisted-20.3.0/docs/core/howto/listings/cred/pop3_server.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/cred/pop3_server.py 2022-02-07 13:12:15.000000000 +0000 @@ -3,15 +3,17 @@ from zope.interface import Interface -from twisted.protocols import basic -from twisted.python import log from twisted.cred import credentials, error from twisted.internet import defer +from twisted.protocols import basic +from twisted.python import log + class IMailbox(Interface): """ Interface specification for mailbox. """ + def deleteMessage(index): pass @@ -23,13 +25,13 @@ def do_DELE(self, i): # uses self.mbox, which is set after login - i = int(i)-1 + i = int(i) - 1 self.mbox.deleteMessage(i) self.successResponse() def do_USER(self, user): self._userIs = user - self.successResponse('USER accepted, send PASS') + self.successResponse("USER accepted, send PASS") def do_PASS(self, password): if self._userIs is None: @@ -43,9 +45,7 @@ def authenticateUserPASS(self, user, password): if self.portal is not None: return self.portal.login( - credentials.UsernamePassword(user, password), - None, - IMailbox + credentials.UsernamePassword(user, password), None, IMailbox ) raise error.UnauthorizedLogin() @@ -53,11 +53,11 @@ interface, avatar, logout = ial if interface is not IMailbox: - self.failResponse('Authentication failed') + self.failResponse("Authentication failed") log.err("_cbMailbox() called with an interface other than IMailbox") return self.mbox = avatar self._onLogout = logout - self.successResponse('Authentication succeeded') + self.successResponse("Authentication succeeded") log.msg("Authenticated login for " + user) diff -Nru twisted-20.3.0/docs/core/howto/listings/deferred/synch-validation.py twisted-22.1.0/docs/core/howto/listings/deferred/synch-validation.py --- twisted-20.3.0/docs/core/howto/listings/deferred/synch-validation.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/deferred/synch-validation.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,5 +1,5 @@ def synchronousIsValidUser(user): - ''' + """ Return true if user is a valid user, false otherwise - ''' + """ return user in ["Alice", "Angus", "Agnes"] diff -Nru twisted-20.3.0/docs/core/howto/listings/logger/ad_hoc.py twisted-22.1.0/docs/core/howto/listings/logger/ad_hoc.py --- twisted-20.3.0/docs/core/howto/listings/logger/ad_hoc.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/logger/ad_hoc.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,6 +1,7 @@ from twisted.logger import Logger -class AdHoc(object): + +class AdHoc: log = Logger(namespace="ad_hoc") def __init__(self, a, b): @@ -8,5 +9,7 @@ self.b = b def logMessage(self): - self.log.info("message from {log_source} " - "where a is {log_source.a} and b is {log_source.b}") + self.log.info( + "message from {log_source} " + "where a is {log_source.a} and b is {log_source.b}" + ) diff -Nru twisted-20.3.0/docs/core/howto/listings/logger/ad_hoc_save.py twisted-22.1.0/docs/core/howto/listings/logger/ad_hoc_save.py --- twisted-20.3.0/docs/core/howto/listings/logger/ad_hoc_save.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/logger/ad_hoc_save.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,7 +1,9 @@ import io -from twisted.logger import jsonFileLogObserver, globalLogPublisher + from ad_hoc import AdHoc -globalLogPublisher.addObserver(jsonFileLogObserver(io.open("log.json", "a"))) +from twisted.logger import globalLogPublisher, jsonFileLogObserver + +globalLogPublisher.addObserver(jsonFileLogObserver(open("log.json", "a"))) AdHoc(3, 4).logMessage() diff -Nru twisted-20.3.0/docs/core/howto/listings/logger/analyze.py twisted-22.1.0/docs/core/howto/listings/logger/analyze.py --- twisted-20.3.0/docs/core/howto/listings/logger/analyze.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/logger/analyze.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,11 +1,7 @@ -from __future__ import print_function - from twisted.logger import extractField -fmt = ( - "message from {log_source} " - "where a is {log_source.a} and b is {log_source.b}" -) +fmt = "message from {log_source} " "where a is {log_source.a} and b is {log_source.b}" + def analyze(event): if event.get("log_format") == fmt: diff -Nru twisted-20.3.0/docs/core/howto/listings/logger/loader-math.py twisted-22.1.0/docs/core/howto/listings/logger/loader-math.py --- twisted-20.3.0/docs/core/howto/listings/logger/loader-math.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/logger/loader-math.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,7 +1,6 @@ -from __future__ import print_function - import io + from twisted.logger import eventsFromJSONLogFile -for event in eventsFromJSONLogFile(io.open("log.json")): +for event in eventsFromJSONLogFile(open("log.json")): print(sum(event["values"])) diff -Nru twisted-20.3.0/docs/core/howto/listings/logger/loader.py twisted-22.1.0/docs/core/howto/listings/logger/loader.py --- twisted-20.3.0/docs/core/howto/listings/logger/loader.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/logger/loader.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,10 +1,9 @@ -import sys import io -from twisted.logger import ( - eventsFromJSONLogFile, textFileLogObserver -) +import sys + +from twisted.logger import eventsFromJSONLogFile, textFileLogObserver output = textFileLogObserver(sys.stdout) -for event in eventsFromJSONLogFile(io.open("log.json")): +for event in eventsFromJSONLogFile(open("log.json")): output(event) diff -Nru twisted-20.3.0/docs/core/howto/listings/logger/logsource.py twisted-22.1.0/docs/core/howto/listings/logger/logsource.py --- twisted-20.3.0/docs/core/howto/listings/logger/logsource.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/logger/logsource.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,6 +1,7 @@ from twisted.logger import Logger -class MyObject(object): + +class MyObject: log = Logger() def __init__(self, value): @@ -9,7 +10,8 @@ def doSomething(self, something): self.log.info( "Object with value {log_source.value!r} doing {something}.", - something=something + something=something, ) + MyObject(7).doSomething("a task") diff -Nru twisted-20.3.0/docs/core/howto/listings/logger/offline_analyze.py twisted-22.1.0/docs/core/howto/listings/logger/offline_analyze.py --- twisted-20.3.0/docs/core/howto/listings/logger/offline_analyze.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/logger/offline_analyze.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,6 +1,8 @@ import io -from twisted.logger import eventsFromJSONLogFile + from analyze import analyze -for event in eventsFromJSONLogFile(io.open("log.json")): +from twisted.logger import eventsFromJSONLogFile + +for event in eventsFromJSONLogFile(open("log.json")): analyze(event) diff -Nru twisted-20.3.0/docs/core/howto/listings/logger/online_analyze.py twisted-22.1.0/docs/core/howto/listings/logger/online_analyze.py --- twisted-20.3.0/docs/core/howto/listings/logger/online_analyze.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/logger/online_analyze.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,6 +1,7 @@ -from twisted.logger import globalLogPublisher -from analyze import analyze from ad_hoc import AdHoc +from analyze import analyze + +from twisted.logger import globalLogPublisher globalLogPublisher.addObserver(analyze) diff -Nru twisted-20.3.0/docs/core/howto/listings/logger/saver.py twisted-22.1.0/docs/core/howto/listings/logger/saver.py --- twisted-20.3.0/docs/core/howto/listings/logger/saver.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/logger/saver.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,11 +1,13 @@ import io -from twisted.logger import jsonFileLogObserver, Logger -log = Logger(observer=jsonFileLogObserver(io.open("log.json", "a")), - namespace="saver") +from twisted.logger import Logger, jsonFileLogObserver + +log = Logger(observer=jsonFileLogObserver(open("log.json", "a")), namespace="saver") + def loggit(values): log.info("Some values: {values!r}", values=values) + loggit([1234, 5678]) loggit([9876, 5432]) diff -Nru twisted-20.3.0/docs/core/howto/listings/pb/cache_classes.py twisted-22.1.0/docs/core/howto/listings/pb/cache_classes.py --- twisted-20.3.0/docs/core/howto/listings/pb/cache_classes.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/pb/cache_classes.py 2022-02-07 13:12:15.000000000 +0000 @@ -3,43 +3,56 @@ # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. -from __future__ import print_function from twisted.spread import pb + class MasterDuckPond(pb.Cacheable): def __init__(self, ducks): self.observers = [] self.ducks = ducks + def count(self): print("I have [%d] ducks" % len(self.ducks)) + def addDuck(self, duck): self.ducks.append(duck) - for o in self.observers: o.callRemote('addDuck', duck) + for o in self.observers: + o.callRemote("addDuck", duck) + def removeDuck(self, duck): self.ducks.remove(duck) - for o in self.observers: o.callRemote('removeDuck', duck) + for o in self.observers: + o.callRemote("removeDuck", duck) + def getStateToCacheAndObserveFor(self, perspective, observer): self.observers.append(observer) # you should ignore pb.Cacheable-specific state, like self.observers - return self.ducks # in this case, just a list of ducks + return self.ducks # in this case, just a list of ducks + def stoppedObserving(self, perspective, observer): self.observers.remove(observer) + class SlaveDuckPond(pb.RemoteCache): # This is a cache of a remote MasterDuckPond def count(self): return len(self.cacheducks) + def getDucks(self): return self.cacheducks + def setCopyableState(self, state): print(" cache - sitting, er, setting ducks") self.cacheducks = state + def observe_addDuck(self, newDuck): print(" cache - addDuck") self.cacheducks.append(newDuck) + def observe_removeDuck(self, deadDuck): print(" cache - removeDuck") self.cacheducks.remove(deadDuck) + pb.setUnjellyableForClass(MasterDuckPond, SlaveDuckPond) diff -Nru twisted-20.3.0/docs/core/howto/listings/pb/cache_receiver.py twisted-22.1.0/docs/core/howto/listings/pb/cache_receiver.py --- twisted-20.3.0/docs/core/howto/listings/pb/cache_receiver.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/pb/cache_receiver.py 2022-02-07 13:12:15.000000000 +0000 @@ -3,28 +3,34 @@ # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. -from __future__ import print_function -from twisted.application import service, internet +import cache_classes + +from twisted.application import internet, service from twisted.internet import reactor from twisted.spread import pb -import cache_classes + class Receiver(pb.Root): def remote_takePond(self, pond): self.pond = pond - print("got pond:", pond) # a DuckPondCache + print("got pond:", pond) # a DuckPondCache self.remote_checkDucks() + def remote_checkDucks(self): print("[%d] ducks: " % self.pond.count(), self.pond.getDucks()) + def remote_ignorePond(self): # stop watching the pond print("dropping pond") # gc causes __del__ causes 'decache' msg causes stoppedObserving self.pond = None + def remote_shutdown(self): reactor.stop() + application = service.Application("copy_receiver") internet.TCPServer(8800, pb.PBServerFactory(Receiver())).setServiceParent( - service.IServiceCollection(application)) + service.IServiceCollection(application) +) diff -Nru twisted-20.3.0/docs/core/howto/listings/pb/cache_sender.py twisted-22.1.0/docs/core/howto/listings/pb/cache_sender.py --- twisted-20.3.0/docs/core/howto/listings/pb/cache_sender.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/pb/cache_sender.py 2022-02-07 13:12:15.000000000 +0000 @@ -3,11 +3,13 @@ # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. -from twisted.spread import pb, jelly -from twisted.python import log -from twisted.internet import reactor from cache_classes import MasterDuckPond +from twisted.internet import reactor +from twisted.python import log +from twisted.spread import jelly, pb + + class Sender: def __init__(self, pond): self.pond = pond @@ -16,25 +18,31 @@ self.remote = remote d = remote.callRemote("takePond", self.pond) d.addCallback(self.phase2).addErrback(log.err) + def phase2(self, response): self.pond.addDuck("ugly duckling") self.pond.count() reactor.callLater(1, self.phase3) + def phase3(self): d = self.remote.callRemote("checkDucks") d.addCallback(self.phase4).addErrback(log.err) + def phase4(self, dummy): self.pond.removeDuck("one duck") self.pond.count() self.remote.callRemote("checkDucks") d = self.remote.callRemote("ignorePond") d.addCallback(self.phase5) + def phase5(self, dummy): d = self.remote.callRemote("shutdown") d.addCallback(self.phase6) + def phase6(self, dummy): reactor.stop() + def main(): master = MasterDuckPond(["one duck", "two duck"]) master.count() @@ -46,5 +54,6 @@ deferred.addCallback(sender.phase1) reactor.run() -if __name__ == '__main__': + +if __name__ == "__main__": main() diff -Nru twisted-20.3.0/docs/core/howto/listings/pb/chatclient.py twisted-22.1.0/docs/core/howto/listings/pb/chatclient.py --- twisted-20.3.0/docs/core/howto/listings/pb/chatclient.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/pb/chatclient.py 2022-02-07 13:12:15.000000000 +0000 @@ -2,22 +2,20 @@ # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. -from __future__ import print_function -from twisted.spread import pb -from twisted.internet import reactor from twisted.cred import credentials +from twisted.internet import reactor +from twisted.spread import pb -class Client(pb.Referenceable): +class Client(pb.Referenceable): def remote_print(self, message): print(message) def connect(self): factory = pb.PBClientFactory() reactor.connectTCP("localhost", 8800, factory) - def1 = factory.login(credentials.UsernamePassword("alice", "1234"), - client=self) + def1 = factory.login(credentials.UsernamePassword("alice", "1234"), client=self) def1.addCallback(self.connected) reactor.run() @@ -41,4 +39,3 @@ Client().connect() - diff -Nru twisted-20.3.0/docs/core/howto/listings/pb/chatserver.py twisted-22.1.0/docs/core/howto/listings/pb/chatserver.py --- twisted-20.3.0/docs/core/howto/listings/pb/chatserver.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/pb/chatserver.py 2022-02-07 13:12:15.000000000 +0000 @@ -5,13 +5,14 @@ from zope.interface import implementer -from twisted.cred import portal, checkers -from twisted.spread import pb +from twisted.cred import checkers, portal from twisted.internet import reactor +from twisted.spread import pb + class ChatServer: def __init__(self): - self.groups = {} # indexed by name + self.groups = {} # indexed by name def joinGroup(self, groupname, user, allowMattress): if groupname not in self.groups: @@ -19,6 +20,7 @@ self.groups[groupname].addUser(user) return self.groups[groupname] + @implementer(portal.IRealm) class ChatRealm: def requestAvatar(self, avatarID, mind, *interfaces): @@ -26,32 +28,41 @@ avatar = User(avatarID) avatar.server = self.server avatar.attached(mind) - return pb.IPerspective, avatar, lambda a=avatar:a.detached(mind) + return pb.IPerspective, avatar, lambda a=avatar: a.detached(mind) + class User(pb.Avatar): def __init__(self, name): self.name = name + def attached(self, mind): self.remote = mind + def detached(self, mind): self.remote = None + def perspective_joinGroup(self, groupname, allowMattress=True): return self.server.joinGroup(groupname, self, allowMattress) + def send(self, message): self.remote.callRemote("print", message) + class Group(pb.Viewable): def __init__(self, groupname, allowMattress): self.name = groupname self.allowMattress = allowMattress self.users = [] + def addUser(self, user): self.users.append(user) + def view_send(self, from_user, message): if not self.allowMattress and "mattress" in message: raise ValueError("Don't say that word") for user in self.users: - user.send("<%s> says: %s" % (from_user.name, message)) + user.send(f"<{from_user.name}> says: {message}") + realm = ChatRealm() realm.server = ChatServer() diff -Nru twisted-20.3.0/docs/core/howto/listings/pb/copy2_classes.py twisted-22.1.0/docs/core/howto/listings/pb/copy2_classes.py --- twisted-20.3.0/docs/core/howto/listings/pb/copy2_classes.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/pb/copy2_classes.py 2022-02-07 13:12:15.000000000 +0000 @@ -5,25 +5,31 @@ from twisted.spread import pb + class FrogPond: def __init__(self, numFrogs, numToads): self.numFrogs = numFrogs self.numToads = numToads + def count(self): return self.numFrogs + self.numToads + class SenderPond(FrogPond, pb.Copyable): def getStateToCopy(self): d = self.__dict__.copy() - d['frogsAndToads'] = d['numFrogs'] + d['numToads'] - del d['numFrogs'] - del d['numToads'] + d["frogsAndToads"] = d["numFrogs"] + d["numToads"] + del d["numFrogs"] + del d["numToads"] return d + class ReceiverPond(pb.RemoteCopy): def setCopyableState(self, state): self.__dict__ = state + def count(self): return self.frogsAndToads + pb.setUnjellyableForClass(SenderPond, ReceiverPond) diff -Nru twisted-20.3.0/docs/core/howto/listings/pb/copy2_receiver.py twisted-22.1.0/docs/core/howto/listings/pb/copy2_receiver.py --- twisted-20.3.0/docs/core/howto/listings/pb/copy2_receiver.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/pb/copy2_receiver.py 2022-02-07 13:12:15.000000000 +0000 @@ -3,21 +3,25 @@ # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. -from __future__ import print_function -from twisted.application import service, internet +import copy2_classes # needed to get ReceiverPond registered with Jelly + +from twisted.application import internet, service from twisted.internet import reactor from twisted.spread import pb -import copy2_classes # needed to get ReceiverPond registered with Jelly + class Receiver(pb.Root): def remote_takePond(self, pond): print(" got pond:", pond) print(" count %d" % pond.count()) - return "safe and sound" # positive acknowledgement + return "safe and sound" # positive acknowledgement + def remote_shutdown(self): reactor.stop() + application = service.Application("copy_receiver") internet.TCPServer(8800, pb.PBServerFactory(Receiver())).setServiceParent( - service.IServiceCollection(application)) + service.IServiceCollection(application) +) diff -Nru twisted-20.3.0/docs/core/howto/listings/pb/copy2_sender.py twisted-22.1.0/docs/core/howto/listings/pb/copy2_sender.py --- twisted-20.3.0/docs/core/howto/listings/pb/copy2_sender.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/pb/copy2_sender.py 2022-02-07 13:12:15.000000000 +0000 @@ -3,13 +3,14 @@ # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. -from __future__ import print_function -from twisted.spread import pb, jelly -from twisted.python import log -from twisted.internet import reactor from copy2_classes import SenderPond +from twisted.internet import reactor +from twisted.python import log +from twisted.spread import jelly, pb + + class Sender: def __init__(self, pond): self.pond = pond @@ -21,6 +22,7 @@ def ok(self, response): print("pond arrived", response) reactor.stop() + def notOk(self, failure): print("error during takePond:") if failure.type == jelly.InsecureJelly: @@ -30,6 +32,7 @@ reactor.stop() return None + def main(): pond = SenderPond(3, 4) print("count %d" % pond.count()) @@ -41,6 +44,6 @@ deferred.addCallback(sender.got_obj) reactor.run() -if __name__ == '__main__': - main() +if __name__ == "__main__": + main() diff -Nru twisted-20.3.0/docs/core/howto/listings/pb/copy_receiver.tac twisted-22.1.0/docs/core/howto/listings/pb/copy_receiver.tac --- twisted-20.3.0/docs/core/howto/listings/pb/copy_receiver.tac 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/pb/copy_receiver.tac 2022-02-07 13:12:15.000000000 +0000 @@ -11,33 +11,41 @@ http://twistedmatrix.com/documents/current/howto/application for details. """ -from __future__ import print_function import sys -if __name__ == '__main__': + +if __name__ == "__main__": print(__doc__) sys.exit(1) -from twisted.application import service, internet +from copy_sender import CopyPond, LilyPond + +from twisted.application import internet, service from twisted.internet import reactor +from twisted.python import log from twisted.spread import pb -from copy_sender import LilyPond, CopyPond -from twisted.python import log -#log.startLogging(sys.stdout) +# log.startLogging(sys.stdout) + class ReceiverPond(pb.RemoteCopy, LilyPond): pass + + pb.setUnjellyableForClass(CopyPond, ReceiverPond) + class Receiver(pb.Root): def remote_takePond(self, pond): print(" got pond:", pond) pond.countFrogs() - return "safe and sound" # positive acknowledgement + return "safe and sound" # positive acknowledgement + def remote_shutdown(self): reactor.stop() + application = service.Application("copy_receiver") internet.TCPServer(8800, pb.PBServerFactory(Receiver())).setServiceParent( - service.IServiceCollection(application)) + service.IServiceCollection(application) +) diff -Nru twisted-20.3.0/docs/core/howto/listings/pb/copy_sender.py twisted-22.1.0/docs/core/howto/listings/pb/copy_sender.py --- twisted-20.3.0/docs/core/howto/listings/pb/copy_sender.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/pb/copy_sender.py 2022-02-07 13:12:15.000000000 +0000 @@ -3,22 +3,25 @@ # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. -from __future__ import print_function -from twisted.spread import pb, jelly -from twisted.python import log from twisted.internet import reactor +from twisted.python import log +from twisted.spread import jelly, pb + class LilyPond: def setStuff(self, color, numFrogs): self.color = color self.numFrogs = numFrogs + def countFrogs(self): print("%d frogs" % self.numFrogs) + class CopyPond(LilyPond, pb.Copyable): pass + class Sender: def __init__(self, pond): self.pond = pond @@ -31,6 +34,7 @@ def ok(self, response): print("pond arrived", response) reactor.stop() + def notOk(self, failure): print("error during takePond:") if failure.type == jelly.InsecureJelly: @@ -40,8 +44,10 @@ reactor.stop() return None + def main(): from copy_sender import CopyPond # so it's not __main__.CopyPond + pond = CopyPond() pond.setStuff("green", 7) pond.countFrogs() @@ -55,5 +61,6 @@ deferred.addCallback(sender.got_obj) reactor.run() -if __name__ == '__main__': + +if __name__ == "__main__": main() diff -Nru twisted-20.3.0/docs/core/howto/listings/pb/exc_client.py twisted-22.1.0/docs/core/howto/listings/pb/exc_client.py --- twisted-20.3.0/docs/core/howto/listings/pb/exc_client.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/pb/exc_client.py 2022-02-07 13:12:15.000000000 +0000 @@ -3,10 +3,10 @@ # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. -from __future__ import print_function -from twisted.spread import pb from twisted.internet import reactor +from twisted.spread import pb + def main(): factory = pb.PBClientFactory() @@ -15,15 +15,18 @@ d.addCallbacks(got_obj) reactor.run() + def got_obj(obj): # change "broken" into "broken2" to demonstrate an unhandled exception d2 = obj.callRemote("broken") d2.addCallback(working) d2.addErrback(broken) + def working(): print("erm, it wasn't *supposed* to work..") - + + def broken(reason): print("got remote Exception") # reason should be a Failure (or subclass) holding the MyError exception @@ -32,4 +35,5 @@ print(" .type =", reason.type) reactor.stop() + main() diff -Nru twisted-20.3.0/docs/core/howto/listings/pb/exc_server.py twisted-22.1.0/docs/core/howto/listings/pb/exc_server.py --- twisted-20.3.0/docs/core/howto/listings/pb/exc_server.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/pb/exc_server.py 2022-02-07 13:12:15.000000000 +0000 @@ -3,32 +3,39 @@ # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. -from __future__ import print_function -from twisted.spread import pb from twisted.internet import reactor +from twisted.spread import pb + class MyError(pb.Error): """This is an Expected Exception. Something bad happened.""" + pass + class MyError2(Exception): """This is an Unexpected Exception. Something really bad happened.""" + pass + class One(pb.Root): def remote_broken(self): msg = "fall down go boom" print("raising a MyError exception with data '%s'" % msg) raise MyError(msg) + def remote_broken2(self): msg = "hadda owie" print("raising a MyError2 exception with data '%s'" % msg) raise MyError2(msg) + def main(): reactor.listenTCP(8800, pb.PBServerFactory(One())) reactor.run() -if __name__ == '__main__': + +if __name__ == "__main__": main() diff -Nru twisted-20.3.0/docs/core/howto/listings/pb/pb1client.py twisted-22.1.0/docs/core/howto/listings/pb/pb1client.py --- twisted-20.3.0/docs/core/howto/listings/pb/pb1client.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/pb/pb1client.py 2022-02-07 13:12:15.000000000 +0000 @@ -3,10 +3,10 @@ # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. -from __future__ import print_function -from twisted.spread import pb from twisted.internet import reactor +from twisted.spread import pb + def main(): factory = pb.PBClientFactory() @@ -15,19 +15,23 @@ def1.addCallbacks(got_obj1, err_obj1) reactor.run() + def err_obj1(reason): print("error getting first object", reason) reactor.stop() + def got_obj1(obj1): print("got first object:", obj1) print("asking it to getTwo") def2 = obj1.callRemote("getTwo") def2.addCallbacks(got_obj2) + def got_obj2(obj2): print("got second object:", obj2) print("telling it to do three(12)") obj2.callRemote("three", 12) + main() diff -Nru twisted-20.3.0/docs/core/howto/listings/pb/pb1server.py twisted-22.1.0/docs/core/howto/listings/pb/pb1server.py --- twisted-20.3.0/docs/core/howto/listings/pb/pb1server.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/pb/pb1server.py 2022-02-07 13:12:15.000000000 +0000 @@ -3,20 +3,23 @@ # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. -from __future__ import print_function from twisted.spread import pb + class Two(pb.Referenceable): def remote_three(self, arg): print("Two.three was given", arg) - + + class One(pb.Root): def remote_getTwo(self): two = Two() print("returning a Two called", two) return two + from twisted.internet import reactor + reactor.listenTCP(8800, pb.PBServerFactory(One())) reactor.run() diff -Nru twisted-20.3.0/docs/core/howto/listings/pb/pb2client.py twisted-22.1.0/docs/core/howto/listings/pb/pb2client.py --- twisted-20.3.0/docs/core/howto/listings/pb/pb2client.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/pb/pb2client.py 2022-02-07 13:12:15.000000000 +0000 @@ -3,10 +3,10 @@ # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. -from __future__ import print_function -from twisted.spread import pb from twisted.internet import reactor +from twisted.spread import pb + def main(): foo = Foo() @@ -15,10 +15,12 @@ factory.getRootObject().addCallback(foo.step1) reactor.run() + # keeping globals around is starting to get ugly, so we use a simple class # instead. Instead of hooking one function to the next, we hook one method # to the next. + class Foo: def __init__(self): self.oneRef = None @@ -35,4 +37,5 @@ print("one is", self.oneRef) self.oneRef.callRemote("checkTwo", two) + main() diff -Nru twisted-20.3.0/docs/core/howto/listings/pb/pb2server.py twisted-22.1.0/docs/core/howto/listings/pb/pb2server.py --- twisted-20.3.0/docs/core/howto/listings/pb/pb2server.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/pb/pb2server.py 2022-02-07 13:12:15.000000000 +0000 @@ -3,28 +3,31 @@ # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. -from __future__ import print_function -from twisted.spread import pb from twisted.internet import reactor +from twisted.spread import pb + class Two(pb.Referenceable): def remote_print(self, arg): print("two.print was given", arg) - + + class One(pb.Root): def __init__(self, two): - #pb.Root.__init__(self) # pb.Root doesn't implement __init__ + # pb.Root.__init__(self) # pb.Root doesn't implement __init__ self.two = two + def remote_getTwo(self): print("One.getTwo(), returning my two called", self.two) return self.two + def remote_checkTwo(self, newtwo): print("One.checkTwo(): comparing my two", self.two) print("One.checkTwo(): against your two", newtwo) if self.two == newtwo: print("One.checkTwo(): our twos are the same") - + two = Two() root_obj = One(two) diff -Nru twisted-20.3.0/docs/core/howto/listings/pb/pb3client.py twisted-22.1.0/docs/core/howto/listings/pb/pb3client.py --- twisted-20.3.0/docs/core/howto/listings/pb/pb3client.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/pb/pb3client.py 2022-02-07 13:12:15.000000000 +0000 @@ -3,26 +3,29 @@ # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. -from __future__ import print_function -from twisted.spread import pb from twisted.internet import reactor +from twisted.spread import pb + class Two(pb.Referenceable): def remote_print(self, arg): print("Two.print() called with", arg) + def main(): two = Two() factory = pb.PBClientFactory() reactor.connectTCP("localhost", 8800, factory) def1 = factory.getRootObject() - def1.addCallback(got_obj, two) # hands our 'two' to the callback + def1.addCallback(got_obj, two) # hands our 'two' to the callback reactor.run() + def got_obj(obj, two): print("got One:", obj) print("giving it our two") obj.callRemote("takeTwo", two) + main() diff -Nru twisted-20.3.0/docs/core/howto/listings/pb/pb3server.py twisted-22.1.0/docs/core/howto/listings/pb/pb3server.py --- twisted-20.3.0/docs/core/howto/listings/pb/pb3server.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/pb/pb3server.py 2022-02-07 13:12:15.000000000 +0000 @@ -3,16 +3,17 @@ # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. -from __future__ import print_function -from twisted.spread import pb from twisted.internet import reactor - +from twisted.spread import pb + + class One(pb.Root): def remote_takeTwo(self, two): print("received a Two called", two) print("telling it to print(12)") two.callRemote("print", 12) + reactor.listenTCP(8800, pb.PBServerFactory(One())) reactor.run() diff -Nru twisted-20.3.0/docs/core/howto/listings/pb/pb4client.py twisted-22.1.0/docs/core/howto/listings/pb/pb4client.py --- twisted-20.3.0/docs/core/howto/listings/pb/pb4client.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/pb/pb4client.py 2022-02-07 13:12:15.000000000 +0000 @@ -3,10 +3,10 @@ # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. -from __future__ import print_function -from twisted.spread import pb from twisted.internet import reactor +from twisted.spread import pb + def main(): rootobj_def = pb.getObjectAt("localhost", 8800, 30) @@ -17,35 +17,42 @@ obj3_def.addCallbacks(got_obj3) reactor.run() + def got_rootobj(rootobj): print("got root object:", rootobj) print("telling root object to do foo(A)") rootobj.callRemote("foo", "A") + def got_obj2(obj2): print("got second object:", obj2) print("telling second object to do foo(B)") obj2.callRemote("foo", "B") + def got_obj3(obj3): print("got third object:", obj3) print("telling third object to do foo(C)") obj3.callRemote("foo", "C") + class my_ObjectRetrieval(pb._ObjectRetrieval): def __init__(self, broker, d, objname): pb._ObjectRetrieval.__init__(self, broker, d) self.objname = objname + def connectionMade(self): assert not self.term, "How did this get called?" x = self.broker.remoteForName(self.objname) del self.broker self.term = 1 self.deferred.callback(x) - + + def getSomeObjectAt(host, port, timeout=None, objname="root"): from twisted.internet import defer from twisted.spread.pb import Broker, BrokerClientFactory + d = defer.Deferred() b = Broker(1) bf = BrokerClientFactory(b) @@ -57,4 +64,5 @@ reactor.connectTCP(host, port, bf, timeout) return d + main() diff -Nru twisted-20.3.0/docs/core/howto/listings/pb/pb5client.py twisted-22.1.0/docs/core/howto/listings/pb/pb5client.py --- twisted-20.3.0/docs/core/howto/listings/pb/pb5client.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/pb/pb5client.py 2022-02-07 13:12:15.000000000 +0000 @@ -3,11 +3,11 @@ # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. -from __future__ import print_function -from twisted.spread import pb -from twisted.internet import reactor from twisted.cred import credentials +from twisted.internet import reactor +from twisted.spread import pb + def main(): factory = pb.PBClientFactory() @@ -16,9 +16,11 @@ def1.addCallback(connected) reactor.run() + def connected(perspective): print("got perspective ref:", perspective) print("asking it to foo(12)") perspective.callRemote("foo", 12) + main() diff -Nru twisted-20.3.0/docs/core/howto/listings/pb/pb5server.py twisted-22.1.0/docs/core/howto/listings/pb/pb5server.py --- twisted-20.3.0/docs/core/howto/listings/pb/pb5server.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/pb/pb5server.py 2022-02-07 13:12:15.000000000 +0000 @@ -3,29 +3,31 @@ # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. -from __future__ import print_function from zope.interface import implementer -from twisted.spread import pb from twisted.cred import checkers, portal from twisted.internet import reactor +from twisted.spread import pb + class MyPerspective(pb.Avatar): def __init__(self, name): self.name = name + def perspective_foo(self, arg): - print("I am", self.name, "perspective_foo(",arg,") called on", self) + print("I am", self.name, "perspective_foo(", arg, ") called on", self) + @implementer(portal.IRealm) class MyRealm: def requestAvatar(self, avatarId, mind, *interfaces): if pb.IPerspective not in interfaces: raise NotImplementedError - return pb.IPerspective, MyPerspective(avatarId), lambda:None + return pb.IPerspective, MyPerspective(avatarId), lambda: None + p = portal.Portal(MyRealm()) -p.registerChecker( - checkers.InMemoryUsernamePasswordDatabaseDontUse(user1="pass1")) +p.registerChecker(checkers.InMemoryUsernamePasswordDatabaseDontUse(user1="pass1")) reactor.listenTCP(8800, pb.PBServerFactory(p)) reactor.run() diff -Nru twisted-20.3.0/docs/core/howto/listings/pb/pb6client1.py twisted-22.1.0/docs/core/howto/listings/pb/pb6client1.py --- twisted-20.3.0/docs/core/howto/listings/pb/pb6client1.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/pb/pb6client1.py 2022-02-07 13:12:15.000000000 +0000 @@ -3,11 +3,11 @@ # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. -from __future__ import print_function -from twisted.spread import pb -from twisted.internet import reactor from twisted.cred import credentials +from twisted.internet import reactor +from twisted.spread import pb + def main(): factory = pb.PBClientFactory() @@ -16,9 +16,11 @@ def1.addCallback(connected) reactor.run() + def connected(perspective): print("got perspective1 ref:", perspective) print("asking it to foo(13)") perspective.callRemote("foo", 13) + main() diff -Nru twisted-20.3.0/docs/core/howto/listings/pb/pb6client2.py twisted-22.1.0/docs/core/howto/listings/pb/pb6client2.py --- twisted-20.3.0/docs/core/howto/listings/pb/pb6client2.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/pb/pb6client2.py 2022-02-07 13:12:15.000000000 +0000 @@ -3,14 +3,11 @@ # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. -from __future__ import print_function -from twisted.spread import pb +from twisted.cred import credentials from twisted.internet import reactor - from twisted.spread import pb -from twisted.internet import reactor -from twisted.cred import credentials + def main(): factory = pb.PBClientFactory() @@ -19,9 +16,11 @@ def1.addCallback(connected) reactor.run() + def connected(perspective): print("got perspective2 ref:", perspective) print("asking it to foo(14)") perspective.callRemote("foo", 14) + main() diff -Nru twisted-20.3.0/docs/core/howto/listings/pb/pb6server.py twisted-22.1.0/docs/core/howto/listings/pb/pb6server.py --- twisted-20.3.0/docs/core/howto/listings/pb/pb6server.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/pb/pb6server.py 2022-02-07 13:12:15.000000000 +0000 @@ -3,30 +3,32 @@ # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. -from __future__ import print_function from zope.interface import implementer -from twisted.spread import pb from twisted.cred import checkers, portal from twisted.internet import reactor +from twisted.spread import pb + class MyPerspective(pb.Avatar): def __init__(self, name): self.name = name + def perspective_foo(self, arg): - print("I am", self.name, "perspective_foo(",arg,") called on", self) + print("I am", self.name, "perspective_foo(", arg, ") called on", self) + @implementer(portal.IRealm) class MyRealm: def requestAvatar(self, avatarId, mind, *interfaces): if pb.IPerspective not in interfaces: raise NotImplementedError - return pb.IPerspective, MyPerspective(avatarId), lambda:None + return pb.IPerspective, MyPerspective(avatarId), lambda: None + p = portal.Portal(MyRealm()) -c = checkers.InMemoryUsernamePasswordDatabaseDontUse(user1="pass1", - user2="pass2") +c = checkers.InMemoryUsernamePasswordDatabaseDontUse(user1="pass1", user2="pass2") p.registerChecker(c) reactor.listenTCP(8800, pb.PBServerFactory(p)) reactor.run() diff -Nru twisted-20.3.0/docs/core/howto/listings/pb/pb7client.py twisted-22.1.0/docs/core/howto/listings/pb/pb7client.py --- twisted-20.3.0/docs/core/howto/listings/pb/pb7client.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/pb/pb7client.py 2022-02-07 13:12:15.000000000 +0000 @@ -3,23 +3,24 @@ # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. -from __future__ import print_function -from twisted.spread import pb from twisted.internet import reactor +from twisted.spread import pb + def one(port, user, pw, service, perspective, number): factory = pb.PBClientFactory() reactor.connectTCP("localhost", port, factory) - def1 = factory.getPerspective( - user, pw, service, perspective) + def1 = factory.getPerspective(user, pw, service, perspective) def1.addCallback(connected, number) + def connected(perspective, number): print("got perspective ref:", perspective) print("asking it to foo(%d)" % number) perspective.callRemote("foo", number) + def main(): one(8800, "user1", "pass1", "service1", "perspective1.1", 10) one(8800, "user1", "pass1", "service2", "perspective2.1", 11) @@ -27,5 +28,6 @@ one(8800, "user2", "pass2", "service2", "perspective2.2", 13) one(8801, "user3", "pass3", "service3", "perspective3.3", 14) reactor.run() - + + main() diff -Nru twisted-20.3.0/docs/core/howto/listings/pb/pbAnonClient.py twisted-22.1.0/docs/core/howto/listings/pb/pbAnonClient.py --- twisted-20.3.0/docs/core/howto/listings/pb/pbAnonClient.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/pb/pbAnonClient.py 2022-02-07 13:12:15.000000000 +0000 @@ -8,14 +8,13 @@ either anonymously or with username/password credentials. """ -from __future__ import print_function from sys import stdout -from twisted.python.log import err, startLogging from twisted.cred.credentials import Anonymous, UsernamePassword from twisted.internet import reactor from twisted.internet.defer import gatherResults +from twisted.python.log import err, startLogging from twisted.spread.pb import PBClientFactory @@ -68,5 +67,5 @@ reactor.run() -if __name__ == '__main__': +if __name__ == "__main__": main() diff -Nru twisted-20.3.0/docs/core/howto/listings/pb/pbAnonServer.py twisted-22.1.0/docs/core/howto/listings/pb/pbAnonServer.py --- twisted-20.3.0/docs/core/howto/listings/pb/pbAnonServer.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/pb/pbAnonServer.py 2022-02-07 13:12:15.000000000 +0000 @@ -13,17 +13,19 @@ MyPerspective with the name "Anonymous". """ -from __future__ import print_function from sys import stdout from zope.interface import implementer -from twisted.python.log import startLogging -from twisted.cred.checkers import ANONYMOUS, AllowAnonymousAccess -from twisted.cred.checkers import InMemoryUsernamePasswordDatabaseDontUse +from twisted.cred.checkers import ( + ANONYMOUS, + AllowAnonymousAccess, + InMemoryUsernamePasswordDatabaseDontUse, +) from twisted.cred.portal import IRealm, Portal from twisted.internet import reactor +from twisted.python.log import startLogging from twisted.spread.pb import Avatar, IPerspective, PBServerFactory @@ -39,26 +41,25 @@ collision this introduces between anonoymous users and authenticated users named "Anonymous"). """ + def __init__(self, name): self.name = name - def perspective_foo(self, arg): """ Print a simple message which gives the argument this method was called with and this avatar's name. """ - print("I am %s. perspective_foo(%s) called on %s." % ( - self.name, arg, self)) - + print(f"I am {self.name}. perspective_foo({arg}) called on {self}.") @implementer(IRealm) -class MyRealm(object): +class MyRealm: """ Trivial realm which supports anonymous and named users by creating avatars which are instances of MyPerspective for either. """ + def requestAvatar(self, avatarId, mind, *interfaces): if IPerspective not in interfaces: raise NotImplementedError("MyRealm only handles IPerspective") @@ -67,7 +68,6 @@ return IPerspective, MyPerspective(avatarId), lambda: None - def main(): """ Create a PB server using MyRealm and run it on port 8800. @@ -88,5 +88,5 @@ reactor.run() -if __name__ == '__main__': +if __name__ == "__main__": main() diff -Nru twisted-20.3.0/docs/core/howto/listings/pb/trap_client.py twisted-22.1.0/docs/core/howto/listings/pb/trap_client.py --- twisted-20.3.0/docs/core/howto/listings/pb/trap_client.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/pb/trap_client.py 2022-02-07 13:12:15.000000000 +0000 @@ -3,19 +3,25 @@ # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. -from __future__ import print_function -from twisted.spread import pb, jelly -from twisted.python import log from twisted.internet import reactor +from twisted.python import log +from twisted.spread import jelly, pb + + +class MyException(pb.Error): + pass + + +class MyOtherException(pb.Error): + pass -class MyException(pb.Error): pass -class MyOtherException(pb.Error): pass class ScaryObject: # not safe for serialization pass + def worksLike(obj): # the callback/errback sequence in class One works just like an # asynchronous version of the following: @@ -26,19 +32,22 @@ except jelly.InsecureJelly: print(" InsecureJelly: you tried to send something unsafe to them") except (MyException, MyOtherException): - print(" remote raised a MyException") # or MyOtherException - except: + print(" remote raised a MyException") # or MyOtherException + except BaseException: print(" something else happened") else: print(" method successful, response:", response) + class One: def worked(self, response): print(" method successful, response:", response) + def check_InsecureJelly(self, failure): failure.trap(jelly.InsecureJelly) print(" InsecureJelly: you tried to send something unsafe to them") return None + def check_MyException(self, failure): which = failure.trap(MyException, MyOtherException) if which == MyException: @@ -46,6 +55,7 @@ else: print(" remote raised a MyOtherException") return None + def catch_everythingElse(self, failure): print(" something else happened") log.err(failure) @@ -64,16 +74,20 @@ def callOne(self): self.doCall("callOne: call with safe object", "safe string") + def callTwo(self): self.doCall("callTwo: call with dangerous object", ScaryObject()) + def callThree(self): self.doCall("callThree: call that raises remote exception", "panic!") + def callShutdown(self): print("telling them to shut down") self.remote.callRemote("shutdown") + def callFour(self): self.doCall("callFour: call on stale reference", "dummy") - + def got_obj(self, obj): self.remote = obj reactor.callLater(1, self.callOne) @@ -83,6 +97,7 @@ reactor.callLater(5, self.callFour) reactor.callLater(6, reactor.stop) + factory = pb.PBClientFactory() reactor.connectTCP("localhost", 8800, factory) deferred = factory.getRootObject() diff -Nru twisted-20.3.0/docs/core/howto/listings/pb/trap_server.py twisted-22.1.0/docs/core/howto/listings/pb/trap_server.py --- twisted-20.3.0/docs/core/howto/listings/pb/trap_server.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/pb/trap_server.py 2022-02-07 13:12:15.000000000 +0000 @@ -6,16 +6,20 @@ from twisted.internet import reactor from twisted.spread import pb + class MyException(pb.Error): pass + class One(pb.Root): def remote_fooMethod(self, arg): if arg == "panic!": raise MyException return "response" + def remote_shutdown(self): reactor.stop() + reactor.listenTCP(8800, pb.PBServerFactory(One())) reactor.run() diff -Nru twisted-20.3.0/docs/core/howto/listings/positioning/nmealogger.py twisted-22.1.0/docs/core/howto/listings/positioning/nmealogger.py --- twisted-20.3.0/docs/core/howto/listings/positioning/nmealogger.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/positioning/nmealogger.py 2022-02-07 13:12:15.000000000 +0000 @@ -4,9 +4,9 @@ """ Connects to an NMEA device, logs beacon information and position. """ -from __future__ import print_function import sys + from twisted.internet import reactor, serialport from twisted.positioning import base, nmea from twisted.python import log, usage @@ -14,23 +14,20 @@ class PositioningReceiver(base.BasePositioningReceiver): def positionReceived(self, latitude, longitude): - log.msg("I'm at {} lat, {} lon".format(latitude, longitude)) - + log.msg(f"I'm at {latitude} lat, {longitude} lon") def beaconInformationReceived(self, beaconInformation): template = "{0.seen} beacons seen, {0.used} beacons used" log.msg(template.format(beaconInformation)) - class Options(usage.Options): optParameters = [ - ['baud-rate', 'b', 4800, "Baud rate (default: 4800)"], - ['serial-port', 'p', '/dev/ttyS0', 'Serial Port device'], + ["baud-rate", "b", 4800, "Baud rate (default: 4800)"], + ["serial-port", "p", "/dev/ttyS0", "Serial Port device"], ] - def run(): log.startLogging(sys.stdout) @@ -38,7 +35,7 @@ try: opts.parseOptions() except usage.UsageError as message: - print("{}: {}".format(sys.argv[0], message)) + print(f"{sys.argv[0]}: {message}") return positioningReceiver = PositioningReceiver() @@ -51,6 +48,5 @@ reactor.run() - if __name__ == "__main__": run() diff -Nru twisted-20.3.0/docs/core/howto/listings/process/process.py twisted-22.1.0/docs/core/howto/listings/process/process.py --- twisted-20.3.0/docs/core/howto/listings/process/process.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/process/process.py 2022-02-07 13:12:15.000000000 +0000 @@ -3,46 +3,57 @@ # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. -from __future__ import print_function -from twisted.internet import protocol -from twisted.internet import reactor import re +from twisted.internet import protocol, reactor + + class MyPP(protocol.ProcessProtocol): def __init__(self, verses): self.verses = verses self.data = "" + def connectionMade(self): print("connectionMade!") for i in range(self.verses): - self.transport.write("Aleph-null bottles of beer on the wall,\n" + - "Aleph-null bottles of beer,\n" + - "Take one down and pass it around,\n" + - "Aleph-null bottles of beer on the wall.\n") - self.transport.closeStdin() # tell them we're done + self.transport.write( + "Aleph-null bottles of beer on the wall,\n" + + "Aleph-null bottles of beer,\n" + + "Take one down and pass it around,\n" + + "Aleph-null bottles of beer on the wall.\n" + ) + self.transport.closeStdin() # tell them we're done + def outReceived(self, data): print("outReceived! with %d bytes!" % len(data)) self.data = self.data + data + def errReceived(self, data): print("errReceived! with %d bytes!" % len(data)) + def inConnectionLost(self): print("inConnectionLost! stdin is closed! (we probably did it)") + def outConnectionLost(self): print("outConnectionLost! The child closed their stdout!") # now is the time to examine what they wrote - #print("I saw them write:", self.data) - (dummy, lines, words, chars, file) = re.split(r'\s+', self.data) + # print("I saw them write:", self.data) + (dummy, lines, words, chars, file) = re.split(r"\s+", self.data) print("I saw %s lines" % lines) + def errConnectionLost(self): print("errConnectionLost! The child closed their stderr.") + def processExited(self, reason): print("processExited, status %d" % (reason.value.exitCode,)) + def processEnded(self, reason): print("processEnded, status %d" % (reason.value.exitCode,)) print("quitting") reactor.stop() + pp = MyPP(10) reactor.spawnProcess(pp, "wc", ["wc"], {}) reactor.run() diff -Nru twisted-20.3.0/docs/core/howto/listings/process/quotes.py twisted-22.1.0/docs/core/howto/listings/process/quotes.py --- twisted-20.3.0/docs/core/howto/listings/process/quotes.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/process/quotes.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,10 +1,12 @@ -from twisted.internet import protocol, utils, reactor -from twisted.python import failure from cStringIO import StringIO +from twisted.internet import protocol, reactor, utils +from twisted.python import failure + + class FortuneQuoter(protocol.Protocol): - fortune = '/usr/games/fortune' + fortune = "/usr/games/fortune" def connectionMade(self): output = utils.getProcessOutput(self.fortune) @@ -18,7 +20,7 @@ self.transport.loseConnection() -if __name__ == '__main__': +if __name__ == "__main__": f = protocol.Factory() f.protocol = FortuneQuoter reactor.listenTCP(10999, f) diff -Nru twisted-20.3.0/docs/core/howto/listings/process/trueandfalse.py twisted-22.1.0/docs/core/howto/listings/process/trueandfalse.py --- twisted-20.3.0/docs/core/howto/listings/process/trueandfalse.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/process/trueandfalse.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,16 +1,17 @@ -from __future__ import print_function +from twisted.internet import reactor, utils -from twisted.internet import utils, reactor def printTrueValue(val): print("/bin/true exits with rc=%d" % val) - output = utils.getProcessValue('/bin/false') + output = utils.getProcessValue("/bin/false") output.addCallback(printFalseValue) + def printFalseValue(val): print("/bin/false exits with rc=%d" % val) reactor.stop() -output = utils.getProcessValue('/bin/true') + +output = utils.getProcessValue("/bin/true") output.addCallback(printTrueValue) reactor.run() diff -Nru twisted-20.3.0/docs/core/howto/listings/sendmsg/copy_descriptor.py twisted-22.1.0/docs/core/howto/listings/sendmsg/copy_descriptor.py --- twisted-20.3.0/docs/core/howto/listings/sendmsg/copy_descriptor.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/sendmsg/copy_descriptor.py 2022-02-07 13:12:15.000000000 +0000 @@ -6,13 +6,13 @@ sendmsg. """ -from __future__ import print_function from os import pipe, read, write from socket import SOL_SOCKET, socketpair -from struct import unpack, pack +from struct import pack, unpack + +from twisted.python.sendmsg import SCM_RIGHTS, recvmsg, sendmsg -from twisted.python.sendmsg import SCM_RIGHTS, sendmsg, recvmsg def main(): foo, bar = socketpair() @@ -20,8 +20,7 @@ # Send a copy of the descriptor. Notice that there must be at least one # byte of normal data passed in. - sent = sendmsg( - foo, b"\x00", [(SOL_SOCKET, SCM_RIGHTS, pack("i", reader))]) + sent = sendmsg(foo, b"\x00", [(SOL_SOCKET, SCM_RIGHTS, pack("i", reader))]) # Receive the copy, including that one byte of normal data. data, ancillary, flags = recvmsg(bar, 1024) @@ -32,5 +31,6 @@ print("Read from original (%d): %r" % (reader, read(reader, 6))) print("Read from duplicate (%d): %r" % (duplicate, read(duplicate, 6))) -if __name__ == '__main__': + +if __name__ == "__main__": main() diff -Nru twisted-20.3.0/docs/core/howto/listings/sendmsg/send_replacement.py twisted-22.1.0/docs/core/howto/listings/sendmsg/send_replacement.py --- twisted-20.3.0/docs/core/howto/listings/sendmsg/send_replacement.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/sendmsg/send_replacement.py 2022-02-07 13:12:15.000000000 +0000 @@ -5,11 +5,11 @@ Demonstration of sending bytes over a TCP connection using sendmsg. """ -from __future__ import print_function from socket import socketpair -from twisted.python.sendmsg import sendmsg, recvmsg +from twisted.python.sendmsg import recvmsg, sendmsg + def main(): foo, bar = socketpair() @@ -19,5 +19,6 @@ print("Received", repr(received)) print("Extra stuff, boring in this case", flags, ancillary) -if __name__ == '__main__': + +if __name__ == "__main__": main() diff -Nru twisted-20.3.0/docs/core/howto/listings/servers/chat.py twisted-22.1.0/docs/core/howto/listings/servers/chat.py --- twisted-20.3.0/docs/core/howto/listings/servers/chat.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/servers/chat.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,9 +1,9 @@ +from twisted.internet import reactor from twisted.internet.protocol import Factory from twisted.protocols.basic import LineReceiver -from twisted.internet import reactor -class Chat(LineReceiver): +class Chat(LineReceiver): def __init__(self, users): self.users = users self.name = None @@ -26,22 +26,21 @@ if name in self.users: self.sendLine("Name taken, please choose another.") return - self.sendLine("Welcome, %s!" % (name,)) + self.sendLine(f"Welcome, {name}!") self.name = name self.users[name] = self self.state = "CHAT" def handle_CHAT(self, message): - message = "<%s> %s" % (self.name, message) + message = f"<{self.name}> {message}" for name, protocol in self.users.iteritems(): if protocol != self: protocol.sendLine(message) class ChatFactory(Factory): - def __init__(self): - self.users = {} # maps user names to Chat instances + self.users = {} # maps user names to Chat instances def buildProtocol(self, addr): return Chat(self.users) diff -Nru twisted-20.3.0/docs/core/howto/listings/ssl/check_echo_certificate.py twisted-22.1.0/docs/core/howto/listings/ssl/check_echo_certificate.py --- twisted-20.3.0/docs/core/howto/listings/ssl/check_echo_certificate.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/ssl/check_echo_certificate.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,32 +1,34 @@ -from __future__ import print_function import sys + from twisted.internet import defer, endpoints, protocol, ssl, task with open("../../../examples/server.pem") as f: certificate = ssl.Certificate.loadPEM(f.read()) + def main(reactor, host, port=443): - options = ssl.optionsForClientTLS(host.decode("utf-8"), - trustRoot=certificate) + options = ssl.optionsForClientTLS(host.decode("utf-8"), trustRoot=certificate) port = int(port) done = defer.Deferred() class ShowCertificate(protocol.Protocol): def connectionMade(self): self.transport.write(b"GET / HTTP/1.0\r\n\r\n") + def dataReceived(self, data): certificate = ssl.Certificate(self.transport.getPeerCertificate()) print(certificate) self.transport.loseConnection() + def connectionLost(self, reason): if reason.check(ssl.SSL.Error): print(reason.value) done.callback(None) endpoints.connectProtocol( - endpoints.SSL4ClientEndpoint(reactor, host, port, options), - ShowCertificate() + endpoints.SSL4ClientEndpoint(reactor, host, port, options), ShowCertificate() ) return done + task.react(main, sys.argv[1:]) diff -Nru twisted-20.3.0/docs/core/howto/listings/ssl/check_server_certificate.py twisted-22.1.0/docs/core/howto/listings/ssl/check_server_certificate.py --- twisted-20.3.0/docs/core/howto/listings/ssl/check_server_certificate.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/ssl/check_server_certificate.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,19 +1,22 @@ -from __future__ import print_function import sys -from twisted.internet import defer, endpoints, protocol, ssl, task, error + +from twisted.internet import defer, endpoints, error, protocol, ssl, task + def main(reactor, host, port=443): - options = ssl.optionsForClientTLS(hostname=host.decode('utf-8')) + options = ssl.optionsForClientTLS(hostname=host.decode("utf-8")) port = int(port) class ShowCertificate(protocol.Protocol): def connectionMade(self): self.transport.write(b"GET / HTTP/1.0\r\n\r\n") self.done = defer.Deferred() + def dataReceived(self, data): certificate = ssl.Certificate(self.transport.getPeerCertificate()) print("OK:", certificate) self.transport.abortConnection() + def connectionLost(self, reason): print("Lost.") if not reason.check(error.ConnectionClosed): @@ -21,8 +24,8 @@ self.done.callback(None) return endpoints.connectProtocol( - endpoints.SSL4ClientEndpoint(reactor, host, port, options), - ShowCertificate() + endpoints.SSL4ClientEndpoint(reactor, host, port, options), ShowCertificate() ).addCallback(lambda protocol: protocol.done) + task.react(main, sys.argv[1:]) diff -Nru twisted-20.3.0/docs/core/howto/listings/trial/calculus/base_1.py twisted-22.1.0/docs/core/howto/listings/trial/calculus/base_1.py --- twisted-20.3.0/docs/core/howto/listings/trial/calculus/base_1.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/trial/calculus/base_1.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,8 +1,7 @@ # -*- test-case-name: calculus.test.test_base_1 -*- - -class Calculation(object): +class Calculation: def add(self, a, b): pass diff -Nru twisted-20.3.0/docs/core/howto/listings/trial/calculus/base_2.py twisted-22.1.0/docs/core/howto/listings/trial/calculus/base_2.py --- twisted-20.3.0/docs/core/howto/listings/trial/calculus/base_2.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/trial/calculus/base_2.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,9 +1,7 @@ # -*- test-case-name: calculus.test.test_base_2 -*- -from __future__ import division - -class Calculation(object): +class Calculation: def add(self, a, b): return a + b diff -Nru twisted-20.3.0/docs/core/howto/listings/trial/calculus/base_3.py twisted-22.1.0/docs/core/howto/listings/trial/calculus/base_3.py --- twisted-20.3.0/docs/core/howto/listings/trial/calculus/base_3.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/trial/calculus/base_3.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,20 +1,17 @@ # -*- test-case-name: calculus.test.test_base_3 -*- -from __future__ import division - -class Calculation(object): +class Calculation: def _make_ints(self, *args): try: return [int(arg) for arg in args] except ValueError: - raise TypeError( - "Couldn't coerce arguments to integers: {}".format(*args)) + raise TypeError("Couldn't coerce arguments to integers: {}".format(*args)) def add(self, a, b): a, b = self._make_ints(a, b) return a + b - + def subtract(self, a, b): a, b = self._make_ints(a, b) return a - b diff -Nru twisted-20.3.0/docs/core/howto/listings/trial/calculus/client_1.py twisted-22.1.0/docs/core/howto/listings/trial/calculus/client_1.py --- twisted-20.3.0/docs/core/howto/listings/trial/calculus/client_1.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/trial/calculus/client_1.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,39 +1,32 @@ # -*- test-case-name: calculus.test.test_client_1 -*- -from twisted.protocols import basic from twisted.internet import defer - +from twisted.protocols import basic class RemoteCalculationClient(basic.LineReceiver): def __init__(self): self.results = [] - def lineReceived(self, line): d = self.results.pop(0) d.callback(int(line)) - def _sendOperation(self, op, a, b): d = defer.Deferred() self.results.append(d) - line = u"{} {} {}".format(op, a, b).encode('utf-8') + line = f"{op} {a} {b}".encode() self.sendLine(line) return d - def add(self, a, b): return self._sendOperation("add", a, b) - def subtract(self, a, b): return self._sendOperation("subtract", a, b) - def multiply(self, a, b): return self._sendOperation("multiply", a, b) - def divide(self, a, b): return self._sendOperation("divide", a, b) diff -Nru twisted-20.3.0/docs/core/howto/listings/trial/calculus/client_2.py twisted-22.1.0/docs/core/howto/listings/trial/calculus/client_2.py --- twisted-20.3.0/docs/core/howto/listings/trial/calculus/client_2.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/trial/calculus/client_2.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,15 +1,13 @@ # -*- test-case-name: calculus.test.test_client_2 -*- -from twisted.protocols import basic from twisted.internet import defer, reactor - +from twisted.protocols import basic class ClientTimeoutError(Exception): pass - class RemoteCalculationClient(basic.LineReceiver): callLater = reactor.callLater @@ -18,37 +16,30 @@ def __init__(self): self.results = [] - def lineReceived(self, line): d, callID = self.results.pop(0) callID.cancel() d.callback(int(line)) - def _cancel(self, d): d.errback(ClientTimeoutError()) - def _sendOperation(self, op, a, b): d = defer.Deferred() callID = self.callLater(self.timeOut, self._cancel, d) self.results.append((d, callID)) - line = u"{} {} {}".format(op, a, b).encode('utf-8') + line = f"{op} {a} {b}".encode() self.sendLine(line) return d - def add(self, a, b): return self._sendOperation("add", a, b) - def subtract(self, a, b): return self._sendOperation("subtract", a, b) - def multiply(self, a, b): return self._sendOperation("multiply", a, b) - def divide(self, a, b): return self._sendOperation("divide", a, b) diff -Nru twisted-20.3.0/docs/core/howto/listings/trial/calculus/client_3.py twisted-22.1.0/docs/core/howto/listings/trial/calculus/client_3.py --- twisted-20.3.0/docs/core/howto/listings/trial/calculus/client_3.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/trial/calculus/client_3.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,17 +1,14 @@ # -*- test-case-name: calculus.test.test_client -*- -from twisted.protocols import basic, policies from twisted.internet import defer - +from twisted.protocols import basic, policies class ClientTimeoutError(Exception): pass - class RemoteCalculationClient(basic.LineReceiver, policies.TimeoutMixin): - def __init__(self): self.results = [] self._timeOut = 60 @@ -21,33 +18,27 @@ d = self.results.pop(0) d.callback(int(line)) - def timeoutConnection(self): for d in self.results: d.errback(ClientTimeoutError()) self.transport.loseConnection() - def _sendOperation(self, op, a, b): d = defer.Deferred() self.results.append(d) - line = u"{} {} {}".format(op, a, b).encode('utf-8') + line = f"{op} {a} {b}".encode() self.sendLine(line) self.setTimeout(self._timeOut) return d - def add(self, a, b): return self._sendOperation("add", a, b) - def subtract(self, a, b): return self._sendOperation("subtract", a, b) - def multiply(self, a, b): return self._sendOperation("multiply", a, b) - def divide(self, a, b): return self._sendOperation("divide", a, b) diff -Nru twisted-20.3.0/docs/core/howto/listings/trial/calculus/remote_1.py twisted-22.1.0/docs/core/howto/listings/trial/calculus/remote_1.py --- twisted-20.3.0/docs/core/howto/listings/trial/calculus/remote_1.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/trial/calculus/remote_1.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,47 +1,45 @@ # -*- test-case-name: calculus.test.test_remote_1 -*- -from twisted.protocols import basic -from twisted.internet import protocol from calculus.base_3 import Calculation +from twisted.internet import protocol +from twisted.protocols import basic -class CalculationProxy(object): +class CalculationProxy: def __init__(self): self.calc = Calculation() - for m in ['add', 'subtract', 'multiply', 'divide']: - setattr(self, 'remote_{}'.format(m), getattr(self.calc, m)) - + for m in ["add", "subtract", "multiply", "divide"]: + setattr(self, f"remote_{m}", getattr(self.calc, m)) class RemoteCalculationProtocol(basic.LineReceiver): def __init__(self): self.proxy = CalculationProxy() - def lineReceived(self, line): - op, a, b = line.decode('utf-8').split() + op, a, b = line.decode("utf-8").split() a = int(a) b = int(b) - op = getattr(self.proxy, 'remote_{}'.format(op)) + op = getattr(self.proxy, f"remote_{op}") result = op(a, b) - self.sendLine(str(result).encode('utf-8')) - + self.sendLine(str(result).encode("utf-8")) class RemoteCalculationFactory(protocol.Factory): protocol = RemoteCalculationProtocol - def main(): + import sys + from twisted.internet import reactor from twisted.python import log - import sys + log.startLogging(sys.stdout) reactor.listenTCP(0, RemoteCalculationFactory()) reactor.run() - + if __name__ == "__main__": main() diff -Nru twisted-20.3.0/docs/core/howto/listings/trial/calculus/remote_2.py twisted-22.1.0/docs/core/howto/listings/trial/calculus/remote_2.py --- twisted-20.3.0/docs/core/howto/listings/trial/calculus/remote_2.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/trial/calculus/remote_2.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,51 +1,54 @@ # -*- test-case-name: calculus.test.test_remote_1 -*- -from twisted.protocols import basic -from twisted.internet import protocol -from twisted.python import log from calculus.base_3 import Calculation +from twisted.internet import protocol +from twisted.protocols import basic +from twisted.python import log -class CalculationProxy(object): +class CalculationProxy: def __init__(self): self.calc = Calculation() - for m in ['add', 'subtract', 'multiply', 'divide']: - setattr(self, 'remote_{}'.format(m), getattr(self.calc, m)) - + for m in ["add", "subtract", "multiply", "divide"]: + setattr(self, f"remote_{m}", getattr(self.calc, m)) class RemoteCalculationProtocol(basic.LineReceiver): def __init__(self): self.proxy = CalculationProxy() - def lineReceived(self, line): - op, a, b = line.decode('utf-8').split() - op = getattr(self.proxy, 'remote_{}'.format(op,)) + op, a, b = line.decode("utf-8").split() + op = getattr( + self.proxy, + "remote_{}".format( + op, + ), + ) try: result = op(a, b) except TypeError: log.err() self.sendLine(b"error") else: - self.sendLine(str(result).encode('utf-8')) - + self.sendLine(str(result).encode("utf-8")) class RemoteCalculationFactory(protocol.Factory): protocol = RemoteCalculationProtocol - def main(): + import sys + from twisted.internet import reactor from twisted.python import log - import sys + log.startLogging(sys.stdout) reactor.listenTCP(0, RemoteCalculationFactory()) reactor.run() - + if __name__ == "__main__": main() diff -Nru twisted-20.3.0/docs/core/howto/listings/trial/calculus/test/test_base_1.py twisted-22.1.0/docs/core/howto/listings/trial/calculus/test/test_base_1.py --- twisted-20.3.0/docs/core/howto/listings/trial/calculus/test/test_base_1.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/trial/calculus/test/test_base_1.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,6 +1,8 @@ from calculus.base_1 import Calculation + from twisted.trial import unittest + class CalculationTestCase(unittest.TestCase): def test_add(self): calc = Calculation() diff -Nru twisted-20.3.0/docs/core/howto/listings/trial/calculus/test/test_base_2b.py twisted-22.1.0/docs/core/howto/listings/trial/calculus/test/test_base_2b.py --- twisted-20.3.0/docs/core/howto/listings/trial/calculus/test/test_base_2b.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/trial/calculus/test/test_base_2b.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,29 +1,24 @@ from calculus.base_2 import Calculation -from twisted.trial import unittest +from twisted.trial import unittest class CalculationTestCase(unittest.TestCase): def setUp(self): self.calc = Calculation() - def _test(self, operation, a, b, expected): result = operation(a, b) self.assertEqual(result, expected) - def test_add(self): self._test(self.calc.add, 3, 8, 11) - def test_subtract(self): self._test(self.calc.subtract, 7, 3, 4) - def test_multiply(self): self._test(self.calc.multiply, 6, 9, 54) - def test_divide(self): self._test(self.calc.divide, 12, 5, 2) diff -Nru twisted-20.3.0/docs/core/howto/listings/trial/calculus/test/test_base_2.py twisted-22.1.0/docs/core/howto/listings/trial/calculus/test/test_base_2.py --- twisted-20.3.0/docs/core/howto/listings/trial/calculus/test/test_base_2.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/trial/calculus/test/test_base_2.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,28 +1,24 @@ from calculus.base_2 import Calculation -from twisted.trial import unittest +from twisted.trial import unittest class CalculationTestCase(unittest.TestCase): - def test_add(self): calc = Calculation() result = calc.add(3, 8) self.assertEqual(result, 11) - def test_subtract(self): calc = Calculation() result = calc.subtract(7, 3) self.assertEqual(result, 4) - def test_multiply(self): calc = Calculation() result = calc.multiply(12, 5) self.assertEqual(result, 60) - def test_divide(self): calc = Calculation() result = calc.divide(12, 5) diff -Nru twisted-20.3.0/docs/core/howto/listings/trial/calculus/test/test_base_3.py twisted-22.1.0/docs/core/howto/listings/trial/calculus/test/test_base_3.py --- twisted-20.3.0/docs/core/howto/listings/trial/calculus/test/test_base_3.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/trial/calculus/test/test_base_3.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,52 +1,42 @@ from calculus.base_3 import Calculation -from twisted.trial import unittest +from twisted.trial import unittest class CalculationTestCase(unittest.TestCase): def setUp(self): self.calc = Calculation() - def _test(self, operation, a, b, expected): result = operation(a, b) self.assertEqual(result, expected) - def _test_error(self, operation): self.assertRaises(TypeError, operation, "foo", 2) self.assertRaises(TypeError, operation, "bar", "egg") self.assertRaises(TypeError, operation, [3], [8, 2]) self.assertRaises(TypeError, operation, {"e": 3}, {"r": "t"}) - def test_add(self): self._test(self.calc.add, 3, 8, 11) - def test_subtract(self): self._test(self.calc.subtract, 7, 3, 4) - def test_multiply(self): self._test(self.calc.multiply, 6, 9, 54) - def test_divide(self): self._test(self.calc.divide, 12, 5, 2) - def test_errorAdd(self): self._test_error(self.calc.add) - def test_errorSubtract(self): self._test_error(self.calc.subtract) - def test_errorMultiply(self): self._test_error(self.calc.multiply) - def test_errorDivide(self): self._test_error(self.calc.divide) diff -Nru twisted-20.3.0/docs/core/howto/listings/trial/calculus/test/test_client_1.py twisted-22.1.0/docs/core/howto/listings/trial/calculus/test/test_client_1.py --- twisted-20.3.0/docs/core/howto/listings/trial/calculus/test/test_client_1.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/trial/calculus/test/test_client_1.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,7 +1,7 @@ from calculus.client_1 import RemoteCalculationClient -from twisted.trial import unittest -from twisted.test import proto_helpers +from twisted.test import proto_helpers +from twisted.trial import unittest class ClientCalculationTestCase(unittest.TestCase): @@ -10,31 +10,26 @@ self.proto = RemoteCalculationClient() self.proto.makeConnection(self.tr) - def _test(self, operation, a, b, expected): d = getattr(self.proto, operation)(a, b) - self.assertEqual( - self.tr.value(), - u'{} {} {}\r\n'.format(operation, a, b).encode('utf-8') - ) + self.assertEqual(self.tr.value(), f"{operation} {a} {b}\r\n".encode()) self.tr.clear() d.addCallback(self.assertEqual, expected) - self.proto.dataReceived(u"{}\r\n".format(expected,).encode('utf-8')) + self.proto.dataReceived( + "{}\r\n".format( + expected, + ).encode("utf-8") + ) return d - def test_add(self): - return self._test('add', 7, 6, 13) - + return self._test("add", 7, 6, 13) def test_subtract(self): - return self._test('subtract', 82, 78, 4) - + return self._test("subtract", 82, 78, 4) def test_multiply(self): - return self._test('multiply', 2, 8, 16) - + return self._test("multiply", 2, 8, 16) def test_divide(self): - return self._test('divide', 14, 3, 4) - + return self._test("divide", 14, 3, 4) diff -Nru twisted-20.3.0/docs/core/howto/listings/trial/calculus/test/test_client_2.py twisted-22.1.0/docs/core/howto/listings/trial/calculus/test/test_client_2.py --- twisted-20.3.0/docs/core/howto/listings/trial/calculus/test/test_client_2.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/trial/calculus/test/test_client_2.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,9 +1,8 @@ -from calculus.client_2 import RemoteCalculationClient, ClientTimeoutError +from calculus.client_2 import ClientTimeoutError, RemoteCalculationClient from twisted.internet import task -from twisted.trial import unittest from twisted.test import proto_helpers - +from twisted.trial import unittest class ClientCalculationTestCase(unittest.TestCase): @@ -15,37 +14,28 @@ self.proto.callLater = self.clock.callLater self.proto.makeConnection(self.tr) - def _test(self, operation, a, b, expected): d = getattr(self.proto, operation)(a, b) - self.assertEqual( - self.tr.value(), - u'{} {} {}\r\n'.format(operation, a, b).encode('utf-8') - ) + self.assertEqual(self.tr.value(), f"{operation} {a} {b}\r\n".encode()) self.tr.clear() d.addCallback(self.assertEqual, expected) - self.proto.dataReceived(u"{}\r\n".format(expected).encode('utf-8')) + self.proto.dataReceived(f"{expected}\r\n".encode()) return d - def test_add(self): - return self._test('add', 7, 6, 13) - + return self._test("add", 7, 6, 13) def test_subtract(self): - return self._test('subtract', 82, 78, 4) - + return self._test("subtract", 82, 78, 4) def test_multiply(self): - return self._test('multiply', 2, 8, 16) - + return self._test("multiply", 2, 8, 16) def test_divide(self): - return self._test('divide', 14, 3, 4) - + return self._test("divide", 14, 3, 4) def test_timeout(self): d = self.proto.add(9, 4) - self.assertEqual(self.tr.value(), b'add 9 4\r\n') + self.assertEqual(self.tr.value(), b"add 9 4\r\n") self.clock.advance(self.proto.timeOut) return self.assertFailure(d, ClientTimeoutError) diff -Nru twisted-20.3.0/docs/core/howto/listings/trial/calculus/test/test_client_3.py twisted-22.1.0/docs/core/howto/listings/trial/calculus/test/test_client_3.py --- twisted-20.3.0/docs/core/howto/listings/trial/calculus/test/test_client_3.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/trial/calculus/test/test_client_3.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,9 +1,8 @@ -from calculus.client_3 import RemoteCalculationClient, ClientTimeoutError +from calculus.client_3 import ClientTimeoutError, RemoteCalculationClient from twisted.internet import task -from twisted.trial import unittest from twisted.test import proto_helpers - +from twisted.trial import unittest class ClientCalculationTestCase(unittest.TestCase): @@ -15,52 +14,45 @@ self.proto.callLater = self.clock.callLater self.proto.makeConnection(self.tr) - def _test(self, operation, a, b, expected): d = getattr(self.proto, operation)(a, b) - self.assertEqual( - self.tr.value(), - u'{} {} {}\r\n'.format(operation, a, b).encode('utf-8') - ) + self.assertEqual(self.tr.value(), f"{operation} {a} {b}\r\n".encode()) self.tr.clear() d.addCallback(self.assertEqual, expected) - self.proto.dataReceived(u"{}\r\n".format(expected).encode('utf-8')) + self.proto.dataReceived(f"{expected}\r\n".encode()) return d - def test_add(self): - return self._test('add', 7, 6, 13) - + return self._test("add", 7, 6, 13) def test_subtract(self): - return self._test('subtract', 82, 78, 4) - + return self._test("subtract", 82, 78, 4) def test_multiply(self): - return self._test('multiply', 2, 8, 16) - + return self._test("multiply", 2, 8, 16) def test_divide(self): - return self._test('divide', 14, 3, 4) - + return self._test("divide", 14, 3, 4) def test_timeout(self): d = self.proto.add(9, 4) - self.assertEqual(self.tr.value(), b'add 9 4\r\n') + self.assertEqual(self.tr.value(), b"add 9 4\r\n") self.clock.advance(self.proto.timeOut) return self.assertFailure(d, ClientTimeoutError) - def test_timeoutConnectionLost(self): called = [] + def lost(arg): called.append(True) + self.proto.connectionLost = lost d = self.proto.add(9, 4) - self.assertEqual(self.tr.value(), b'add 9 4\r\n') + self.assertEqual(self.tr.value(), b"add 9 4\r\n") self.clock.advance(self.proto.timeOut) def check(ignore): self.assertEqual(called, [True]) + return self.assertFailure(d, ClientTimeoutError).addCallback(check) diff -Nru twisted-20.3.0/docs/core/howto/listings/trial/calculus/test/test_client_4.py twisted-22.1.0/docs/core/howto/listings/trial/calculus/test/test_client_4.py --- twisted-20.3.0/docs/core/howto/listings/trial/calculus/test/test_client_4.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/trial/calculus/test/test_client_4.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,9 +1,8 @@ -from calculus.client_3 import RemoteCalculationClient, ClientTimeoutError +from calculus.client_3 import ClientTimeoutError, RemoteCalculationClient from twisted.internet import task -from twisted.trial import unittest from twisted.test import proto_helpers - +from twisted.trial import unittest class ClientCalculationTestCase(unittest.TestCase): @@ -15,49 +14,45 @@ self.proto.callLater = self.clock.callLater self.proto.makeConnection(self.tr) - def _test(self, operation, a, b, expected): d = getattr(self.proto, operation)(a, b) - self.assertEqual(self.tr.value(), u'{} {} {}\r\n'.format(operation, a, b).encode('utf-8')) + self.assertEqual(self.tr.value(), f"{operation} {a} {b}\r\n".encode()) self.tr.clear() - self.proto.dataReceived(u"{}\r\n".format(expected).encode('utf-8')) + self.proto.dataReceived(f"{expected}\r\n".encode()) self.assertEqual(expected, self.successResultOf(d)) - def test_add(self): - self._test('add', 7, 6, 13) - + self._test("add", 7, 6, 13) def test_subtract(self): - self._test('subtract', 82, 78, 4) - + self._test("subtract", 82, 78, 4) def test_multiply(self): - self._test('multiply', 2, 8, 16) - + self._test("multiply", 2, 8, 16) def test_divide(self): - self._test('divide', 14, 3, 4) - + self._test("divide", 14, 3, 4) def test_timeout(self): d = self.proto.add(9, 4) - self.assertEqual(self.tr.value(), b'add 9 4\r\n') + self.assertEqual(self.tr.value(), b"add 9 4\r\n") self.clock.advance(self.proto.timeOut) self.failureResultOf(d).trap(ClientTimeoutError) - def test_timeoutConnectionLost(self): called = [] + def lost(arg): called.append(True) + self.proto.connectionLost = lost d = self.proto.add(9, 4) - self.assertEqual(self.tr.value(), b'add 9 4\r\n') + self.assertEqual(self.tr.value(), b"add 9 4\r\n") self.clock.advance(self.proto.timeOut) def check(ignore): self.assertEqual(called, [True]) + self.failureResultOf(d).trap(ClientTimeoutError) self.assertEqual(called, [True]) diff -Nru twisted-20.3.0/docs/core/howto/listings/trial/calculus/test/test_remote_1.py twisted-22.1.0/docs/core/howto/listings/trial/calculus/test/test_remote_1.py --- twisted-20.3.0/docs/core/howto/listings/trial/calculus/test/test_remote_1.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/trial/calculus/test/test_remote_1.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,34 +1,28 @@ from calculus.remote_1 import RemoteCalculationFactory -from twisted.trial import unittest -from twisted.test import proto_helpers +from twisted.test import proto_helpers +from twisted.trial import unittest class RemoteCalculationTestCase(unittest.TestCase): def setUp(self): factory = RemoteCalculationFactory() - self.proto = factory.buildProtocol(('127.0.0.1', 0)) + self.proto = factory.buildProtocol(("127.0.0.1", 0)) self.tr = proto_helpers.StringTransport() self.proto.makeConnection(self.tr) - def _test(self, operation, a, b, expected): - self.proto.dataReceived(u'{} {} {}\r\n'.format(operation, a, b).encode('utf-8')) + self.proto.dataReceived(f"{operation} {a} {b}\r\n".encode()) self.assertEqual(int(self.tr.value()), expected) - def test_add(self): - return self._test('add', 7, 6, 13) - + return self._test("add", 7, 6, 13) def test_subtract(self): - return self._test('subtract', 82, 78, 4) - + return self._test("subtract", 82, 78, 4) def test_multiply(self): - return self._test('multiply', 2, 8, 16) - + return self._test("multiply", 2, 8, 16) def test_divide(self): - return self._test('divide', 14, 3, 4) - + return self._test("divide", 14, 3, 4) diff -Nru twisted-20.3.0/docs/core/howto/listings/trial/calculus/test/test_remote_2.py twisted-22.1.0/docs/core/howto/listings/trial/calculus/test/test_remote_2.py --- twisted-20.3.0/docs/core/howto/listings/trial/calculus/test/test_remote_2.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/trial/calculus/test/test_remote_2.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,46 +1,40 @@ -from calculus.remote_1 import RemoteCalculationFactory from calculus.client_2 import RemoteCalculationClient +from calculus.remote_1 import RemoteCalculationFactory +from twisted.internet import protocol, reactor from twisted.trial import unittest -from twisted.internet import reactor, protocol - class RemoteRunCalculationTestCase(unittest.TestCase): - def setUp(self): factory = RemoteCalculationFactory() self.port = reactor.listenTCP(0, factory, interface="127.0.0.1") self.client = None - def tearDown(self): if self.client is not None: self.client.transport.loseConnection() return self.port.stopListening() - def _test(self, op, a, b, expected): creator = protocol.ClientCreator(reactor, RemoteCalculationClient) + def cb(client): self.client = client - return getattr(self.client, op)(a, b - ).addCallback(self.assertEqual, expected) - return creator.connectTCP('127.0.0.1', self.port.getHost().port - ).addCallback(cb) + return getattr(self.client, op)(a, b).addCallback( + self.assertEqual, expected + ) + return creator.connectTCP("127.0.0.1", self.port.getHost().port).addCallback(cb) def test_add(self): return self._test("add", 5, 9, 14) - def test_subtract(self): return self._test("subtract", 47, 13, 34) - def test_multiply(self): return self._test("multiply", 7, 3, 21) - def test_divide(self): return self._test("divide", 84, 10, 8) diff -Nru twisted-20.3.0/docs/core/howto/listings/trial/calculus/test/test_remote_3.py twisted-22.1.0/docs/core/howto/listings/trial/calculus/test/test_remote_3.py --- twisted-20.3.0/docs/core/howto/listings/trial/calculus/test/test_remote_3.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/trial/calculus/test/test_remote_3.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,42 +1,34 @@ from calculus.remote_2 import RemoteCalculationFactory -from twisted.trial import unittest -from twisted.test import proto_helpers +from twisted.test import proto_helpers +from twisted.trial import unittest class RemoteCalculationTestCase(unittest.TestCase): def setUp(self): factory = RemoteCalculationFactory() - self.proto = factory.buildProtocol(('127.0.0.1', 0)) + self.proto = factory.buildProtocol(("127.0.0.1", 0)) self.tr = proto_helpers.StringTransport() self.proto.makeConnection(self.tr) - def _test(self, operation, a, b, expected): - self.proto.dataReceived( - u'{} {} {}\r\n'.format(operation, a, b).encode('utf-8') - ) + self.proto.dataReceived(f"{operation} {a} {b}\r\n".encode()) self.assertEqual(int(self.tr.value()), expected) - def test_add(self): - return self._test('add', 7, 6, 13) - + return self._test("add", 7, 6, 13) def test_subtract(self): - return self._test('subtract', 82, 78, 4) - + return self._test("subtract", 82, 78, 4) def test_multiply(self): - return self._test('multiply', 2, 8, 16) - + return self._test("multiply", 2, 8, 16) def test_divide(self): - return self._test('divide', 14, 3, 4) - + return self._test("divide", 14, 3, 4) def test_invalidParameters(self): - self.proto.dataReceived(b'add foo bar\r\n') + self.proto.dataReceived(b"add foo bar\r\n") self.assertEqual(self.tr.value(), b"error\r\n") errors = self.flushLoggedErrors(TypeError) self.assertEqual(len(errors), 1) diff -Nru twisted-20.3.0/docs/core/howto/listings/TwistedQuotes/pbquoteclient.py twisted-22.1.0/docs/core/howto/listings/TwistedQuotes/pbquoteclient.py --- twisted-20.3.0/docs/core/howto/listings/TwistedQuotes/pbquoteclient.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/TwistedQuotes/pbquoteclient.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,32 +1,36 @@ - from sys import stdout + from twisted.python import log + log.discardLogs() from twisted.internet import reactor from twisted.spread import pb + def connected(root): - root.callRemote('nextQuote').addCallbacks(success, failure) + root.callRemote("nextQuote").addCallbacks(success, failure) + def success(quote): stdout.write(quote + "\n") reactor.stop() + def failure(error): stdout.write("Failed to obtain quote.\n") reactor.stop() + factory = pb.PBClientFactory() reactor.connectTCP( - "localhost", # host name - pb.portno, # port number - factory, # factory - ) - - + "localhost", # host name + pb.portno, # port number + factory, # factory +) -factory.getRootObject().addCallbacks(connected, # when we get the root - failure) # when we can't -reactor.run() # start the main loop +factory.getRootObject().addCallbacks( + connected, failure # when we get the root +) # when we can't +reactor.run() # start the main loop diff -Nru twisted-20.3.0/docs/core/howto/listings/TwistedQuotes/pbquote.py twisted-22.1.0/docs/core/howto/listings/TwistedQuotes/pbquote.py --- twisted-20.3.0/docs/core/howto/listings/TwistedQuotes/pbquote.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/TwistedQuotes/pbquote.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,10 +1,9 @@ from twisted.spread import pb -class QuoteReader(pb.Root): +class QuoteReader(pb.Root): def __init__(self, quoter): self.quoter = quoter def remote_nextQuote(self): return self.quoter.getQuote() - diff -Nru twisted-20.3.0/docs/core/howto/listings/TwistedQuotes/quoteproto.py twisted-22.1.0/docs/core/howto/listings/TwistedQuotes/quoteproto.py --- twisted-20.3.0/docs/core/howto/listings/TwistedQuotes/quoteproto.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/TwistedQuotes/quoteproto.py 2022-02-07 13:12:15.000000000 +0000 @@ -3,25 +3,23 @@ from twisted.internet.protocol import Factory, Protocol - class IQuoter(Interface): """ An object that returns quotes. """ + def getQuote(): """ Return a quote. """ - class QOTD(Protocol): def connectionMade(self): - self.transport.write(self.factory.quoter.getQuote()+'\r\n') + self.transport.write(self.factory.quoter.getQuote() + "\r\n") self.transport.loseConnection() - class QOTDFactory(Factory): """ A factory for the Quote of the Day protocol. @@ -30,6 +28,7 @@ @ivar quoter: An object which provides L{IQuoter} which will be used by the L{QOTD} protocol to get quotes to emit. """ + protocol = QOTD def __init__(self, quoter): diff -Nru twisted-20.3.0/docs/core/howto/listings/TwistedQuotes/quoters.py twisted-22.1.0/docs/core/howto/listings/TwistedQuotes/quoters.py --- twisted-20.3.0/docs/core/howto/listings/TwistedQuotes/quoters.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/TwistedQuotes/quoters.py 2022-02-07 13:12:15.000000000 +0000 @@ -5,31 +5,29 @@ from TwistedQuotes import quoteproto - @implementer(quoteproto.IQuoter) class StaticQuoter: """ Return a static quote. """ + def __init__(self, quote): self.quote = quote - def getQuote(self): return self.quote - @implementer(quoteproto.IQuoter) class FortuneQuoter: """ Load quotes from a fortune-format file. """ + def __init__(self, filenames): self.filenames = filenames - def getQuote(self): with open(choice(self.filenames)) as quoteFile: - quotes = quoteFile.read().split('\n%\n') + quotes = quoteFile.read().split("\n%\n") return choice(quotes) diff -Nru twisted-20.3.0/docs/core/howto/listings/TwistedQuotes/quotetap2.py twisted-22.1.0/docs/core/howto/listings/TwistedQuotes/quotetap2.py --- twisted-20.3.0/docs/core/howto/listings/TwistedQuotes/quotetap2.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/TwistedQuotes/quotetap2.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,34 +1,39 @@ -from TwistedQuotes import quoteproto # Protocol and Factory -from TwistedQuotes import quoters # "give me a quote" code -from TwistedQuotes import pbquote # perspective broker binding - -from twisted.application import service, internet -from twisted.python import usage # twisted command-line processing -from twisted.spread import pb # Perspective Broker +from TwistedQuotes import pbquote # perspective broker binding +from TwistedQuotes import quoteproto # Protocol and Factory +from TwistedQuotes import quoters # "give me a quote" code + +from twisted.application import internet, service +from twisted.python import usage # twisted command-line processing +from twisted.spread import pb # Perspective Broker + class Options(usage.Options): - optParameters = [["port", "p", 8007, - "Port number to listen on for QOTD protocol."], - ["static", "s", "An apple a day keeps the doctor away.", - "A static quote to display."], - ["file", "f", None, - "A fortune-format text file to read quotes from."], - ["pb", "b", None, - "Port to listen with PB server"]] + optParameters = [ + ["port", "p", 8007, "Port number to listen on for QOTD protocol."], + [ + "static", + "s", + "An apple a day keeps the doctor away.", + "A static quote to display.", + ], + ["file", "f", None, "A fortune-format text file to read quotes from."], + ["pb", "b", None, "Port to listen with PB server"], + ] + def makeService(config): svc = service.MultiService() - if config["file"]: # If I was given a "file" option... + if config["file"]: # If I was given a "file" option... # Read quotes from a file, selecting a random one each time, - quoter = quoters.FortuneQuoter([config['file']]) - else: # otherwise, + quoter = quoters.FortuneQuoter([config["file"]]) + else: # otherwise, # read a single quote from the command line (or use the default). - quoter = quoters.StaticQuoter(config['static']) - port = int(config["port"]) # TCP port to listen on - factory = quoteproto.QOTDFactory(quoter) # here we create a QOTDFactory + quoter = quoters.StaticQuoter(config["static"]) + port = int(config["port"]) # TCP port to listen on + factory = quoteproto.QOTDFactory(quoter) # here we create a QOTDFactory # Finally, set up our factory, with its custom quoter, to create QOTD # protocol instances when events arrive on the specified port. - pbport = config['pb'] # TCP PB port to listen on + pbport = config["pb"] # TCP PB port to listen on if pbport: pbfact = pb.PBServerFactory(pbquote.QuoteReader(quoter)) svc.addService(internet.TCPServer(int(pbport), pbfact)) diff -Nru twisted-20.3.0/docs/core/howto/listings/TwistedQuotes/quotetap.py twisted-22.1.0/docs/core/howto/listings/TwistedQuotes/quotetap.py --- twisted-20.3.0/docs/core/howto/listings/TwistedQuotes/quotetap.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/TwistedQuotes/quotetap.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,29 +1,33 @@ -from twisted.application import internet # services that run TCP/SSL/etc. -from TwistedQuotes import quoteproto # Protocol and Factory -from TwistedQuotes import quoters # "give me a quote" code +from TwistedQuotes import quoteproto # Protocol and Factory +from TwistedQuotes import quoters # "give me a quote" code -from twisted.python import usage # twisted command-line processing +from twisted.application import internet # services that run TCP/SSL/etc. +from twisted.python import usage # twisted command-line processing class Options(usage.Options): - optParameters = [["port", "p", 8007, - "Port number to listen on for QOTD protocol."], - ["static", "s", "An apple a day keeps the doctor away.", - "A static quote to display."], - ["file", "f", None, - "A fortune-format text file to read quotes from."]] + optParameters = [ + ["port", "p", 8007, "Port number to listen on for QOTD protocol."], + [ + "static", + "s", + "An apple a day keeps the doctor away.", + "A static quote to display.", + ], + ["file", "f", None, "A fortune-format text file to read quotes from."], + ] def makeService(config): """Return a service that will be attached to the application.""" - if config["file"]: # If I was given a "file" option... + if config["file"]: # If I was given a "file" option... # Read quotes from a file, selecting a random one each time, - quoter = quoters.FortuneQuoter([config['file']]) - else: # otherwise, + quoter = quoters.FortuneQuoter([config["file"]]) + else: # otherwise, # read a single quote from the command line (or use the default). - quoter = quoters.StaticQuoter(config['static']) - port = int(config["port"]) # TCP port to listen on - factory = quoteproto.QOTDFactory(quoter) # here we create a QOTDFactory + quoter = quoters.StaticQuoter(config["static"]) + port = int(config["port"]) # TCP port to listen on + factory = quoteproto.QOTDFactory(quoter) # here we create a QOTDFactory # Finally, set up our factory, with its custom quoter, to create QOTD # protocol instances when events arrive on the specified port. return internet.TCPServer(port, factory) diff -Nru twisted-20.3.0/docs/core/howto/listings/udp/adopt_datagram_port.py twisted-22.1.0/docs/core/howto/listings/udp/adopt_datagram_port.py --- twisted-20.3.0/docs/core/howto/listings/udp/adopt_datagram_port.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/udp/adopt_datagram_port.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,14 +1,12 @@ -from __future__ import print_function - import socket -from twisted.internet.protocol import DatagramProtocol from twisted.internet import reactor +from twisted.internet.protocol import DatagramProtocol class Echo(DatagramProtocol): def datagramReceived(self, data, addr): - print("received %r from %s" % (data, addr)) + print(f"received {data!r} from {addr}") self.transport.write(data, addr) @@ -17,11 +15,10 @@ # Make the port non-blocking and start it listening. portSocket.setblocking(False) -portSocket.bind(('127.0.0.1', 9999)) +portSocket.bind(("127.0.0.1", 9999)) # Now pass the port file descriptor to the reactor. -port = reactor.adoptDatagramPort( - portSocket.fileno(), socket.AF_INET, Echo()) +port = reactor.adoptDatagramPort(portSocket.fileno(), socket.AF_INET, Echo()) # The portSocket should be cleaned up by the process that creates it. portSocket.close() diff -Nru twisted-20.3.0/docs/core/howto/listings/udp/basic_example.py twisted-22.1.0/docs/core/howto/listings/udp/basic_example.py --- twisted-20.3.0/docs/core/howto/listings/udp/basic_example.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/udp/basic_example.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,14 +1,12 @@ -from __future__ import print_function - -from twisted.internet.protocol import DatagramProtocol from twisted.internet import reactor +from twisted.internet.protocol import DatagramProtocol class Echo(DatagramProtocol): - def datagramReceived(self, data, addr): - print("received %r from %s" % (data, addr)) + print(f"received {data!r} from {addr}") self.transport.write(data, addr) + reactor.listenUDP(9999, Echo()) reactor.run() diff -Nru twisted-20.3.0/docs/core/howto/listings/udp/connected_udp.py twisted-22.1.0/docs/core/howto/listings/udp/connected_udp.py --- twisted-20.3.0/docs/core/howto/listings/udp/connected_udp.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/udp/connected_udp.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,7 +1,5 @@ -from __future__ import print_function - -from twisted.internet.protocol import DatagramProtocol from twisted.internet import reactor +from twisted.internet.protocol import DatagramProtocol class Helloer(DatagramProtocol): @@ -10,17 +8,18 @@ port = 1234 self.transport.connect(host, port) - print(("now we can only send to host %s port %d" % (host, port))) + print("now we can only send to host %s port %d" % (host, port)) self.transport.write(b"hello") # no need for address def datagramReceived(self, data, addr): - print("received %r from %s" % (data, addr)) + print(f"received {data!r} from {addr}") # Possibly invoked if there is no server listening on the # address to which we are sending. def connectionRefused(self): print("No one listening") + # 0 means any port, we don't care in this case reactor.listenUDP(0, Helloer()) reactor.run() diff -Nru twisted-20.3.0/docs/core/howto/listings/udp/getting_ip.py twisted-22.1.0/docs/core/howto/listings/udp/getting_ip.py --- twisted-20.3.0/docs/core/howto/listings/udp/getting_ip.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/udp/getting_ip.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,5 +1,3 @@ -from __future__ import print_function - from twisted.internet import reactor @@ -7,5 +5,6 @@ print("IP of 'localhost' is", ip) reactor.stop() -reactor.resolve('localhost').addCallback(gotIP) + +reactor.resolve("localhost").addCallback(gotIP) reactor.run() diff -Nru twisted-20.3.0/docs/core/howto/listings/udp/ipv6_listen.py twisted-22.1.0/docs/core/howto/listings/udp/ipv6_listen.py --- twisted-20.3.0/docs/core/howto/listings/udp/ipv6_listen.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/udp/ipv6_listen.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,13 +1,12 @@ -from __future__ import print_function - -from twisted.internet.protocol import DatagramProtocol from twisted.internet import reactor +from twisted.internet.protocol import DatagramProtocol class Echo(DatagramProtocol): def datagramReceived(self, data, addr): - print("received %r from %s" % (data, addr)) + print(f"received {data!r} from {addr}") self.transport.write(data, addr) -reactor.listenUDP(9999, Echo(), interface='::') + +reactor.listenUDP(9999, Echo(), interface="::") reactor.run() diff -Nru twisted-20.3.0/docs/core/howto/listings/udp/MulticastClient.py twisted-22.1.0/docs/core/howto/listings/udp/MulticastClient.py --- twisted-20.3.0/docs/core/howto/listings/udp/MulticastClient.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/udp/MulticastClient.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,20 +1,17 @@ -from __future__ import print_function - -from twisted.internet.protocol import DatagramProtocol from twisted.internet import reactor +from twisted.internet.protocol import DatagramProtocol class MulticastPingClient(DatagramProtocol): - def startProtocol(self): # Join the multicast address, so we can receive replies: self.transport.joinGroup("228.0.0.5") # Send to 228.0.0.5:9999 - all listeners on the multicast address # (including us) will receive this message. - self.transport.write(b'Client: Ping', ("228.0.0.5", 9999)) + self.transport.write(b"Client: Ping", ("228.0.0.5", 9999)) def datagramReceived(self, datagram, address): - print("Datagram %s received from %s" % (repr(datagram), repr(address))) + print(f"Datagram {repr(datagram)} received from {repr(address)}") reactor.listenMulticast(9999, MulticastPingClient(), listenMultiple=True) diff -Nru twisted-20.3.0/docs/core/howto/listings/udp/MulticastServer.py twisted-22.1.0/docs/core/howto/listings/udp/MulticastServer.py --- twisted-20.3.0/docs/core/howto/listings/udp/MulticastServer.py 2019-05-26 05:43:40.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/listings/udp/MulticastServer.py 2022-02-07 13:12:15.000000000 +0000 @@ -1,11 +1,8 @@ -from __future__ import print_function - -from twisted.internet.protocol import DatagramProtocol from twisted.internet import reactor +from twisted.internet.protocol import DatagramProtocol class MulticastPingPong(DatagramProtocol): - def startProtocol(self): """ Called after protocol has started listening. @@ -16,7 +13,7 @@ self.transport.joinGroup("228.0.0.5") def datagramReceived(self, datagram, address): - print("Datagram %s received from %s" % (repr(datagram), repr(address))) + print(f"Datagram {repr(datagram)} received from {repr(address)}") if datagram == b"Client: Ping" or datagram == "Client: Ping": # Rather than replying to the group multicast address, we send the # reply directly (unicast) to the originating port: @@ -25,6 +22,5 @@ # We use listenMultiple=True so that we can run MulticastServer.py and # MulticastClient.py on same machine: -reactor.listenMulticast(9999, MulticastPingPong(), - listenMultiple=True) +reactor.listenMulticast(9999, MulticastPingPong(), listenMultiple=True) reactor.run() diff -Nru twisted-20.3.0/docs/core/howto/logger.rst twisted-22.1.0/docs/core/howto/logger.rst --- twisted-20.3.0/docs/core/howto/logger.rst 2019-06-15 22:29:05.000000000 +0000 +++ twisted-22.1.0/docs/core/howto/logger.rst 2022-02-07 13:12:15.000000000 +0000 @@ -65,8 +65,8 @@ Usage for emitting applications ------------------------------- -The first thing that an application that emits logging events needs to do is to instantiate a :api:`twisted.logger.Logger ` object, which provides the API to emit events. -A :api:`twisted.logger.Logger ` may be created globally for a module: +The first thing that an application that emits logging events needs to do is to instantiate a :py:class:`Logger ` object, which provides the API to emit events. +A :py:class:`Logger ` may be created globally for a module: .. code-block:: python @@ -76,7 +76,7 @@ def handleData(data): log.debug("Got data: {data!r}.", data=data) -A :api:`twisted.logger.Logger ` can also be associated with a class: +A :py:class:`Logger ` can also be associated with a class: .. code-block:: python @@ -104,7 +104,7 @@ Capturing Failures ~~~~~~~~~~~~~~~~~~ -:api:`twisted.logger.Logger ` provides a :api:`twisted.logger.Logger.failure ` method, which allows one to capture a :api:`twisted.python.failure.Failure ` object conveniently: +:py:class:`Logger ` provides a :py:meth:`failure ` method, which allows one to capture a :py:class:`Failure ` object conveniently: .. code-block:: python @@ -113,12 +113,12 @@ try: 1 / 0 - except: + except BaseException: log.failure("Math is hard!") -The emitted event will have the ``"log_failure"`` key set, which is a :api:`twisted.python.failure.Failure ` that captures the exception. +The emitted event will have the ``"log_failure"`` key set, which is a :py:class:`Failure ` that captures the exception. This can be used by my observers to obtain a traceback. -For example, :api:`twisted.logger.FileLogObserver ` will append the traceback to it's output:: +For example, :py:class:`FileLogObserver ` will append the traceback to it's output:: Math is hard! @@ -129,16 +129,16 @@ exceptions.ZeroDivisionError: integer division or modulo by zero Note that this API is meant to capture unexpected and unhandled errors (that is: bugs, which is why tracebacks are preserved). -As such, it defaults to logging at the :api:`twisted.logger.LogLevel.critical ` level. +As such, it defaults to logging at the :py:attr:`critical ` level. It is generally more appropriate to instead use `log.error()` when logging an expected error condition that was appropriately handled by the software. Namespaces ~~~~~~~~~~ -All :api:`twisted.logger.Logger ` s have a namespace, which can be used to categorize events. -Namespaces may be specified by passing in a ``namespace`` argument to :api:`twisted.logger.Logger ` 's initializer, but if none is given, the logger will derive its namespace from the module name of the callable that instantiated it, or, in the case of a class, from the fully qualified name of the class. -A :api:`twisted.logger.Logger ` will add a ``log_namespace`` key to the events it emits. +All :py:class:`Logger ` s have a namespace, which can be used to categorize events. +Namespaces may be specified by passing in a ``namespace`` argument to :py:class:`Logger ` 's initializer, but if none is given, the logger will derive its namespace from the module name of the callable that instantiated it, or, in the case of a class, from the fully qualified name of the class. +A :py:class:`Logger ` will add a ``log_namespace`` key to the events it emits. In the first example above, the namespace would be ``some.module`` , and in the second example, it would be ``some.module.Foo`` . @@ -146,37 +146,37 @@ Log levels ~~~~~~~~~~ -:api:`twisted.logger.Logger ` s provide a number of methods for emitting events. +:py:class:`Logger ` s provide a number of methods for emitting events. These methods all have the same signature, but each will attach a specific ``log_level`` key to events. -Log levels are defined by the :api:`twisted.logger.LogLevel ` constants container. +Log levels are defined by the :py:class:`LogLevel ` constants container. These are: -:api:`twisted.logger.LogLevel.debug ` +:py:attr:`debug ` Debugging events: Information of use to a developer of the software, not generally of interest to someone running the software unless they are attempting to diagnose a software issue. -:api:`twisted.logger.LogLevel.info ` +:py:attr:`info ` Informational events: Routine information about the status of an application, such as incoming connections, startup of a subsystem, etc. -:api:`twisted.logger.LogLevel.warn ` +:py:attr:`warn ` Warning events: Events that may require greater attention than informational events but are not a systemic failure condition, such as authorization failures, bad data from a network client, etc. Such events are of potential interest to system administrators, and should ideally be phrased in such a way, or documented, so as to indicate an action that an administrator might take to mitigate the warning. -:api:`twisted.logger.LogLevel.error ` +:py:attr:`error ` Error conditions: Events indicating a systemic failure. For example, resource exhaustion, or the loss of connectivity to an external system, such as a database or API endpoint, without which no useful work can proceed. Similar to warnings, errors related to operational parameters may be actionable to system administrators and should provide references to resources which an administrator might use to resolve them. -:api:`twisted.logger.LogLevel.critical ` +:py:attr:`critical ` Critical failures: Errors indicating systemic failure (ie. service outage), data corruption, imminent data loss, etc. which must be handled immediately. This includes errors unanticipated by the software, such as unhandled exceptions, wherein the cause and consequences are unknown. -In the first example above, the call to ``log.debug`` will add a ``log_level`` key to the emitted event with a value of :api:`twisted.logger.LogLevel.debug ` . -In the second example, calling ``self.log.error`` would use a value of :api:`twisted.logger.LogLevel.error ` . +In the first example above, the call to ``log.debug`` will add a ``log_level`` key to the emitted event with a value of :py:attr:`LogLevel.debug ` . +In the second example, calling ``self.log.error`` would use a value of :py:attr:`LogLevel.error ` . The above descriptions are simply guidance, but it is worth noting that log levels have a reduced value if they are used inconsistently. If one module in an application considers a message informational, and another module considers a similar message an error, then filtering based on log levels becomes harder. @@ -188,21 +188,21 @@ Emitter method signatures ~~~~~~~~~~~~~~~~~~~~~~~~~ -The emitter methods (:api:`twisted.logger.Logger.debug ` , :api:`twisted.logger.Logger.info ` , :api:`twisted.logger.Logger.warn ` , etc.) all take an optional format string as a first argument, followed by keyword arguments that will be included in the emitted event. +The emitter methods (:py:meth:`debug ` , :py:meth:`info ` , :py:meth:`warn ` , etc.) all take an optional format string as a first argument, followed by keyword arguments that will be included in the emitted event. Note that all three examples in the opening section of this HOWTO fit this signature. The first omits the format, which doesn't lend itself well to text logging. The second omits the keyword arguments, which hostile to anything other than text logging, and is therefore ill-advised. Finally, the third provides both, which is the recommended usage. -These methods are all convenience wrappers around the :api:`twisted.logger.Logger.emit ` method, which takes a :api:`twisted.logger.LogLevel ` as its first argument. +These methods are all convenience wrappers around the :py:meth:`emit ` method, which takes a :py:class:`LogLevel ` as its first argument. Format strings ~~~~~~~~~~~~~~ -Format strings provide observers with a standard way to format an event as text suitable for a human being to read. Formatting is accomplished using the function :api:`twisted.logger.eventAsText `. +Format strings provide observers with a standard way to format an event as text suitable for a human being to read. Formatting is accomplished using the function :py:func:`eventAsText `. When writing a format string, take care to present it in a manner which would make as much sense as possible to a human reader. Particularly, format strings need not be written with an eye towards parseability or machine-readability. If you want to save your log events along with their structure and then analyze them later, see the next section, on :ref:`"saving events for later" ` . @@ -243,18 +243,18 @@ ``log_logger`` - :api:`twisted.logger.Logger ` object that the event was emitted to. + :py:class:`Logger ` object that the event was emitted to. ``log_source`` The source object that emitted the event. - When a :api:`twisted.logger.Logger ` is accessed as an attribute of a class, the class is the source. + When a :py:class:`Logger ` is accessed as an attribute of a class, the class is the source. When accessed as an attribute of an instance, the instance is the source. In other cases, the source is ``None`` . ``log_level`` - The :api:`twisted.logger.LogLevel ` associated with the event. + The :py:class:`LogLevel ` associated with the event. ``log_namespace`` @@ -267,11 +267,11 @@ ``log_time`` - The time that the event was emitted, as returned by :api:`time.time