summaryrefslogtreecommitdiffstats
path: root/contrib/python
diff options
context:
space:
mode:
authorrobot-piglet <[email protected]>2026-06-03 16:47:44 +0300
committerrobot-piglet <[email protected]>2026-06-03 17:44:32 +0300
commitf253db69975b87e0efb99cee1e57f69ff8cccc10 (patch)
tree7430eaeabf337137af1bbe7b6e939dadba3da082 /contrib/python
parent3b137bd05f166eb07dfaa42e6f63dd610597a69b (diff)
Intermediate changes
commit_hash:7321e1c01f99561516cbd12d4eda7442dbe4a679
Diffstat (limited to 'contrib/python')
-rw-r--r--contrib/python/Twisted/py3/.dist-info/METADATA174
-rw-r--r--contrib/python/Twisted/py3/LICENSE2
-rw-r--r--contrib/python/Twisted/py3/README.rst2
-rw-r--r--contrib/python/Twisted/py3/twisted/_threads/_pool.py7
-rw-r--r--contrib/python/Twisted/py3/twisted/_threads/_team.py12
-rw-r--r--contrib/python/Twisted/py3/twisted/_threads/_threadworker.py3
-rw-r--r--contrib/python/Twisted/py3/twisted/_version.py2
-rw-r--r--contrib/python/Twisted/py3/twisted/application/internet.py5
-rw-r--r--contrib/python/Twisted/py3/twisted/application/reactors.py3
-rw-r--r--contrib/python/Twisted/py3/twisted/application/runner/_exit.py7
-rw-r--r--contrib/python/Twisted/py3/twisted/application/runner/_pidfile.py28
-rw-r--r--contrib/python/Twisted/py3/twisted/application/runner/_runner.py3
-rw-r--r--contrib/python/Twisted/py3/twisted/application/strports.py6
-rw-r--r--contrib/python/Twisted/py3/twisted/application/twist/_options.py14
-rw-r--r--contrib/python/Twisted/py3/twisted/application/twist/_twist.py2
-rw-r--r--contrib/python/Twisted/py3/twisted/conch/checkers.py29
-rw-r--r--contrib/python/Twisted/py3/twisted/conch/client/direct.py14
-rw-r--r--contrib/python/Twisted/py3/twisted/conch/client/knownhosts.py3
-rw-r--r--contrib/python/Twisted/py3/twisted/conch/client/options.py5
-rw-r--r--contrib/python/Twisted/py3/twisted/conch/manhole.py3
-rw-r--r--contrib/python/Twisted/py3/twisted/conch/manhole_ssh.py6
-rw-r--r--contrib/python/Twisted/py3/twisted/conch/openssh_compat/factory.py5
-rw-r--r--contrib/python/Twisted/py3/twisted/conch/openssh_compat/primes.py7
-rw-r--r--contrib/python/Twisted/py3/twisted/conch/recvline.py3
-rw-r--r--contrib/python/Twisted/py3/twisted/conch/scripts/cftp.py7
-rw-r--r--contrib/python/Twisted/py3/twisted/conch/scripts/ckeygen.py12
-rw-r--r--contrib/python/Twisted/py3/twisted/conch/scripts/conch.py6
-rw-r--r--contrib/python/Twisted/py3/twisted/conch/scripts/tkconch.py9
-rw-r--r--contrib/python/Twisted/py3/twisted/conch/ssh/common.py3
-rw-r--r--contrib/python/Twisted/py3/twisted/conch/ssh/factory.py26
-rw-r--r--contrib/python/Twisted/py3/twisted/conch/ssh/filetransfer.py3
-rw-r--r--contrib/python/Twisted/py3/twisted/conch/ssh/keys.py116
-rw-r--r--contrib/python/Twisted/py3/twisted/conch/ssh/service.py4
-rw-r--r--contrib/python/Twisted/py3/twisted/conch/ssh/transport.py22
-rw-r--r--contrib/python/Twisted/py3/twisted/conch/ssh/userauth.py61
-rw-r--r--contrib/python/Twisted/py3/twisted/conch/unix.py8
-rw-r--r--contrib/python/Twisted/py3/twisted/copyright.py2
-rw-r--r--contrib/python/Twisted/py3/twisted/cred/checkers.py14
-rw-r--r--contrib/python/Twisted/py3/twisted/cred/credentials.py13
-rw-r--r--contrib/python/Twisted/py3/twisted/cred/portal.py21
-rw-r--r--contrib/python/Twisted/py3/twisted/cred/strcred.py6
-rw-r--r--contrib/python/Twisted/py3/twisted/internet/__init__.py27
-rw-r--r--contrib/python/Twisted/py3/twisted/internet/_glibbase.py25
-rw-r--r--contrib/python/Twisted/py3/twisted/internet/_posixstdio.py2
-rw-r--r--contrib/python/Twisted/py3/twisted/internet/_producer_helpers.py4
-rw-r--r--contrib/python/Twisted/py3/twisted/internet/_resolver.py104
-rw-r--r--contrib/python/Twisted/py3/twisted/internet/_service_identity.py42
-rw-r--r--contrib/python/Twisted/py3/twisted/internet/_signals.py7
-rw-r--r--contrib/python/Twisted/py3/twisted/internet/_sslverify.py653
-rw-r--r--contrib/python/Twisted/py3/twisted/internet/abstract.py10
-rw-r--r--contrib/python/Twisted/py3/twisted/internet/address.py3
-rw-r--r--contrib/python/Twisted/py3/twisted/internet/asyncioreactor.py16
-rw-r--r--contrib/python/Twisted/py3/twisted/internet/base.py99
-rw-r--r--contrib/python/Twisted/py3/twisted/internet/defer.py289
-rw-r--r--contrib/python/Twisted/py3/twisted/internet/endpoints.py149
-rw-r--r--contrib/python/Twisted/py3/twisted/internet/interfaces.py252
-rw-r--r--contrib/python/Twisted/py3/twisted/internet/iocpreactor/reactor.py3
-rw-r--r--contrib/python/Twisted/py3/twisted/internet/iocpreactor/tcp.py8
-rw-r--r--contrib/python/Twisted/py3/twisted/internet/iocpreactor/udp.py4
-rw-r--r--contrib/python/Twisted/py3/twisted/internet/posixbase.py6
-rw-r--r--contrib/python/Twisted/py3/twisted/internet/process.py16
-rw-r--r--contrib/python/Twisted/py3/twisted/internet/protocol.py88
-rw-r--r--contrib/python/Twisted/py3/twisted/internet/reactor.py83
-rw-r--r--contrib/python/Twisted/py3/twisted/internet/selectreactor.py6
-rw-r--r--contrib/python/Twisted/py3/twisted/internet/task.py99
-rw-r--r--contrib/python/Twisted/py3/twisted/internet/tcp.py69
-rw-r--r--contrib/python/Twisted/py3/twisted/internet/testing.py55
-rw-r--r--contrib/python/Twisted/py3/twisted/internet/threads.py23
-rw-r--r--contrib/python/Twisted/py3/twisted/internet/udp.py5
-rw-r--r--contrib/python/Twisted/py3/twisted/internet/unix.py4
-rw-r--r--contrib/python/Twisted/py3/twisted/logger/_buffer.py6
-rw-r--r--contrib/python/Twisted/py3/twisted/logger/_capture.py5
-rw-r--r--contrib/python/Twisted/py3/twisted/logger/_file.py12
-rw-r--r--contrib/python/Twisted/py3/twisted/logger/_filter.py4
-rw-r--r--contrib/python/Twisted/py3/twisted/logger/_flatten.py8
-rw-r--r--contrib/python/Twisted/py3/twisted/logger/_format.py19
-rw-r--r--contrib/python/Twisted/py3/twisted/logger/_global.py15
-rw-r--r--contrib/python/Twisted/py3/twisted/logger/_interfaces.py8
-rw-r--r--contrib/python/Twisted/py3/twisted/logger/_io.py8
-rw-r--r--contrib/python/Twisted/py3/twisted/logger/_json.py15
-rw-r--r--contrib/python/Twisted/py3/twisted/logger/_legacy.py10
-rw-r--r--contrib/python/Twisted/py3/twisted/logger/_logger.py30
-rw-r--r--contrib/python/Twisted/py3/twisted/logger/_observer.py6
-rw-r--r--contrib/python/Twisted/py3/twisted/logger/_stdlib.py11
-rw-r--r--contrib/python/Twisted/py3/twisted/logger/_util.py4
-rw-r--r--contrib/python/Twisted/py3/twisted/mail/_pop3client.py3
-rw-r--r--contrib/python/Twisted/py3/twisted/mail/imap4.py7
-rw-r--r--contrib/python/Twisted/py3/twisted/mail/interfaces.py3
-rw-r--r--contrib/python/Twisted/py3/twisted/mail/pop3.py4
-rw-r--r--contrib/python/Twisted/py3/twisted/mail/pop3client.py3
-rw-r--r--contrib/python/Twisted/py3/twisted/mail/protocols.py2
-rw-r--r--contrib/python/Twisted/py3/twisted/mail/relaymanager.py10
-rw-r--r--contrib/python/Twisted/py3/twisted/mail/smtp.py40
-rw-r--r--contrib/python/Twisted/py3/twisted/names/authority.py3
-rw-r--r--contrib/python/Twisted/py3/twisted/names/dns.py179
-rw-r--r--contrib/python/Twisted/py3/twisted/names/server.py2
-rw-r--r--contrib/python/Twisted/py3/twisted/pair/ip.py10
-rw-r--r--contrib/python/Twisted/py3/twisted/pair/rawudp.py3
-rw-r--r--contrib/python/Twisted/py3/twisted/pair/tuntap.py3
-rw-r--r--contrib/python/Twisted/py3/twisted/persisted/dirdbm.py5
-rw-r--r--contrib/python/Twisted/py3/twisted/persisted/styles.py5
-rw-r--r--contrib/python/Twisted/py3/twisted/plugin.py6
-rw-r--r--contrib/python/Twisted/py3/twisted/plugins/__init__.py4
-rw-r--r--contrib/python/Twisted/py3/twisted/plugins/cred_unix.py13
-rw-r--r--contrib/python/Twisted/py3/twisted/plugins/twisted_core.py2
-rw-r--r--contrib/python/Twisted/py3/twisted/positioning/_sentence.py3
-rw-r--r--contrib/python/Twisted/py3/twisted/positioning/base.py3
-rw-r--r--contrib/python/Twisted/py3/twisted/protocols/_sni.py347
-rw-r--r--contrib/python/Twisted/py3/twisted/protocols/_tls_legacy.py110
-rw-r--r--contrib/python/Twisted/py3/twisted/protocols/amp.py87
-rw-r--r--contrib/python/Twisted/py3/twisted/protocols/ftp.py4
-rw-r--r--contrib/python/Twisted/py3/twisted/protocols/haproxy/_exceptions.py5
-rw-r--r--contrib/python/Twisted/py3/twisted/protocols/haproxy/_interfaces.py4
-rw-r--r--contrib/python/Twisted/py3/twisted/protocols/haproxy/_parser.py4
-rw-r--r--contrib/python/Twisted/py3/twisted/protocols/haproxy/_v1parser.py6
-rw-r--r--contrib/python/Twisted/py3/twisted/protocols/haproxy/_v2parser.py16
-rw-r--r--contrib/python/Twisted/py3/twisted/protocols/haproxy/_wrapper.py6
-rw-r--r--contrib/python/Twisted/py3/twisted/protocols/policies.py25
-rw-r--r--contrib/python/Twisted/py3/twisted/protocols/portforward.py53
-rw-r--r--contrib/python/Twisted/py3/twisted/protocols/postfix.py7
-rw-r--r--contrib/python/Twisted/py3/twisted/protocols/sip.py5
-rw-r--r--contrib/python/Twisted/py3/twisted/protocols/tls.py210
-rw-r--r--contrib/python/Twisted/py3/twisted/python/_release.py3
-rw-r--r--contrib/python/Twisted/py3/twisted/python/_shellcomp.py13
-rw-r--r--contrib/python/Twisted/py3/twisted/python/_textattributes.py11
-rw-r--r--contrib/python/Twisted/py3/twisted/python/_tzhelper.py15
-rw-r--r--contrib/python/Twisted/py3/twisted/python/components.py3
-rw-r--r--contrib/python/Twisted/py3/twisted/python/context.py3
-rw-r--r--contrib/python/Twisted/py3/twisted/python/deprecate.py81
-rw-r--r--contrib/python/Twisted/py3/twisted/python/fakepwd.py12
-rw-r--r--contrib/python/Twisted/py3/twisted/python/filepath.py67
-rw-r--r--contrib/python/Twisted/py3/twisted/python/formmethod.py10
-rw-r--r--contrib/python/Twisted/py3/twisted/python/htmlizer.py5
-rw-r--r--contrib/python/Twisted/py3/twisted/python/log.py11
-rw-r--r--contrib/python/Twisted/py3/twisted/python/logfile.py7
-rw-r--r--contrib/python/Twisted/py3/twisted/python/rebuild.py8
-rw-r--r--contrib/python/Twisted/py3/twisted/python/reflect.py6
-rw-r--r--contrib/python/Twisted/py3/twisted/python/runtime.py11
-rw-r--r--contrib/python/Twisted/py3/twisted/python/sendmsg.py3
-rw-r--r--contrib/python/Twisted/py3/twisted/python/systemd.py17
-rw-r--r--contrib/python/Twisted/py3/twisted/python/threadpool.py12
-rw-r--r--contrib/python/Twisted/py3/twisted/python/usage.py14
-rw-r--r--contrib/python/Twisted/py3/twisted/python/util.py15
-rw-r--r--contrib/python/Twisted/py3/twisted/python/zippath.py43
-rw-r--r--contrib/python/Twisted/py3/twisted/runner/procmon.py21
-rw-r--r--contrib/python/Twisted/py3/twisted/runner/procmontap.py4
-rw-r--r--contrib/python/Twisted/py3/twisted/scripts/_twistd_unix.py4
-rw-r--r--contrib/python/Twisted/py3/twisted/scripts/htmlizer.py2
-rw-r--r--contrib/python/Twisted/py3/twisted/scripts/trial.py19
-rw-r--r--contrib/python/Twisted/py3/twisted/spread/jelly.py6
-rw-r--r--contrib/python/Twisted/py3/twisted/spread/pb.py4
-rw-r--r--contrib/python/Twisted/py3/twisted/tap/portforward.py11
-rw-r--r--contrib/python/Twisted/py3/twisted/trial/_asyncrunner.py8
-rw-r--r--contrib/python/Twisted/py3/twisted/trial/_asynctest.py6
-rw-r--r--contrib/python/Twisted/py3/twisted/trial/_dist/distreporter.py15
-rw-r--r--contrib/python/Twisted/py3/twisted/trial/_dist/disttrial.py41
-rw-r--r--contrib/python/Twisted/py3/twisted/trial/_dist/functional.py6
-rw-r--r--contrib/python/Twisted/py3/twisted/trial/_dist/stream.py7
-rw-r--r--contrib/python/Twisted/py3/twisted/trial/_dist/worker.py22
-rw-r--r--contrib/python/Twisted/py3/twisted/trial/_dist/workerreporter.py23
-rw-r--r--contrib/python/Twisted/py3/twisted/trial/_synctest.py67
-rw-r--r--contrib/python/Twisted/py3/twisted/trial/itrial.py21
-rw-r--r--contrib/python/Twisted/py3/twisted/trial/reporter.py33
-rw-r--r--contrib/python/Twisted/py3/twisted/trial/runner.py33
-rw-r--r--contrib/python/Twisted/py3/twisted/web/_abnf.py5
-rw-r--r--contrib/python/Twisted/py3/twisted/web/_element.py25
-rw-r--r--contrib/python/Twisted/py3/twisted/web/_flatten.py68
-rw-r--r--contrib/python/Twisted/py3/twisted/web/_http2.py17
-rw-r--r--contrib/python/Twisted/py3/twisted/web/_newclient.py4
-rw-r--r--contrib/python/Twisted/py3/twisted/web/_stan.py39
-rw-r--r--contrib/python/Twisted/py3/twisted/web/_template_util.py73
-rw-r--r--contrib/python/Twisted/py3/twisted/web/_websocket_impl.py7
-rw-r--r--contrib/python/Twisted/py3/twisted/web/client.py49
-rw-r--r--contrib/python/Twisted/py3/twisted/web/http.py54
-rw-r--r--contrib/python/Twisted/py3/twisted/web/http_headers.py44
-rw-r--r--contrib/python/Twisted/py3/twisted/web/iweb.py34
-rw-r--r--contrib/python/Twisted/py3/twisted/web/proxy.py2
-rw-r--r--contrib/python/Twisted/py3/twisted/web/resource.py4
-rw-r--r--contrib/python/Twisted/py3/twisted/web/server.py9
-rw-r--r--contrib/python/Twisted/py3/twisted/web/static.py15
-rw-r--r--contrib/python/Twisted/py3/twisted/web/test/requesthelper.py17
-rw-r--r--contrib/python/Twisted/py3/twisted/web/wsgi.py7
-rw-r--r--contrib/python/Twisted/py3/twisted/words/im/basesupport.py4
-rw-r--r--contrib/python/Twisted/py3/twisted/words/protocols/irc.py4
-rw-r--r--contrib/python/Twisted/py3/twisted/words/protocols/jabber/jid.py15
-rw-r--r--contrib/python/Twisted/py3/twisted/words/protocols/jabber/xmlstream.py16
-rw-r--r--contrib/python/Twisted/py3/twisted/words/tap.py5
-rw-r--r--contrib/python/Twisted/py3/ya.make5
188 files changed, 3089 insertions, 2348 deletions
diff --git a/contrib/python/Twisted/py3/.dist-info/METADATA b/contrib/python/Twisted/py3/.dist-info/METADATA
index 9414ada6988..0894155a0bd 100644
--- a/contrib/python/Twisted/py3/.dist-info/METADATA
+++ b/contrib/python/Twisted/py3/.dist-info/METADATA
@@ -1,6 +1,6 @@
Metadata-Version: 2.4
Name: Twisted
-Version: 25.5.0
+Version: 26.4.0
Summary: An asynchronous networking framework written in Python
Project-URL: Changelog, https://github.com/twisted/twisted/blob/HEAD/NEWS.rst
Project-URL: Documentation, https://docs.twisted.org/
@@ -14,13 +14,12 @@ License: MIT License
License-File: LICENSE
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
-Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
-Requires-Python: >=3.8.0
+Requires-Python: >=3.9.12
Requires-Dist: attrs>=22.2.0
Requires-Dist: automat>=24.8.0
Requires-Dist: constantly>=15.1
@@ -30,8 +29,8 @@ Requires-Dist: typing-extensions>=4.2.0
Requires-Dist: zope-interface>=5
Provides-Extra: all-non-platform
Requires-Dist: appdirs>=1.4.0; extra == 'all-non-platform'
-Requires-Dist: bcrypt>=3.1.3; extra == 'all-non-platform'
-Requires-Dist: cryptography>=3.3; extra == 'all-non-platform'
+Requires-Dist: bcrypt>=3.2.1; extra == 'all-non-platform'
+Requires-Dist: cryptography>=38; extra == 'all-non-platform'
Requires-Dist: cython-test-exception-raiser<2,>=1.0.2; extra == 'all-non-platform'
Requires-Dist: h2<5.0,>=3.2; extra == 'all-non-platform'
Requires-Dist: httpx[http2]>=0.27; extra == 'all-non-platform'
@@ -39,37 +38,22 @@ Requires-Dist: hypothesis>=6.56; extra == 'all-non-platform'
Requires-Dist: idna>=2.4; extra == 'all-non-platform'
Requires-Dist: priority<2.0,>=1.1.0; extra == 'all-non-platform'
Requires-Dist: pyhamcrest>=2; extra == 'all-non-platform'
-Requires-Dist: pyopenssl>=21.0.0; extra == 'all-non-platform'
+Requires-Dist: pyopenssl>=25.2.0; extra == 'all-non-platform'
Requires-Dist: pyserial>=3.0; extra == 'all-non-platform'
Requires-Dist: pywin32!=226; (platform_system == 'Windows') and extra == 'all-non-platform'
Requires-Dist: service-identity>=18.1.0; extra == 'all-non-platform'
Requires-Dist: wsproto; extra == 'all-non-platform'
Provides-Extra: all_non_platform
-Requires-Dist: appdirs>=1.4.0; extra == 'all_non_platform'
-Requires-Dist: bcrypt>=3.1.3; extra == 'all_non_platform'
-Requires-Dist: cryptography>=3.3; extra == 'all_non_platform'
-Requires-Dist: cython-test-exception-raiser<2,>=1.0.2; extra == 'all_non_platform'
-Requires-Dist: h2<5.0,>=3.2; extra == 'all_non_platform'
-Requires-Dist: httpx[http2]>=0.27; extra == 'all_non_platform'
-Requires-Dist: hypothesis>=6.56; extra == 'all_non_platform'
-Requires-Dist: idna>=2.4; extra == 'all_non_platform'
-Requires-Dist: priority<2.0,>=1.1.0; extra == 'all_non_platform'
-Requires-Dist: pyhamcrest>=2; extra == 'all_non_platform'
-Requires-Dist: pyopenssl>=21.0.0; extra == 'all_non_platform'
-Requires-Dist: pyserial>=3.0; extra == 'all_non_platform'
-Requires-Dist: pywin32!=226; (platform_system == 'Windows') and extra == 'all_non_platform'
-Requires-Dist: service-identity>=18.1.0; extra == 'all_non_platform'
-Requires-Dist: wsproto; extra == 'all_non_platform'
Provides-Extra: conch
Requires-Dist: appdirs>=1.4.0; extra == 'conch'
-Requires-Dist: bcrypt>=3.1.3; extra == 'conch'
-Requires-Dist: cryptography>=3.3; extra == 'conch'
+Requires-Dist: bcrypt>=3.2.1; extra == 'conch'
+Requires-Dist: cryptography>=38; extra == 'conch'
Provides-Extra: dev
Requires-Dist: coverage~=7.5; extra == 'dev'
Requires-Dist: cython-test-exception-raiser<2,>=1.0.2; extra == 'dev'
Requires-Dist: httpx[http2]>=0.27; extra == 'dev'
Requires-Dist: hypothesis>=6.56; extra == 'dev'
-Requires-Dist: pydoctor~=24.11.1; extra == 'dev'
+Requires-Dist: pydoctor~=25.4.0; extra == 'dev'
Requires-Dist: pyflakes~=2.2; extra == 'dev'
Requires-Dist: pyhamcrest>=2; extra == 'dev'
Requires-Dist: python-subunit~=1.4; extra == 'dev'
@@ -78,56 +62,37 @@ Requires-Dist: sphinx<7,>=6; extra == 'dev'
Requires-Dist: towncrier~=23.6; extra == 'dev'
Requires-Dist: twistedchecker~=0.7; extra == 'dev'
Provides-Extra: dev-release
-Requires-Dist: pydoctor~=24.11.1; extra == 'dev-release'
+Requires-Dist: pydoctor~=25.4.0; extra == 'dev-release'
Requires-Dist: sphinx-rtd-theme~=1.3; extra == 'dev-release'
Requires-Dist: sphinx<7,>=6; extra == 'dev-release'
Requires-Dist: towncrier~=23.6; extra == 'dev-release'
Provides-Extra: dev_release
-Requires-Dist: pydoctor~=24.11.1; extra == 'dev_release'
-Requires-Dist: sphinx-rtd-theme~=1.3; extra == 'dev_release'
-Requires-Dist: sphinx<7,>=6; extra == 'dev_release'
-Requires-Dist: towncrier~=23.6; extra == 'dev_release'
Provides-Extra: gtk-platform
Requires-Dist: appdirs>=1.4.0; extra == 'gtk-platform'
-Requires-Dist: bcrypt>=3.1.3; extra == 'gtk-platform'
-Requires-Dist: cryptography>=3.3; extra == 'gtk-platform'
+Requires-Dist: bcrypt>=3.2.1; extra == 'gtk-platform'
+Requires-Dist: cryptography>=38; extra == 'gtk-platform'
Requires-Dist: cython-test-exception-raiser<2,>=1.0.2; extra == 'gtk-platform'
Requires-Dist: h2<5.0,>=3.2; extra == 'gtk-platform'
Requires-Dist: httpx[http2]>=0.27; extra == 'gtk-platform'
Requires-Dist: hypothesis>=6.56; extra == 'gtk-platform'
Requires-Dist: idna>=2.4; extra == 'gtk-platform'
Requires-Dist: priority<2.0,>=1.1.0; extra == 'gtk-platform'
-Requires-Dist: pygobject; extra == 'gtk-platform'
+Requires-Dist: pygobject; (python_version >= '3.10') and extra == 'gtk-platform'
+Requires-Dist: pygobject<3.52.1; (python_version < '3.10') and extra == 'gtk-platform'
Requires-Dist: pyhamcrest>=2; extra == 'gtk-platform'
-Requires-Dist: pyopenssl>=21.0.0; extra == 'gtk-platform'
+Requires-Dist: pyopenssl>=25.2.0; extra == 'gtk-platform'
Requires-Dist: pyserial>=3.0; extra == 'gtk-platform'
Requires-Dist: pywin32!=226; (platform_system == 'Windows') and extra == 'gtk-platform'
Requires-Dist: service-identity>=18.1.0; extra == 'gtk-platform'
Requires-Dist: wsproto; extra == 'gtk-platform'
Provides-Extra: gtk_platform
-Requires-Dist: appdirs>=1.4.0; extra == 'gtk_platform'
-Requires-Dist: bcrypt>=3.1.3; extra == 'gtk_platform'
-Requires-Dist: cryptography>=3.3; extra == 'gtk_platform'
-Requires-Dist: cython-test-exception-raiser<2,>=1.0.2; extra == 'gtk_platform'
-Requires-Dist: h2<5.0,>=3.2; extra == 'gtk_platform'
-Requires-Dist: httpx[http2]>=0.27; extra == 'gtk_platform'
-Requires-Dist: hypothesis>=6.56; extra == 'gtk_platform'
-Requires-Dist: idna>=2.4; extra == 'gtk_platform'
-Requires-Dist: priority<2.0,>=1.1.0; extra == 'gtk_platform'
-Requires-Dist: pygobject; extra == 'gtk_platform'
-Requires-Dist: pyhamcrest>=2; extra == 'gtk_platform'
-Requires-Dist: pyopenssl>=21.0.0; extra == 'gtk_platform'
-Requires-Dist: pyserial>=3.0; extra == 'gtk_platform'
-Requires-Dist: pywin32!=226; (platform_system == 'Windows') and extra == 'gtk_platform'
-Requires-Dist: service-identity>=18.1.0; extra == 'gtk_platform'
-Requires-Dist: wsproto; extra == 'gtk_platform'
Provides-Extra: http2
Requires-Dist: h2<5.0,>=3.2; extra == 'http2'
Requires-Dist: priority<2.0,>=1.1.0; extra == 'http2'
Provides-Extra: macos-platform
Requires-Dist: appdirs>=1.4.0; extra == 'macos-platform'
-Requires-Dist: bcrypt>=3.1.3; extra == 'macos-platform'
-Requires-Dist: cryptography>=3.3; extra == 'macos-platform'
+Requires-Dist: bcrypt>=3.2.1; extra == 'macos-platform'
+Requires-Dist: cryptography>=38; extra == 'macos-platform'
Requires-Dist: cython-test-exception-raiser<2,>=1.0.2; extra == 'macos-platform'
Requires-Dist: h2<5.0,>=3.2; extra == 'macos-platform'
Requires-Dist: httpx[http2]>=0.27; extra == 'macos-platform'
@@ -135,56 +100,32 @@ Requires-Dist: hypothesis>=6.56; extra == 'macos-platform'
Requires-Dist: idna>=2.4; extra == 'macos-platform'
Requires-Dist: priority<2.0,>=1.1.0; extra == 'macos-platform'
Requires-Dist: pyhamcrest>=2; extra == 'macos-platform'
-Requires-Dist: pyobjc-core; (python_version >= '3.9') and extra == 'macos-platform'
-Requires-Dist: pyobjc-core<11; (python_version < '3.9') and extra == 'macos-platform'
-Requires-Dist: pyobjc-framework-cfnetwork; (python_version >= '3.9') and extra == 'macos-platform'
-Requires-Dist: pyobjc-framework-cfnetwork<11; (python_version < '3.9') and extra == 'macos-platform'
-Requires-Dist: pyobjc-framework-cocoa; (python_version >= '3.9') and extra == 'macos-platform'
-Requires-Dist: pyobjc-framework-cocoa<11; (python_version < '3.9') and extra == 'macos-platform'
-Requires-Dist: pyopenssl>=21.0.0; extra == 'macos-platform'
+Requires-Dist: pyobjc-core>=12; (python_version >= '3.10') and extra == 'macos-platform'
+Requires-Dist: pyobjc-framework-cfnetwork>=12; (python_version >= '3.10') and extra == 'macos-platform'
+Requires-Dist: pyobjc-framework-cocoa>=12; (python_version >= '3.10') and extra == 'macos-platform'
+Requires-Dist: pyopenssl>=25.2.0; extra == 'macos-platform'
Requires-Dist: pyserial>=3.0; extra == 'macos-platform'
Requires-Dist: pywin32!=226; (platform_system == 'Windows') and extra == 'macos-platform'
Requires-Dist: service-identity>=18.1.0; extra == 'macos-platform'
Requires-Dist: wsproto; extra == 'macos-platform'
Provides-Extra: macos_platform
-Requires-Dist: appdirs>=1.4.0; extra == 'macos_platform'
-Requires-Dist: bcrypt>=3.1.3; extra == 'macos_platform'
-Requires-Dist: cryptography>=3.3; extra == 'macos_platform'
-Requires-Dist: cython-test-exception-raiser<2,>=1.0.2; extra == 'macos_platform'
-Requires-Dist: h2<5.0,>=3.2; extra == 'macos_platform'
-Requires-Dist: httpx[http2]>=0.27; extra == 'macos_platform'
-Requires-Dist: hypothesis>=6.56; extra == 'macos_platform'
-Requires-Dist: idna>=2.4; extra == 'macos_platform'
-Requires-Dist: priority<2.0,>=1.1.0; extra == 'macos_platform'
-Requires-Dist: pyhamcrest>=2; extra == 'macos_platform'
-Requires-Dist: pyobjc-core; (python_version >= '3.9') and extra == 'macos_platform'
-Requires-Dist: pyobjc-core<11; (python_version < '3.9') and extra == 'macos_platform'
-Requires-Dist: pyobjc-framework-cfnetwork; (python_version >= '3.9') and extra == 'macos_platform'
-Requires-Dist: pyobjc-framework-cfnetwork<11; (python_version < '3.9') and extra == 'macos_platform'
-Requires-Dist: pyobjc-framework-cocoa; (python_version >= '3.9') and extra == 'macos_platform'
-Requires-Dist: pyobjc-framework-cocoa<11; (python_version < '3.9') and extra == 'macos_platform'
-Requires-Dist: pyopenssl>=21.0.0; extra == 'macos_platform'
-Requires-Dist: pyserial>=3.0; extra == 'macos_platform'
-Requires-Dist: pywin32!=226; (platform_system == 'Windows') and extra == 'macos_platform'
-Requires-Dist: service-identity>=18.1.0; extra == 'macos_platform'
-Requires-Dist: wsproto; extra == 'macos_platform'
Provides-Extra: mypy
Requires-Dist: appdirs>=1.4.0; extra == 'mypy'
-Requires-Dist: bcrypt>=3.1.3; extra == 'mypy'
+Requires-Dist: bcrypt>=3.2.1; extra == 'mypy'
Requires-Dist: coverage~=7.5; extra == 'mypy'
-Requires-Dist: cryptography>=3.3; extra == 'mypy'
+Requires-Dist: cryptography>=38; extra == 'mypy'
Requires-Dist: cython-test-exception-raiser<2,>=1.0.2; extra == 'mypy'
Requires-Dist: h2<5.0,>=3.2; extra == 'mypy'
Requires-Dist: httpx[http2]>=0.27; extra == 'mypy'
Requires-Dist: hypothesis>=6.56; extra == 'mypy'
Requires-Dist: idna>=2.4; extra == 'mypy'
-Requires-Dist: mypy-zope==1.0.6; extra == 'mypy'
-Requires-Dist: mypy==1.10.1; extra == 'mypy'
+Requires-Dist: mypy-zope==1.0.14; extra == 'mypy'
+Requires-Dist: mypy==1.19.1; extra == 'mypy'
Requires-Dist: priority<2.0,>=1.1.0; extra == 'mypy'
-Requires-Dist: pydoctor~=24.11.1; extra == 'mypy'
+Requires-Dist: pydoctor~=25.4.0; extra == 'mypy'
Requires-Dist: pyflakes~=2.2; extra == 'mypy'
Requires-Dist: pyhamcrest>=2; extra == 'mypy'
-Requires-Dist: pyopenssl>=21.0.0; extra == 'mypy'
+Requires-Dist: pyopenssl>=25.2.0; extra == 'mypy'
Requires-Dist: pyserial>=3.0; extra == 'mypy'
Requires-Dist: python-subunit~=1.4; extra == 'mypy'
Requires-Dist: pywin32!=226; (platform_system == 'Windows') and extra == 'mypy'
@@ -198,8 +139,8 @@ Requires-Dist: types-setuptools; extra == 'mypy'
Requires-Dist: wsproto; extra == 'mypy'
Provides-Extra: osx-platform
Requires-Dist: appdirs>=1.4.0; extra == 'osx-platform'
-Requires-Dist: bcrypt>=3.1.3; extra == 'osx-platform'
-Requires-Dist: cryptography>=3.3; extra == 'osx-platform'
+Requires-Dist: bcrypt>=3.2.1; extra == 'osx-platform'
+Requires-Dist: cryptography>=38; extra == 'osx-platform'
Requires-Dist: cython-test-exception-raiser<2,>=1.0.2; extra == 'osx-platform'
Requires-Dist: h2<5.0,>=3.2; extra == 'osx-platform'
Requires-Dist: httpx[http2]>=0.27; extra == 'osx-platform'
@@ -207,39 +148,15 @@ Requires-Dist: hypothesis>=6.56; extra == 'osx-platform'
Requires-Dist: idna>=2.4; extra == 'osx-platform'
Requires-Dist: priority<2.0,>=1.1.0; extra == 'osx-platform'
Requires-Dist: pyhamcrest>=2; extra == 'osx-platform'
-Requires-Dist: pyobjc-core; (python_version >= '3.9') and extra == 'osx-platform'
-Requires-Dist: pyobjc-core<11; (python_version < '3.9') and extra == 'osx-platform'
-Requires-Dist: pyobjc-framework-cfnetwork; (python_version >= '3.9') and extra == 'osx-platform'
-Requires-Dist: pyobjc-framework-cfnetwork<11; (python_version < '3.9') and extra == 'osx-platform'
-Requires-Dist: pyobjc-framework-cocoa; (python_version >= '3.9') and extra == 'osx-platform'
-Requires-Dist: pyobjc-framework-cocoa<11; (python_version < '3.9') and extra == 'osx-platform'
-Requires-Dist: pyopenssl>=21.0.0; extra == 'osx-platform'
+Requires-Dist: pyobjc-core>=12; (python_version >= '3.10') and extra == 'osx-platform'
+Requires-Dist: pyobjc-framework-cfnetwork>=12; (python_version >= '3.10') and extra == 'osx-platform'
+Requires-Dist: pyobjc-framework-cocoa>=12; (python_version >= '3.10') and extra == 'osx-platform'
+Requires-Dist: pyopenssl>=25.2.0; extra == 'osx-platform'
Requires-Dist: pyserial>=3.0; extra == 'osx-platform'
Requires-Dist: pywin32!=226; (platform_system == 'Windows') and extra == 'osx-platform'
Requires-Dist: service-identity>=18.1.0; extra == 'osx-platform'
Requires-Dist: wsproto; extra == 'osx-platform'
Provides-Extra: osx_platform
-Requires-Dist: appdirs>=1.4.0; extra == 'osx_platform'
-Requires-Dist: bcrypt>=3.1.3; extra == 'osx_platform'
-Requires-Dist: cryptography>=3.3; extra == 'osx_platform'
-Requires-Dist: cython-test-exception-raiser<2,>=1.0.2; extra == 'osx_platform'
-Requires-Dist: h2<5.0,>=3.2; extra == 'osx_platform'
-Requires-Dist: httpx[http2]>=0.27; extra == 'osx_platform'
-Requires-Dist: hypothesis>=6.56; extra == 'osx_platform'
-Requires-Dist: idna>=2.4; extra == 'osx_platform'
-Requires-Dist: priority<2.0,>=1.1.0; extra == 'osx_platform'
-Requires-Dist: pyhamcrest>=2; extra == 'osx_platform'
-Requires-Dist: pyobjc-core; (python_version >= '3.9') and extra == 'osx_platform'
-Requires-Dist: pyobjc-core<11; (python_version < '3.9') and extra == 'osx_platform'
-Requires-Dist: pyobjc-framework-cfnetwork; (python_version >= '3.9') and extra == 'osx_platform'
-Requires-Dist: pyobjc-framework-cfnetwork<11; (python_version < '3.9') and extra == 'osx_platform'
-Requires-Dist: pyobjc-framework-cocoa; (python_version >= '3.9') and extra == 'osx_platform'
-Requires-Dist: pyobjc-framework-cocoa<11; (python_version < '3.9') and extra == 'osx_platform'
-Requires-Dist: pyopenssl>=21.0.0; extra == 'osx_platform'
-Requires-Dist: pyserial>=3.0; extra == 'osx_platform'
-Requires-Dist: pywin32!=226; (platform_system == 'Windows') and extra == 'osx_platform'
-Requires-Dist: service-identity>=18.1.0; extra == 'osx_platform'
-Requires-Dist: wsproto; extra == 'osx_platform'
Provides-Extra: serial
Requires-Dist: pyserial>=3.0; extra == 'serial'
Requires-Dist: pywin32!=226; (platform_system == 'Windows') and extra == 'serial'
@@ -250,14 +167,14 @@ Requires-Dist: hypothesis>=6.56; extra == 'test'
Requires-Dist: pyhamcrest>=2; extra == 'test'
Provides-Extra: tls
Requires-Dist: idna>=2.4; extra == 'tls'
-Requires-Dist: pyopenssl>=21.0.0; extra == 'tls'
+Requires-Dist: pyopenssl>=25.2.0; extra == 'tls'
Requires-Dist: service-identity>=18.1.0; extra == 'tls'
Provides-Extra: websocket
Requires-Dist: wsproto; extra == 'websocket'
Provides-Extra: windows-platform
Requires-Dist: appdirs>=1.4.0; extra == 'windows-platform'
-Requires-Dist: bcrypt>=3.1.3; extra == 'windows-platform'
-Requires-Dist: cryptography>=3.3; extra == 'windows-platform'
+Requires-Dist: bcrypt>=3.2.1; extra == 'windows-platform'
+Requires-Dist: cryptography>=38; extra == 'windows-platform'
Requires-Dist: cython-test-exception-raiser<2,>=1.0.2; extra == 'windows-platform'
Requires-Dist: h2<5.0,>=3.2; extra == 'windows-platform'
Requires-Dist: httpx[http2]>=0.27; extra == 'windows-platform'
@@ -265,7 +182,7 @@ Requires-Dist: hypothesis>=6.56; extra == 'windows-platform'
Requires-Dist: idna>=2.4; extra == 'windows-platform'
Requires-Dist: priority<2.0,>=1.1.0; extra == 'windows-platform'
Requires-Dist: pyhamcrest>=2; extra == 'windows-platform'
-Requires-Dist: pyopenssl>=21.0.0; extra == 'windows-platform'
+Requires-Dist: pyopenssl>=25.2.0; extra == 'windows-platform'
Requires-Dist: pyserial>=3.0; extra == 'windows-platform'
Requires-Dist: pywin32!=226; extra == 'windows-platform'
Requires-Dist: pywin32!=226; (platform_system == 'Windows') and extra == 'windows-platform'
@@ -273,23 +190,6 @@ Requires-Dist: service-identity>=18.1.0; extra == 'windows-platform'
Requires-Dist: twisted-iocpsupport>=1.0.2; extra == 'windows-platform'
Requires-Dist: wsproto; extra == 'windows-platform'
Provides-Extra: windows_platform
-Requires-Dist: appdirs>=1.4.0; extra == 'windows_platform'
-Requires-Dist: bcrypt>=3.1.3; extra == 'windows_platform'
-Requires-Dist: cryptography>=3.3; extra == 'windows_platform'
-Requires-Dist: cython-test-exception-raiser<2,>=1.0.2; extra == 'windows_platform'
-Requires-Dist: h2<5.0,>=3.2; extra == 'windows_platform'
-Requires-Dist: httpx[http2]>=0.27; extra == 'windows_platform'
-Requires-Dist: hypothesis>=6.56; extra == 'windows_platform'
-Requires-Dist: idna>=2.4; extra == 'windows_platform'
-Requires-Dist: priority<2.0,>=1.1.0; extra == 'windows_platform'
-Requires-Dist: pyhamcrest>=2; extra == 'windows_platform'
-Requires-Dist: pyopenssl>=21.0.0; extra == 'windows_platform'
-Requires-Dist: pyserial>=3.0; extra == 'windows_platform'
-Requires-Dist: pywin32!=226; extra == 'windows_platform'
-Requires-Dist: pywin32!=226; (platform_system == 'Windows') and extra == 'windows_platform'
-Requires-Dist: service-identity>=18.1.0; extra == 'windows_platform'
-Requires-Dist: twisted-iocpsupport>=1.0.2; extra == 'windows_platform'
-Requires-Dist: wsproto; extra == 'windows_platform'
Description-Content-Type: text/x-rst
Twisted
@@ -393,7 +293,7 @@ Or, for speed, use pre-commit directly::
Copyright
---------
-All of the code in this distribution is Copyright (c) 2001-2025 Twisted Matrix Laboratories.
+All of the code in this distribution is Copyright (c) 2001-2026 Twisted Matrix Laboratories.
Twisted is made available under the MIT license.
The included `LICENSE <https://github.com/twisted/twisted/blob/trunk/LICENSE>`_ file describes this in detail.
diff --git a/contrib/python/Twisted/py3/LICENSE b/contrib/python/Twisted/py3/LICENSE
index 3a9fdc29b98..49acef5ac7c 100644
--- a/contrib/python/Twisted/py3/LICENSE
+++ b/contrib/python/Twisted/py3/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2001-2025
+Copyright (c) 2001-2026
Allen Short
Amber Hawkie Brown
Andrew Bennetts
diff --git a/contrib/python/Twisted/py3/README.rst b/contrib/python/Twisted/py3/README.rst
index 9afa4ffb3db..8800c5c4aba 100644
--- a/contrib/python/Twisted/py3/README.rst
+++ b/contrib/python/Twisted/py3/README.rst
@@ -99,7 +99,7 @@ Or, for speed, use pre-commit directly::
Copyright
---------
-All of the code in this distribution is Copyright (c) 2001-2025 Twisted Matrix Laboratories.
+All of the code in this distribution is Copyright (c) 2001-2026 Twisted Matrix Laboratories.
Twisted is made available under the MIT license.
The included `LICENSE <LICENSE>`_ file describes this in detail.
diff --git a/contrib/python/Twisted/py3/twisted/_threads/_pool.py b/contrib/python/Twisted/py3/twisted/_threads/_pool.py
index 99c055d2404..3dff48bafda 100644
--- a/contrib/python/Twisted/py3/twisted/_threads/_pool.py
+++ b/contrib/python/Twisted/py3/twisted/_threads/_pool.py
@@ -7,12 +7,11 @@ Top level thread pool interface, used to implement
L{twisted.python.threadpool}.
"""
+from __future__ import annotations
from queue import Queue
from threading import Lock, Thread, local as LocalStorage
-from typing import Callable, Optional
-
-from typing_extensions import Protocol
+from typing import Callable, Protocol
from twisted.python.log import err
from ._ithreads import IWorker
@@ -59,7 +58,7 @@ def pool(
def startThread(target: Callable[..., object]) -> None:
return threadFactory(target=target).start()
- def limitedWorkerCreator() -> Optional[IWorker]:
+ def limitedWorkerCreator() -> IWorker | None:
stats = team.statistics()
if stats.busyWorkerCount + stats.idleWorkerCount >= currentLimit():
return None
diff --git a/contrib/python/Twisted/py3/twisted/_threads/_team.py b/contrib/python/Twisted/py3/twisted/_threads/_team.py
index 95e40cffa95..2d34fd37dbf 100644
--- a/contrib/python/Twisted/py3/twisted/_threads/_team.py
+++ b/contrib/python/Twisted/py3/twisted/_threads/_team.py
@@ -9,7 +9,7 @@ workers.
from __future__ import annotations
from collections import deque
-from typing import Callable, Optional, Set
+from typing import Callable
from zope.interface import implementer
@@ -77,7 +77,7 @@ class Team:
def __init__(
self,
coordinator: IExclusiveWorker,
- createWorker: Callable[[], Optional[IWorker]],
+ createWorker: Callable[[], IWorker | None],
logException: Callable[[], None],
):
"""
@@ -100,9 +100,9 @@ class Team:
self._logException = logException
# Don't touch these except from the coordinator.
- self._idle: Set[IWorker] = set()
+ self._idle: set[IWorker] = set()
self._busyCount = 0
- self._pending: "deque[Callable[..., object]]" = deque()
+ self._pending: deque[Callable[..., object]] = deque()
self._shouldQuitCoordinator = False
self._toShrink = 0
@@ -131,7 +131,7 @@ class Team:
return
self._recycleWorker(worker)
- def shrink(self, n: Optional[int] = None) -> None:
+ def shrink(self, n: int | None = None) -> None:
"""
Decrease the number of idle workers by C{n}.
@@ -142,7 +142,7 @@ class Team:
self._quit.check()
self._coordinator.do(lambda: self._quitIdlers(n))
- def _quitIdlers(self, n: Optional[int] = None) -> None:
+ def _quitIdlers(self, n: int | None = None) -> None:
"""
The implmentation of C{shrink}, performed by the coordinator worker.
diff --git a/contrib/python/Twisted/py3/twisted/_threads/_threadworker.py b/contrib/python/Twisted/py3/twisted/_threads/_threadworker.py
index a4617a1974c..673b6b66bf2 100644
--- a/contrib/python/Twisted/py3/twisted/_threads/_threadworker.py
+++ b/contrib/python/Twisted/py3/twisted/_threads/_threadworker.py
@@ -7,8 +7,9 @@ Implementation of an L{IWorker} based on native threads and queues.
"""
from __future__ import annotations
+from collections.abc import Iterator
from enum import Enum, auto
-from typing import TYPE_CHECKING, Callable, Iterator, Literal, Protocol, TypeVar
+from typing import TYPE_CHECKING, Callable, Literal, Protocol, TypeVar
if TYPE_CHECKING:
import threading
diff --git a/contrib/python/Twisted/py3/twisted/_version.py b/contrib/python/Twisted/py3/twisted/_version.py
index 02290f83ab6..f6bce32271a 100644
--- a/contrib/python/Twisted/py3/twisted/_version.py
+++ b/contrib/python/Twisted/py3/twisted/_version.py
@@ -7,5 +7,5 @@ Provides Twisted version information.
from incremental import Version
-__version__ = Version("Twisted", 25, 5, 0)
+__version__ = Version("Twisted", 26, 4, 0)
__all__ = ["__version__"]
diff --git a/contrib/python/Twisted/py3/twisted/application/internet.py b/contrib/python/Twisted/py3/twisted/application/internet.py
index 8bcc9722a0a..ab4e6b142ff 100644
--- a/contrib/python/Twisted/py3/twisted/application/internet.py
+++ b/contrib/python/Twisted/py3/twisted/application/internet.py
@@ -37,8 +37,7 @@ C{TCPServer(8080, server.Site(r))}. See the documentation for the
reactor.listen/connect* methods for more information.
"""
-
-from typing import List
+from __future__ import annotations
from twisted.application import service
from twisted.internet import task
@@ -48,7 +47,7 @@ from ._client_service import ClientService, _maybeGlobalReactor, backoffPolicy
class _VolatileDataService(service.Service):
- volatile: List[str] = []
+ volatile: list[str] = []
def __getstate__(self):
d = service.Service.__getstate__(self)
diff --git a/contrib/python/Twisted/py3/twisted/application/reactors.py b/contrib/python/Twisted/py3/twisted/application/reactors.py
index a476ca98b79..04d516c44b8 100644
--- a/contrib/python/Twisted/py3/twisted/application/reactors.py
+++ b/contrib/python/Twisted/py3/twisted/application/reactors.py
@@ -6,7 +6,8 @@
Plugin-based system for enumerating available reactors and installing one of
them.
"""
-from typing import Iterable, cast
+from collections.abc import Iterable
+from typing import cast
from zope.interface import Attribute, Interface, implementer
diff --git a/contrib/python/Twisted/py3/twisted/application/runner/_exit.py b/contrib/python/Twisted/py3/twisted/application/runner/_exit.py
index 3d6bc10f6f9..ae2df3efbba 100644
--- a/contrib/python/Twisted/py3/twisted/application/runner/_exit.py
+++ b/contrib/python/Twisted/py3/twisted/application/runner/_exit.py
@@ -6,10 +6,11 @@
System exit support.
"""
-import typing
+from __future__ import annotations
+
from enum import IntEnum
from sys import exit as sysexit, stderr, stdout
-from typing import Union
+from typing import NoReturn
try:
import posix as Status
@@ -80,7 +81,7 @@ class ExitStatus(IntEnum):
EX_CONFIG = Status.EX_CONFIG
-def exit(status: Union[int, ExitStatus], message: str = "") -> "typing.NoReturn":
+def exit(status: int | ExitStatus, message: str = "") -> NoReturn:
"""
Exit the python interpreter with the given status and an optional message.
diff --git a/contrib/python/Twisted/py3/twisted/application/runner/_pidfile.py b/contrib/python/Twisted/py3/twisted/application/runner/_pidfile.py
index b6aab1499fb..8b27b24428a 100644
--- a/contrib/python/Twisted/py3/twisted/application/runner/_pidfile.py
+++ b/contrib/python/Twisted/py3/twisted/application/runner/_pidfile.py
@@ -10,7 +10,7 @@ from __future__ import annotations
import errno
from os import getpid, kill, name as SYSTEM_NAME
from types import TracebackType
-from typing import Any, Optional, Type
+from typing import Any
from zope.interface import Interface, implementer
@@ -62,7 +62,7 @@ class IPIDFile(Interface):
for which there is no corresponding running process.
"""
- def __enter__() -> "IPIDFile":
+ def __enter__() -> IPIDFile:
"""
Enter a context using this PIDFile.
@@ -73,10 +73,10 @@ class IPIDFile(Interface):
"""
def __exit__(
- excType: Optional[Type[BaseException]],
- excValue: Optional[BaseException],
- traceback: Optional[TracebackType],
- ) -> Optional[bool]:
+ excType: type[BaseException] | None,
+ excValue: BaseException | None,
+ traceback: TracebackType | None,
+ ) -> bool | None:
"""
Exit a context using this PIDFile.
@@ -187,7 +187,7 @@ class PIDFile:
else:
return True
- def __enter__(self) -> "PIDFile":
+ def __enter__(self) -> PIDFile:
try:
if self.isRunning():
raise AlreadyRunningError()
@@ -198,9 +198,9 @@ class PIDFile:
def __exit__(
self,
- excType: Optional[Type[BaseException]],
- excValue: Optional[BaseException],
- traceback: Optional[TracebackType],
+ excType: type[BaseException] | None,
+ excValue: BaseException | None,
+ traceback: TracebackType | None,
) -> None:
self.remove()
return None
@@ -242,14 +242,14 @@ class NonePIDFile:
def isRunning(self) -> bool:
return False
- def __enter__(self) -> "NonePIDFile":
+ def __enter__(self) -> NonePIDFile:
return self
def __exit__(
self,
- excType: Optional[Type[BaseException]],
- excValue: Optional[BaseException],
- traceback: Optional[TracebackType],
+ excType: type[BaseException] | None,
+ excValue: BaseException | None,
+ traceback: TracebackType | None,
) -> None:
return None
diff --git a/contrib/python/Twisted/py3/twisted/application/runner/_runner.py b/contrib/python/Twisted/py3/twisted/application/runner/_runner.py
index 01bbaeba1bd..79913912175 100644
--- a/contrib/python/Twisted/py3/twisted/application/runner/_runner.py
+++ b/contrib/python/Twisted/py3/twisted/application/runner/_runner.py
@@ -6,10 +6,11 @@
Twisted application runner.
"""
+from collections.abc import Mapping
from os import kill
from signal import SIGTERM
from sys import stderr
-from typing import Any, Callable, Mapping, TextIO
+from typing import Any, Callable, TextIO
from attr import Factory, attrib, attrs
from constantly import NamedConstant
diff --git a/contrib/python/Twisted/py3/twisted/application/strports.py b/contrib/python/Twisted/py3/twisted/application/strports.py
index 3c96ee335d4..088e8d4311a 100644
--- a/contrib/python/Twisted/py3/twisted/application/strports.py
+++ b/contrib/python/Twisted/py3/twisted/application/strports.py
@@ -8,7 +8,9 @@ Construct listening port services from a simple string description.
@see: L{twisted.internet.endpoints.serverFromString}
@see: L{twisted.internet.endpoints.clientFromString}
"""
-from typing import Optional, cast
+from __future__ import annotations
+
+from typing import cast
from twisted.application.internet import StreamServerEndpointService
from twisted.internet import endpoints, interfaces
@@ -23,7 +25,7 @@ def _getReactor() -> interfaces.IReactorCore:
def service(
description: str,
factory: interfaces.IProtocolFactory,
- reactor: Optional[interfaces.IReactorCore] = None,
+ reactor: interfaces.IReactorCore | None = None,
) -> StreamServerEndpointService:
"""
Return the service corresponding to a description.
diff --git a/contrib/python/Twisted/py3/twisted/application/twist/_options.py b/contrib/python/Twisted/py3/twisted/application/twist/_options.py
index 2bcd207bf4c..2d30d39cd1e 100644
--- a/contrib/python/Twisted/py3/twisted/application/twist/_options.py
+++ b/contrib/python/Twisted/py3/twisted/application/twist/_options.py
@@ -6,10 +6,12 @@
Command line options for C{twist}.
"""
-import typing
+from __future__ import annotations
+
+from collections.abc import Iterable, Mapping, Sequence
from sys import stderr, stdout
from textwrap import dedent
-from typing import Callable, Iterable, Mapping, Optional, Sequence, Tuple, cast
+from typing import Callable, NoReturn, cast
from twisted.copyright import version
from twisted.internet.interfaces import IReactorCore
@@ -28,7 +30,7 @@ from ..service import IServiceMaker
openFile = open
-def _update_doc(opt: Callable[["TwistOptions", str], None], **kwargs: str) -> None:
+def _update_doc(opt: Callable[[TwistOptions, str], None], **kwargs: str) -> None:
"""
Update the docstring of a method that implements an option.
The string is dedented and the given keyword arguments are substituted.
@@ -58,7 +60,7 @@ class TwistOptions(Options):
def getSynopsis(self) -> str:
return f"{Options.getSynopsis(self)} plugin [plugin_options]"
- def opt_version(self) -> "typing.NoReturn":
+ def opt_version(self) -> NoReturn:
"""
Print version and exit.
"""
@@ -166,7 +168,7 @@ class TwistOptions(Options):
self["fileLogObserverFactory"] = jsonFileLogObserver
self["logFormat"] = "json"
- def parseOptions(self, options: Optional[Sequence[str]] = None) -> None:
+ def parseOptions(self, options: Sequence[str] | None = None) -> None:
self.selectDefaultLogObserver()
Options.parseOptions(self, options=options)
@@ -187,7 +189,7 @@ class TwistOptions(Options):
@property
def subCommands(
self,
- ) -> Iterable[Tuple[str, None, Callable[[IServiceMaker], Options], str]]:
+ ) -> Iterable[tuple[str, None, Callable[[IServiceMaker], Options], str]]:
plugins = self.plugins
for name in sorted(plugins):
plugin = plugins[name]
diff --git a/contrib/python/Twisted/py3/twisted/application/twist/_twist.py b/contrib/python/Twisted/py3/twisted/application/twist/_twist.py
index 80cf4470f11..f6f19737dfa 100644
--- a/contrib/python/Twisted/py3/twisted/application/twist/_twist.py
+++ b/contrib/python/Twisted/py3/twisted/application/twist/_twist.py
@@ -7,7 +7,7 @@ Run a Twisted application.
"""
import sys
-from typing import Sequence
+from collections.abc import Sequence
from twisted.application.app import _exitWithSignal
from twisted.internet.interfaces import IReactorCore, _ISupportsExitSignalCapturing
diff --git a/contrib/python/Twisted/py3/twisted/conch/checkers.py b/contrib/python/Twisted/py3/twisted/conch/checkers.py
index 3ade2d8eeb8..ed596c52a5f 100644
--- a/contrib/python/Twisted/py3/twisted/conch/checkers.py
+++ b/contrib/python/Twisted/py3/twisted/conch/checkers.py
@@ -6,17 +6,18 @@
Provide L{ICredentialsChecker} implementations to be used in Conch protocols.
"""
+from __future__ import annotations
import binascii
import errno
import sys
from base64 import decodebytes
-from typing import IO, Any, Callable, Iterable, Iterator, Mapping, Optional, Tuple, cast
+from collections.abc import Iterable, Iterator, Mapping
+from typing import IO, Any, Callable, Literal, Protocol, cast
from zope.interface import Interface, implementer, providedBy
from incremental import Version
-from typing_extensions import Literal, Protocol
from twisted.conch import error
from twisted.conch.ssh import keys
@@ -34,12 +35,12 @@ from twisted.python.util import runAsEffectiveUser
_log = Logger()
-class UserRecord(Tuple[str, str, int, int, str, str, str]):
+class UserRecord(tuple[str, str, int, int, str, str, str]):
"""
- A record in a UNIX-style password database. See L{pwd} for field details.
+ A record in a UNIX-style password database. See L{pwd} for field details.
- This corresponds to the undocumented type L{pwd.struct_passwd}, but lacks named
- field accessors.
+ This corresponds to the undocumented type C{pwd.struct_passwd}, but lacks
+ named field accessors.
"""
@property
@@ -62,7 +63,7 @@ class UserDB(Protocol):
"""
-pwd: Optional[UserDB]
+pwd: UserDB | None
try:
import pwd as _pwd
except ImportError:
@@ -83,7 +84,7 @@ class CryptedPasswordRecord(Protocol):
"""
A sequence where the item at index 1 may be a crypted password.
- Both L{pwd.struct_passwd} and L{spwd.struct_spwd} conform to this protocol.
+ Both C{pwd.struct_passwd} and C{spwd.struct_spwd} conform to this protocol.
"""
def __getitem__(self, index: Literal[1]) -> str:
@@ -106,7 +107,7 @@ def _lookupUser(userdb: UserDB, username: bytes) -> UserRecord:
return userdb.getpwnam(username.decode(sys.getfilesystemencoding()))
-def _pwdGetByName(username: str) -> Optional[CryptedPasswordRecord]:
+def _pwdGetByName(username: str) -> CryptedPasswordRecord | None:
"""
Look up a user in the /etc/passwd database using the pwd module. If the
pwd module is not available, return None.
@@ -114,7 +115,7 @@ def _pwdGetByName(username: str) -> Optional[CryptedPasswordRecord]:
@param username: the username of the user to return the passwd database
information for.
- @returns: A L{pwd.struct_passwd}, where field 1 may contain a crypted
+ @returns: A C{pwd.struct_passwd}, where field 1 may contain a crypted
password, or L{None} when the L{pwd} database is unavailable.
@raises KeyError: when no such user exists
@@ -124,16 +125,16 @@ def _pwdGetByName(username: str) -> Optional[CryptedPasswordRecord]:
return cast(CryptedPasswordRecord, pwd.getpwnam(username))
-def _shadowGetByName(username: str) -> Optional[CryptedPasswordRecord]:
+def _shadowGetByName(username: str) -> CryptedPasswordRecord | None:
"""
- Look up a user in the /etc/shadow database using the spwd module. If it is
+ Look up a user in the /etc/shadow database using the spwd module. If it is
not available, return L{None}.
@param username: the username of the user to return the shadow database
information for.
@type username: L{str}
- @returns: A L{spwd.struct_spwd}, where field 1 may contain a crypted
+ @returns: A C{spwd.struct_spwd}, where field 1 may contain a crypted
password, or L{None} when the L{spwd} database is unavailable.
@raises KeyError: when no such user exists
@@ -506,7 +507,7 @@ class UNIXAuthorizedKeysFiles:
def __init__(
self,
- userdb: Optional[UserDB] = None,
+ userdb: UserDB | None = None,
parseKey: Callable[[bytes], keys.Key] = keys.Key.fromString,
):
"""
diff --git a/contrib/python/Twisted/py3/twisted/conch/client/direct.py b/contrib/python/Twisted/py3/twisted/conch/client/direct.py
index 33fd1d2df46..b38fb43e4e2 100644
--- a/contrib/python/Twisted/py3/twisted/conch/client/direct.py
+++ b/contrib/python/Twisted/py3/twisted/conch/client/direct.py
@@ -23,7 +23,7 @@ if TYPE_CHECKING:
from twisted.conch.ssh.userauth import SSHUserAuthClient
-class SSHClientFactory(protocol.ClientFactory):
+class SSHClientFactory(protocol.ClientFactory["transport.SSHClientTransport"]):
def __init__(
self,
d: Deferred[None],
@@ -46,7 +46,7 @@ class SSHClientFactory(protocol.ClientFactory):
d, self.d = self.d, None
d.errback(reason)
- def buildProtocol(self, addr: IAddress) -> SSHClientTransport:
+ def buildProtocol(self, addr: IAddress | None) -> SSHClientTransport:
trans = SSHClientTransport(self)
if self.options["ciphers"]:
trans.supportedCiphers = self.options["ciphers"]
@@ -61,7 +61,7 @@ class SSHClientFactory(protocol.ClientFactory):
class SSHClientTransport(transport.SSHClientTransport):
# pre-mypy LSP violation
- factory: SSHClientFactory # type:ignore[assignment]
+ factory: SSHClientFactory
def __init__(self, factory: SSHClientFactory) -> None:
self.factory = factory
@@ -140,6 +140,12 @@ def connect(
userAuthObject: SSHUserAuthClient,
) -> Deferred[None]:
d: Deferred[None] = defer.Deferred()
- factory = SSHClientFactory(d, options, verifyHostKey, userAuthObject)
+ factory: protocol.ClientFactory[transport.SSHClientTransport] = SSHClientFactory(
+ d, options, verifyHostKey, userAuthObject
+ )
+ # this is just broken, right? very straightforwardly this is inference
+ # giving up because there's a cycle in the type graph
+ # (_ProtoWithFactory.factory -> Factory[Self] -> Factory.protocol:
+ # "Optional[Callable[..., P]]" -> P < _ProtoWithFactory)
IReactorTCP(reactor).connectTCP(host, port, factory)
return d
diff --git a/contrib/python/Twisted/py3/twisted/conch/client/knownhosts.py b/contrib/python/Twisted/py3/twisted/conch/client/knownhosts.py
index 1aa4b477c27..87944c2862b 100644
--- a/contrib/python/Twisted/py3/twisted/conch/client/knownhosts.py
+++ b/contrib/python/Twisted/py3/twisted/conch/client/knownhosts.py
@@ -13,9 +13,10 @@ from __future__ import annotations
import hmac
import sys
from binascii import Error as DecodeError, a2b_base64, b2a_base64
+from collections.abc import Iterable
from contextlib import closing
from hashlib import sha1
-from typing import IO, Callable, Iterable, Literal
+from typing import IO, Callable, Literal
from zope.interface import implementer
diff --git a/contrib/python/Twisted/py3/twisted/conch/client/options.py b/contrib/python/Twisted/py3/twisted/conch/client/options.py
index 2ab2455099a..72e8f38ec2e 100644
--- a/contrib/python/Twisted/py3/twisted/conch/client/options.py
+++ b/contrib/python/Twisted/py3/twisted/conch/client/options.py
@@ -1,8 +1,9 @@
# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.
+from __future__ import annotations
+
import sys
-from typing import List, Optional, Union
#
from twisted.conch.ssh.transport import SSHCiphers, SSHClientTransport
@@ -10,7 +11,7 @@ from twisted.python import usage
class ConchOptions(usage.Options):
- optParameters: List[List[Optional[Union[str, int]]]] = [
+ optParameters: list[list[str | int | None]] = [
["user", "l", None, "Log in using this user name."],
["identity", "i", None],
["ciphers", "c", None],
diff --git a/contrib/python/Twisted/py3/twisted/conch/manhole.py b/contrib/python/Twisted/py3/twisted/conch/manhole.py
index 670ac0480ec..ab758b5f565 100644
--- a/contrib/python/Twisted/py3/twisted/conch/manhole.py
+++ b/contrib/python/Twisted/py3/twisted/conch/manhole.py
@@ -19,7 +19,6 @@ import tokenize
from io import BytesIO
from traceback import format_exception
from types import TracebackType
-from typing import Type
from twisted.conch import recvline
from twisted.internet import defer
@@ -117,7 +116,7 @@ class ManholeInterpreter(code.InteractiveInterpreter):
def excepthook(
self,
- excType: Type[BaseException],
+ excType: type[BaseException],
excValue: BaseException,
excTraceback: TracebackType,
) -> None:
diff --git a/contrib/python/Twisted/py3/twisted/conch/manhole_ssh.py b/contrib/python/Twisted/py3/twisted/conch/manhole_ssh.py
index 8ac3b6d4be1..977e947d227 100644
--- a/contrib/python/Twisted/py3/twisted/conch/manhole_ssh.py
+++ b/contrib/python/Twisted/py3/twisted/conch/manhole_ssh.py
@@ -8,8 +8,6 @@ insults/SSH integration support.
@author: Jp Calderone
"""
-from typing import Dict
-
from zope.interface import implementer
from twisted.conch import avatar, error as econch, interfaces as iconch
@@ -141,8 +139,8 @@ class TerminalRealm:
class ConchFactory(factory.SSHFactory):
- publicKeys: Dict[bytes, bytes] = {}
- privateKeys: Dict[bytes, bytes] = {}
+ publicKeys: dict[bytes, bytes] = {}
+ privateKeys: dict[bytes, bytes] = {}
def __init__(self, portal):
self.portal = portal
diff --git a/contrib/python/Twisted/py3/twisted/conch/openssh_compat/factory.py b/contrib/python/Twisted/py3/twisted/conch/openssh_compat/factory.py
index 20051fc89f7..f92b2cbe156 100644
--- a/contrib/python/Twisted/py3/twisted/conch/openssh_compat/factory.py
+++ b/contrib/python/Twisted/py3/twisted/conch/openssh_compat/factory.py
@@ -7,9 +7,10 @@ Factory for reading openssh configuration files: public keys, private keys, and
moduli file.
"""
+from __future__ import annotations
+
import errno
import os
-from typing import Dict, List, Optional, Tuple
from twisted.conch.openssh_compat import primes
from twisted.conch.ssh import common, factory, keys
@@ -67,7 +68,7 @@ class OpenSSHFactory(factory.SSHFactory):
privateKeys[key.sshType()] = key
return privateKeys
- def getPrimes(self) -> Optional[Dict[int, List[Tuple[int, int]]]]:
+ def getPrimes(self) -> dict[int, list[tuple[int, int]]] | None:
try:
return primes.parseModuliFile(self.moduliRoot + "/moduli")
except OSError:
diff --git a/contrib/python/Twisted/py3/twisted/conch/openssh_compat/primes.py b/contrib/python/Twisted/py3/twisted/conch/openssh_compat/primes.py
index 9e2070e19aa..3c3bbc139d5 100644
--- a/contrib/python/Twisted/py3/twisted/conch/openssh_compat/primes.py
+++ b/contrib/python/Twisted/py3/twisted/conch/openssh_compat/primes.py
@@ -10,13 +10,10 @@ Maintainer: Paul Swartz
"""
-from typing import Dict, List, Tuple
-
-
-def parseModuliFile(filename: str) -> Dict[int, List[Tuple[int, int]]]:
+def parseModuliFile(filename: str) -> dict[int, list[tuple[int, int]]]:
with open(filename) as f:
lines = f.readlines()
- primes: Dict[int, List[Tuple[int, int]]] = {}
+ primes: dict[int, list[tuple[int, int]]] = {}
for l in lines:
l = l.strip()
if not l or l[0] == "#":
diff --git a/contrib/python/Twisted/py3/twisted/conch/recvline.py b/contrib/python/Twisted/py3/twisted/conch/recvline.py
index aa8115bdc7a..6a495ff5392 100644
--- a/contrib/python/Twisted/py3/twisted/conch/recvline.py
+++ b/contrib/python/Twisted/py3/twisted/conch/recvline.py
@@ -9,7 +9,6 @@ Basic line editing support.
"""
import string
-from typing import Dict
from zope.interface import implementer
@@ -18,7 +17,7 @@ from twisted.logger import Logger
from twisted.python import reflect
from twisted.python.compat import iterbytes
-_counters: Dict[str, int] = {}
+_counters: dict[str, int] = {}
class Logging:
diff --git a/contrib/python/Twisted/py3/twisted/conch/scripts/cftp.py b/contrib/python/Twisted/py3/twisted/conch/scripts/cftp.py
index e8241fdc3ad..8f81aa9542c 100644
--- a/contrib/python/Twisted/py3/twisted/conch/scripts/cftp.py
+++ b/contrib/python/Twisted/py3/twisted/conch/scripts/cftp.py
@@ -5,6 +5,9 @@
"""
Implementation module for the I{cftp} command.
"""
+
+from __future__ import annotations
+
import fcntl
import fnmatch
import getpass
@@ -15,7 +18,7 @@ import stat
import struct
import sys
import tty
-from typing import List, Optional, TextIO, Union
+from typing import TextIO
from twisted.conch.client import connect, default, options
from twisted.conch.ssh import channel, common, connection, filetransfer
@@ -35,7 +38,7 @@ class ClientOptions(options.ConchOptions):
"executing commands to send and receive file information"
)
- optParameters: List[List[Optional[Union[str, int]]]] = [
+ optParameters: list[list[str | int | None]] = [
["buffersize", "B", 32768, "Size of the buffer to use for sending/receiving."],
["batchfile", "b", None, "File to read commands from, or '-' for stdin."],
["requests", "R", 5, "Number of requests to make before waiting for a reply."],
diff --git a/contrib/python/Twisted/py3/twisted/conch/scripts/ckeygen.py b/contrib/python/Twisted/py3/twisted/conch/scripts/ckeygen.py
index 728dc6430c7..bac2ba41ffd 100644
--- a/contrib/python/Twisted/py3/twisted/conch/scripts/ckeygen.py
+++ b/contrib/python/Twisted/py3/twisted/conch/scripts/ckeygen.py
@@ -15,7 +15,7 @@ import sys
from collections.abc import Callable
from functools import wraps
from importlib import reload
-from typing import Any, Dict, Optional
+from typing import Any
from twisted.conch.ssh import keys
from twisted.python import failure, filepath, log, usage
@@ -206,8 +206,8 @@ def _defaultPrivateKeySubtype(keyType):
def _getKeyOrDefault(
- options: Dict[Any, Any],
- inputCollector: Optional[Callable[[str], str]] = None,
+ options: dict[Any, Any],
+ inputCollector: Callable[[str], str] | None = None,
keyTypeName: str = "rsa",
) -> str:
"""
@@ -231,7 +231,7 @@ def _getKeyOrDefault(
return str(filename)
-def printFingerprint(options: Dict[Any, Any]) -> None:
+def printFingerprint(options: dict[Any, Any]) -> None:
filename = _getKeyOrDefault(options)
if os.path.exists(filename + ".pub"):
filename += ".pub"
@@ -328,8 +328,8 @@ def _inputSaveFile(prompt: str) -> str:
def _saveKey(
key: keys.Key,
- options: Dict[Any, Any],
- inputCollector: Optional[Callable[[str], str]] = None,
+ options: dict[Any, Any],
+ inputCollector: Callable[[str], str] | None = None,
) -> None:
"""
Persist a SSH key on local filesystem.
diff --git a/contrib/python/Twisted/py3/twisted/conch/scripts/conch.py b/contrib/python/Twisted/py3/twisted/conch/scripts/conch.py
index 74e3a9f9fe0..a0caec95640 100644
--- a/contrib/python/Twisted/py3/twisted/conch/scripts/conch.py
+++ b/contrib/python/Twisted/py3/twisted/conch/scripts/conch.py
@@ -16,7 +16,7 @@ import signal
import struct
import sys
import tty
-from typing import Any, List, Tuple
+from typing import Any
from twisted.conch.client import connect, default
from twisted.conch.client.options import ConchOptions
@@ -73,8 +73,8 @@ class ClientOptions(ConchOptions):
],
)
- localForwards: List[Tuple[int, Tuple[int, int]]] = []
- remoteForwards: List[Tuple[int, Tuple[int, int]]] = []
+ localForwards: list[tuple[int, tuple[int, int]]] = []
+ remoteForwards: list[tuple[int, tuple[int, int]]] = []
def opt_escape(self, esc):
"""
diff --git a/contrib/python/Twisted/py3/twisted/conch/scripts/tkconch.py b/contrib/python/Twisted/py3/twisted/conch/scripts/tkconch.py
index e6738403daa..ffd47c5cc44 100644
--- a/contrib/python/Twisted/py3/twisted/conch/scripts/tkconch.py
+++ b/contrib/python/Twisted/py3/twisted/conch/scripts/tkconch.py
@@ -16,7 +16,6 @@ import sys
import tkinter as Tkinter
import tkinter.filedialog as tkFileDialog
import tkinter.messagebox as tkMessageBox
-from typing import List, Tuple
from twisted.conch import error
from twisted.conch.client.default import isInKnownHosts
@@ -278,9 +277,9 @@ class GeneralOptions(usage.Options):
],
)
- identitys: List[str] = []
- localForwards: List[Tuple[int, Tuple[int, int]]] = []
- remoteForwards: List[Tuple[int, Tuple[int, int]]] = []
+ identitys: list[str] = []
+ localForwards: list[tuple[int, tuple[int, int]]] = []
+ remoteForwards: list[tuple[int, tuple[int, int]]] = []
def opt_identity(self, i):
self.identitys.append(i)
@@ -506,7 +505,7 @@ class SSHClientTransport(transport.SSHClientTransport):
class SSHUserAuthClient(userauth.SSHUserAuthClient):
- usedFiles: List[str] = []
+ usedFiles: list[str] = []
def getPassword(self, prompt=None):
if not prompt:
diff --git a/contrib/python/Twisted/py3/twisted/conch/ssh/common.py b/contrib/python/Twisted/py3/twisted/conch/ssh/common.py
index 8d01ab14e50..7df80c7083f 100644
--- a/contrib/python/Twisted/py3/twisted/conch/ssh/common.py
+++ b/contrib/python/Twisted/py3/twisted/conch/ssh/common.py
@@ -8,7 +8,8 @@ Common functions for the SSH classes.
from __future__ import annotations
import struct
-from typing import Sequence, overload
+from collections.abc import Sequence
+from typing import overload
from cryptography.utils import int_to_bytes
diff --git a/contrib/python/Twisted/py3/twisted/conch/ssh/factory.py b/contrib/python/Twisted/py3/twisted/conch/ssh/factory.py
index da1c3a8f9ea..89b62d4d169 100644
--- a/contrib/python/Twisted/py3/twisted/conch/ssh/factory.py
+++ b/contrib/python/Twisted/py3/twisted/conch/ssh/factory.py
@@ -8,27 +8,34 @@ See also L{twisted.conch.openssh_compat.factory} for OpenSSH compatibility.
Maintainer: Paul Swartz
"""
-
+from __future__ import annotations
import random
from itertools import chain
-from typing import Dict, List, Optional, Tuple
+from typing import Any
from twisted.conch import error
from twisted.conch.ssh import _kex, connection, transport, userauth
from twisted.internet import protocol
+from twisted.internet.interfaces import IAddress
from twisted.logger import Logger
+# Note: type hint here is slightly wonky. Possibly a mypy bug? We ought to be
+# able to say SSHServerTransport here, but it doesn't conform to
+# _ProtoWithFactory. When we try to investigaate why, its factory attribute
+# doesn't supply Factory[SSHServerTransport]. But then when we try to figure
+# out its factory attribute, we're back here again...
+
-class SSHFactory(protocol.Factory):
+class SSHFactory(protocol.Factory[Any]):
"""
A Factory for SSH servers.
"""
- primes: Optional[Dict[int, List[Tuple[int, int]]]]
+ primes: dict[int, list[tuple[int, int]]] | None
_log = Logger()
- protocol = transport.SSHServerTransport
+ protocol: Any = transport.SSHServerTransport
services = {
b"ssh-userauth": userauth.SSHUserAuthServer,
@@ -48,7 +55,7 @@ class SSHFactory(protocol.Factory):
if not hasattr(self, "primes"):
self.primes = self.getPrimes()
- def buildProtocol(self, addr):
+ def buildProtocol(self, addr: IAddress | None) -> transport.SSHServerTransport:
"""
Create an instance of the server side of the SSH protocol.
@@ -58,7 +65,8 @@ class SSHFactory(protocol.Factory):
@rtype: L{twisted.conch.ssh.transport.SSHServerTransport}
@return: The built transport.
"""
- t = protocol.Factory.buildProtocol(self, addr)
+ t: transport.SSHServerTransport | None = super().buildProtocol(addr)
+ assert t is not None
t.supportedPublicKeys = list(
chain.from_iterable(
key.supportedSignatureAlgorithms() for key in self.privateKeys.values()
@@ -96,14 +104,14 @@ class SSHFactory(protocol.Factory):
"""
raise NotImplementedError("getPrivateKeys unimplemented")
- def getPrimes(self) -> Optional[Dict[int, List[Tuple[int, int]]]]:
+ def getPrimes(self) -> dict[int, list[tuple[int, int]]] | None:
"""
Called when the factory is started to get Diffie-Hellman generators and
primes to use. Returns a dictionary mapping number of bits to lists of
tuple of (generator, prime).
"""
- def getDHPrime(self, bits: int) -> Tuple[int, int]:
+ def getDHPrime(self, bits: int) -> tuple[int, int]:
"""
Return a tuple of (g, p) for a Diffe-Hellman process, with p being as
close to C{bits} bits as possible.
diff --git a/contrib/python/Twisted/py3/twisted/conch/ssh/filetransfer.py b/contrib/python/Twisted/py3/twisted/conch/ssh/filetransfer.py
index 830ff711ddd..9b0302d9d9e 100644
--- a/contrib/python/Twisted/py3/twisted/conch/ssh/filetransfer.py
+++ b/contrib/python/Twisted/py3/twisted/conch/ssh/filetransfer.py
@@ -8,7 +8,6 @@ import errno
import os
import struct
import warnings
-from typing import Dict
from zope.interface import implementer
@@ -25,7 +24,7 @@ class FileTransferBase(protocol.Protocol):
versions = (3,)
- packetTypes: Dict[int, str] = {}
+ packetTypes: dict[int, str] = {}
def __init__(self):
self.buf = b""
diff --git a/contrib/python/Twisted/py3/twisted/conch/ssh/keys.py b/contrib/python/Twisted/py3/twisted/conch/ssh/keys.py
index e52608df9aa..3d8c93cb0b4 100644
--- a/contrib/python/Twisted/py3/twisted/conch/ssh/keys.py
+++ b/contrib/python/Twisted/py3/twisted/conch/ssh/keys.py
@@ -13,7 +13,7 @@ import unicodedata
import warnings
from base64 import b64encode, decodebytes, encodebytes
from hashlib import md5, sha256
-from typing import Any
+from typing import Any, Literal
import bcrypt
from constantly import NamedConstant, Names
@@ -27,7 +27,6 @@ from cryptography.hazmat.primitives.serialization import (
load_pem_private_key,
load_ssh_public_key,
)
-from typing_extensions import Literal
from twisted.conch.ssh import common, sexpy
from twisted.conch.ssh.common import int_to_bytes
@@ -235,16 +234,32 @@ class Key:
integer y
The format of ECDSA-SHA2-* public key blob is::
+
string 'ecdsa-sha2-[identifier]'
integer x
integer y
- identifier is the standard NIST curve name.
+ Where 'identifier' is the standard NIST curve name.
The format of an Ed25519 public key blob is::
string 'ssh-ed25519'
string a
+ The format of a [email protected] public key is::
+
+ string curve name
+ ec_point Q
+ string application (user-specified, but typically "ssh:")
+
+ The format of a [email protected] public key is::
+
+ string public key
+ string application (user-specified, but typically "ssh:")
+
+ The security key formats are specified at https://github.com/openssh/openssh-portable/blob/80993390bed15bbd1c348f3352e55d0db01ca0fd/PROTOCOL.u2f.
+
@type blob: L{bytes}
@param blob: The key data.
@@ -273,18 +288,27 @@ class Key:
)
if keyType == b"[email protected]":
+ _, encodedPoint, application, rest = common.getNS(rest, 3)
keyObject = cls._fromECEncodedPoint(
- encodedPoint=common.getNS(rest, 2)[1],
+ encodedPoint=encodedPoint,
curve=b"ecdsa-sha2-nistp256",
)
keyObject._sk = True
+ keyObject.application = application
+ return keyObject
+
+ if keyType == b"[email protected]":
+ a, application, rest = common.getNS(rest, 2)
+ keyObject = cls._fromEd25519Components(a)
+ keyObject._sk = True
+ keyObject.application = application
+
return keyObject
- if keyType in [b"ssh-ed25519", b"[email protected]"]:
+ if keyType in [b"ssh-ed25519"]:
a, rest = common.getNS(rest)
keyObject = cls._fromEd25519Components(a)
- if keyType.startswith(b"sk-ssh-"):
- keyObject._sk = True
+
return keyObject
raise BadKeyError(f"unknown blob type: {keyType}")
@@ -904,7 +928,9 @@ class Key:
@type keyObject: C{cryptography.hazmat.primitives.asymmetric} key.
"""
self._keyObject = keyObject
+ # Only used for OpenSSH sk ssh keys
self._sk = False
+ self._application = None
def __eq__(self, other: object) -> bool:
"""
@@ -1219,17 +1245,19 @@ class Key:
def blob(self):
"""
- Return the public key blob for this key. The blob is the
- over-the-wire format for public keys.
+ Return the public key blob for this key. The blob is the over-the-wire
+ format for public keys.
SECSH-TRANS RFC 4253 Section 6.6.
RSA keys::
+
string 'ssh-rsa'
integer e
integer n
DSA keys::
+
string 'ssh-dss'
integer p
integer q
@@ -1237,16 +1265,34 @@ class Key:
integer y
EC keys::
+
string 'ecdsa-sha2-[identifier]'
integer x
integer y
- identifier is the standard NIST curve name
+ Where 'identifier' is the standard NIST curve name.
Ed25519 keys::
+
string 'ssh-ed25519'
string a
+
+ string curve name
+ ec_point Q
+ string application (user-specified, but typically "ssh:")
+
+
+ string public key
+ string application (user-specified, but typically "ssh:")
+
+ The security key formats are specified at
+ U{https://github.com/openssh/openssh-portable/blob/80993390bed15bbd1c348f3352e55d0db01ca0fd/PROTOCOL.u2f}.
+
@rtype: L{bytes}
"""
type = self.type()
@@ -1263,17 +1309,31 @@ class Key:
)
elif type == "EC":
byteLength = (self._keyObject.curve.key_size + 7) // 8
- return (
- common.NS(data["curve"])
- + common.NS(data["curve"][-8:])
+ curve = data["curve"][-8:]
+ if self._sk:
+ # We convert the curve name.
+ # The curve name is only `nistpNNN` part.
+ # Format example:
+ # "ecdsa-sha2-nistp256" -> "nistp256"
+ # "[email protected]" -> "nistp256"
+ curve = data["curve"][-20:-12]
+ blob = (
+ common.NS(self.sshType())
+ + common.NS(curve)
+ common.NS(
b"\x04"
+ utils.int_to_bytes(data["x"], byteLength)
+ utils.int_to_bytes(data["y"], byteLength)
)
)
+ if self._sk:
+ blob += common.NS(self.application)
+ return blob
elif type == "Ed25519":
- return common.NS(b"ssh-ed25519") + common.NS(data["a"])
+ blob = common.NS(self.sshType()) + common.NS(data["a"])
+ if self._sk:
+ blob += common.NS(self.application)
+ return blob
else:
raise BadKeyError(f"unknown key type: {type}")
@@ -1362,8 +1422,8 @@ class Key:
@_mutuallyExclusiveArguments(
[
- ["extra", "comment"],
- ["extra", "passphrase"],
+ ("extra", "comment"),
+ ("extra", "passphrase"),
]
)
def toString(self, type, extra=None, subtype=None, comment=None, passphrase=None):
@@ -1810,6 +1870,30 @@ class Key:
else:
return True
+ def isSecurityKey(self):
+ """
+ Return True if key is an OpenSSH security key.
+ """
+ return self.sshType() in [
+ ]
+
+ @property
+ def application(self):
+ """
+ Returns the application value for OpenSSH sk SSH keys
+ and None for other key types.
+ """
+ return self._application
+
+ @application.setter
+ def application(self, value):
+ """
+ Modifies the application value for OpenSSH sk SSH key types.
+ """
+ self._application = value
+
def _getPersistentRSAKey(location, keySize=4096):
"""
diff --git a/contrib/python/Twisted/py3/twisted/conch/ssh/service.py b/contrib/python/Twisted/py3/twisted/conch/ssh/service.py
index acfd40ee6a7..caa806c995c 100644
--- a/contrib/python/Twisted/py3/twisted/conch/ssh/service.py
+++ b/contrib/python/Twisted/py3/twisted/conch/ssh/service.py
@@ -9,7 +9,7 @@ Maintainer: Paul Swartz
"""
from __future__ import annotations
-from typing import TYPE_CHECKING, Dict
+from typing import TYPE_CHECKING
from twisted.logger import Logger
@@ -21,7 +21,7 @@ class SSHService:
# this is the ssh name for the service:
name: bytes = None # type:ignore[assignment]
- protocolMessages: Dict[int, str] = {} # map #'s -> protocol names
+ protocolMessages: dict[int, str] = {} # map #'s -> protocol names
transport: SSHTransportBase | None = None # gets set later
_log = Logger()
diff --git a/contrib/python/Twisted/py3/twisted/conch/ssh/transport.py b/contrib/python/Twisted/py3/twisted/conch/ssh/transport.py
index 323236f4720..2f3f31c2009 100644
--- a/contrib/python/Twisted/py3/twisted/conch/ssh/transport.py
+++ b/contrib/python/Twisted/py3/twisted/conch/ssh/transport.py
@@ -17,14 +17,19 @@ import struct
import types
import zlib
from hashlib import md5, sha1, sha256, sha384, sha512
-from typing import TYPE_CHECKING, Any, Callable, Dict, Tuple, Union
+from typing import TYPE_CHECKING, Any, Callable, Literal, Union
from cryptography.exceptions import UnsupportedAlgorithm
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import dh, ec, x25519
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
-from typing_extensions import Literal
+
+try:
+ from cryptography.hazmat.decrepit.ciphers.algorithms import TripleDES
+except ImportError:
+ # Deprecated path, will be removed in cryptography 48.0.0
+ from cryptography.hazmat.primitives.ciphers.algorithms import TripleDES
from twisted import __version__ as twisted_version
from twisted.conch.ssh import _kex, address, keys
@@ -62,7 +67,7 @@ _Hash = Any
_DigestMod = Union[str, Callable[[], _Hash], types.ModuleType]
-class _MACParams(Tuple[_DigestMod, bytes, bytes, int]):
+class _MACParams(tuple[_DigestMod, bytes, bytes, int]):
"""
L{_MACParams} represents the parameters necessary to compute SSH MAC
(Message Authenticate Codes).
@@ -107,14 +112,14 @@ class SSHCiphers:
"""
cipherMap = {
- b"3des-cbc": (algorithms.TripleDES, 24, modes.CBC),
+ b"3des-cbc": (TripleDES, 24, modes.CBC),
b"aes256-cbc": (algorithms.AES, 32, modes.CBC),
b"aes192-cbc": (algorithms.AES, 24, modes.CBC),
b"aes128-cbc": (algorithms.AES, 16, modes.CBC),
b"aes128-ctr": (algorithms.AES, 16, modes.CTR),
b"aes192-ctr": (algorithms.AES, 24, modes.CTR),
b"aes256-ctr": (algorithms.AES, 32, modes.CTR),
- b"3des-ctr": (algorithms.TripleDES, 24, modes.CTR),
+ b"3des-ctr": (TripleDES, 24, modes.CTR),
b"none": (None, 0, modes.CBC),
}
macMap = {
@@ -509,9 +514,7 @@ class SSHTransportBase(protocol.Protocol):
_EXT_INFO_S = b"ext-info-s"
_peerSupportsExtensions = False
- peerExtensions: Dict[bytes, bytes] = {}
-
- factory: SSHFactory
+ peerExtensions: dict[bytes, bytes] = {}
# Set by twisted.conch.ssh.userauth.SSHUserAuthServer._cbFinishedAuth
avatar: object
@@ -1443,6 +1446,7 @@ class SSHServerTransport(SSHTransportBase):
@ivar p: the Diffie-Hellman group prime.
"""
+ factory: SSHFactory
isClient = False
ignoreNextPacket = 0
@@ -1907,7 +1911,7 @@ class SSHClientTransport(SSHTransportBase):
d.addErrback(
lambda unused: self.sendDisconnect(
DISCONNECT_HOST_KEY_NOT_VERIFIABLE,
- f"bad host key [ecdh] {unused}".encode("utf-8"),
+ f"bad host key [ecdh] {unused}".encode(),
)
)
return d
diff --git a/contrib/python/Twisted/py3/twisted/conch/ssh/userauth.py b/contrib/python/Twisted/py3/twisted/conch/ssh/userauth.py
index 0d24df00f92..369550fb13f 100644
--- a/contrib/python/Twisted/py3/twisted/conch/ssh/userauth.py
+++ b/contrib/python/Twisted/py3/twisted/conch/ssh/userauth.py
@@ -11,7 +11,8 @@ Maintainer: Paul Swartz
from __future__ import annotations
import struct
-from typing import Callable, Tuple, Type
+from hashlib import sha256
+from typing import Callable
from twisted.conch import error, interfaces
from twisted.conch.ssh import keys, service, transport
@@ -26,8 +27,8 @@ from twisted.logger import Logger
from twisted.python import failure
from twisted.python.compat import nativeString
-_ConchPortalTuple = Tuple[
- Type[interfaces.IConchUser], interfaces.IConchUser, Callable[[], None]
+_ConchPortalTuple = tuple[
+ type[interfaces.IConchUser], interfaces.IConchUser, Callable[[], None]
]
@@ -277,7 +278,7 @@ class SSHUserAuthServer(service.SSHService):
result: Deferred[_ConchPortalTuple]
try:
- keys.Key.fromString(blob)
+ pubKey = keys.Key.fromString(blob)
except keys.BadKeyError:
error = "Unsupported key type {} or bad key".format(algName.decode("ascii"))
self._log.error(error)
@@ -287,7 +288,7 @@ class SSHUserAuthServer(service.SSHService):
if hasSig:
assert self.transport is not None, "must have transport for auth"
assert self.transport.sessionID is not None, "must have session for auth"
- b = (
+ messageToBeValidated = (
NS(self.transport.sessionID)
+ bytes((MSG_USERAUTH_REQUEST,))
+ NS(self.user)
@@ -297,7 +298,18 @@ class SSHUserAuthServer(service.SSHService):
+ NS(algName)
+ NS(blob)
)
- c = credentials.SSHPrivateKey(self.user, algName, blob, b, signature)
+ if pubKey.isSecurityKey():
+ try:
+ application = pubKey.application
+ messageToBeValidated = self._wrapMessageWithSKMetadata(
+ signature, messageToBeValidated, application
+ )
+ except ValueError:
+ return defer.fail(UnauthorizedLogin("Invalid security key format"))
+
+ c = credentials.SSHPrivateKey(
+ self.user, algName, blob, messageToBeValidated, signature
+ )
result = self.portal.login(c, None, interfaces.IConchUser)
else:
c = credentials.SSHPrivateKey(self.user, algName, blob, None, None)
@@ -306,6 +318,43 @@ class SSHUserAuthServer(service.SSHService):
)
return result
+ @staticmethod
+ def _wrapMessageWithSKMetadata(signature, messageToBeValidated, application):
+ """
+ Wraps the SSH user auth message request with security key information.
+ This blob will be verified against the signature. See
+ U{https://github.com/openssh/openssh-portable/blob/a4aa090a3d40dddb07d5ebebc501f6457541a501/PROTOCOL.u2f#L176}
+
+ In addition to the message to be signed, the U2F signature operation
+ requires the key handle and a few additional parameters. The signature
+ is signed over a blob that consists of::
+
+ byte[32] SHA256(application)
+ byte flags (including "user present", extensions present)
+ uint32 counter
+ byte[] extensions
+ byte[32] SHA256(message)
+
+ The signature format used on the wire in SSH2_USERAUTH_REQUEST::
+
+ string signature
+ byte flags
+ uint32 counter
+ """
+ _, _, trailing = getNS(signature, 2)
+ if len(trailing) < 5:
+ raise ValueError("SK signature missing flags+counter")
+ flags = trailing[0:1]
+ counter = trailing[1:5]
+
+ return (
+ sha256(application).digest()
+ + flags
+ + counter
+ + sha256(messageToBeValidated).digest()
+ )
+
def _ebCheckKey(self, reason: failure.Failure, packet: bytes) -> failure.Failure:
"""
Called back if the user did not sent a signature. If reason is
diff --git a/contrib/python/Twisted/py3/twisted/conch/unix.py b/contrib/python/Twisted/py3/twisted/conch/unix.py
index 0696aeb1ac6..e8e44f57a57 100644
--- a/contrib/python/Twisted/py3/twisted/conch/unix.py
+++ b/contrib/python/Twisted/py3/twisted/conch/unix.py
@@ -16,7 +16,7 @@ import socket
import struct
import time
import tty
-from typing import Callable, Dict, Tuple
+from typing import Callable
from zope.interface import implementer
@@ -52,10 +52,10 @@ except ImportError:
class UnixSSHRealm:
def requestAvatar(
self,
- username: bytes | Tuple[()],
+ username: bytes | tuple[()],
mind: object,
*interfaces: portal._InterfaceItself,
- ) -> Tuple[portal._InterfaceItself, UnixConchUser, Callable[[], None]]:
+ ) -> tuple[portal._InterfaceItself, UnixConchUser, Callable[[], None]]:
if not isinstance(username, bytes):
raise LoginDenied("UNIX SSH realm does not authorize anonymous sessions.")
user = UnixConchUser(username.decode())
@@ -72,7 +72,7 @@ class UnixConchUser(ConchUser):
if username in userlist:
l.append(gid)
self.otherGroups = l
- self.listeners: Dict[
+ self.listeners: dict[
str, IListeningPort
] = {} # Dict mapping (interface, port) -> listener
self.channelLookup.update(
diff --git a/contrib/python/Twisted/py3/twisted/copyright.py b/contrib/python/Twisted/py3/twisted/copyright.py
index 8a21533ba74..a28262236b0 100644
--- a/contrib/python/Twisted/py3/twisted/copyright.py
+++ b/contrib/python/Twisted/py3/twisted/copyright.py
@@ -13,7 +13,7 @@ from twisted import __version__ as version, version as _longversion
longversion = str(_longversion)
copyright = """\
-Copyright (c) 2001-2025 Twisted Matrix Laboratories.
+Copyright (c) 2001-2026 Twisted Matrix Laboratories.
See LICENSE for details."""
disclaimer = """
diff --git a/contrib/python/Twisted/py3/twisted/cred/checkers.py b/contrib/python/Twisted/py3/twisted/cred/checkers.py
index 24dd40ff173..21697af6acc 100644
--- a/contrib/python/Twisted/py3/twisted/cred/checkers.py
+++ b/contrib/python/Twisted/py3/twisted/cred/checkers.py
@@ -7,10 +7,10 @@ Basic credential checkers
@var ANONYMOUS: An empty tuple used to represent the anonymous avatar ID.
"""
-
+from __future__ import annotations
import os
-from typing import Any, Dict, Optional, Tuple, Union
+from typing import Any
from zope.interface import Attribute, Interface, implementer
@@ -35,7 +35,7 @@ from twisted.python import failure
# username. We do not want an instance of 'object', because that would
# create potential problems with persistence.
-ANONYMOUS: Tuple[()] = ()
+ANONYMOUS: tuple[()] = ()
class ICredentialsChecker(Interface):
@@ -48,7 +48,7 @@ class ICredentialsChecker(Interface):
"may check."
)
- def requestAvatarId(credentials: Any) -> Deferred[Union[bytes, Tuple[()]]]:
+ def requestAvatarId(credentials: Any) -> Deferred[bytes | tuple[()]]:
"""
Validate credentials and produce an avatar ID.
@@ -168,7 +168,7 @@ class FilePasswordDB:
"""
cache = False
- _credCache: Optional[Dict[bytes, bytes]] = None
+ _credCache: dict[bytes, bytes] | None = None
_cacheTimestamp: float = 0
_log = Logger()
@@ -277,7 +277,7 @@ class FilePasswordDB:
self._log.error("Unable to load credentials db: {e!r}", e=e)
raise error.UnauthorizedLogin()
- def getUser(self, username: bytes) -> Tuple[bytes, bytes]:
+ def getUser(self, username: bytes) -> tuple[bytes, bytes]:
"""
Look up the credentials for a username.
@@ -310,7 +310,7 @@ class FilePasswordDB:
def requestAvatarId(
self, credentials: IUsernamePassword
- ) -> Deferred[Union[bytes, Tuple[()]]]:
+ ) -> Deferred[bytes | tuple[()]]:
try:
u, p = self.getUser(credentials.username)
except KeyError:
diff --git a/contrib/python/Twisted/py3/twisted/cred/credentials.py b/contrib/python/Twisted/py3/twisted/cred/credentials.py
index b2e9013c09b..012be122492 100644
--- a/contrib/python/Twisted/py3/twisted/cred/credentials.py
+++ b/contrib/python/Twisted/py3/twisted/cred/credentials.py
@@ -16,6 +16,7 @@ import re
import time
from binascii import hexlify
from hashlib import md5
+from typing import TYPE_CHECKING
from zope.interface import Attribute, Interface, implementer
@@ -64,6 +65,18 @@ class IUsernameHashedPassword(ICredentials):
appropriate for the particular credentials class.
"""
+ if not TYPE_CHECKING: # pragma: no branch
+
+ def __init__(self) -> None: # type:ignore
+ """
+ IUsernameHashedPassword does not have any particular requirement
+ upon its constructor.
+ """
+ # This is a workaround for pydoctor bug
+ # https://github.com/twisted/pydoctor/issues/940
+
+ del __init__
+
username: bytes = Attribute(
"""
The username associated with these credentials.
diff --git a/contrib/python/Twisted/py3/twisted/cred/portal.py b/contrib/python/Twisted/py3/twisted/cred/portal.py
index 3e5abde918d..1f6d0b3e239 100644
--- a/contrib/python/Twisted/py3/twisted/cred/portal.py
+++ b/contrib/python/Twisted/py3/twisted/cred/portal.py
@@ -6,9 +6,10 @@
"""
The point of integration of application and authentication.
"""
+from __future__ import annotations
-
-from typing import Callable, Dict, Iterable, List, Tuple, Type, Union
+from collections.abc import Iterable
+from typing import Callable
from zope.interface import Interface, providedBy
@@ -24,12 +25,12 @@ from twisted.python import failure, reflect
# implementation of Interface itself (subclassing it actually instantiates it),
# since mypy-zope treats Interface objects *as* types, this is how you have to
# treat it.
-_InterfaceItself = Type[Interface]
+_InterfaceItself = type[Interface]
# This is the result shape for both IRealm.requestAvatar and Portal.login,
# although the former is optionally allowed to return synchronously and the
# latter must be Deferred.
-_requestResult = Tuple[_InterfaceItself, object, Callable[[], None]]
+_requestResult = tuple[_InterfaceItself, object, Callable[[], None]]
class IRealm(Interface):
@@ -39,8 +40,8 @@ class IRealm(Interface):
"""
def requestAvatar(
- avatarId: Union[bytes, Tuple[()]], mind: object, *interfaces: _InterfaceItself
- ) -> Union[Deferred[_requestResult], _requestResult]:
+ avatarId: bytes | tuple[()], mind: object, *interfaces: _InterfaceItself
+ ) -> Deferred[_requestResult] | _requestResult:
"""
Return avatar which provides one of the given interfaces.
@@ -75,7 +76,7 @@ class Portal:
in the realm object and in the credentials checker objects.
"""
- checkers: Dict[Type[Interface], ICredentialsChecker]
+ checkers: dict[type[Interface], ICredentialsChecker]
def __init__(
self, realm: IRealm, checkers: Iterable[ICredentialsChecker] = ()
@@ -88,14 +89,14 @@ class Portal:
for checker in checkers:
self.registerChecker(checker)
- def listCredentialsInterfaces(self) -> List[Type[Interface]]:
+ def listCredentialsInterfaces(self) -> list[type[Interface]]:
"""
Return list of credentials interfaces that can be used to login.
"""
return list(self.checkers.keys())
def registerChecker(
- self, checker: ICredentialsChecker, *credentialInterfaces: Type[Interface]
+ self, checker: ICredentialsChecker, *credentialInterfaces: type[Interface]
) -> None:
if not credentialInterfaces:
credentialInterfaces = checker.credentialInterfaces
@@ -103,7 +104,7 @@ class Portal:
self.checkers[credentialInterface] = checker
def login(
- self, credentials: ICredentials, mind: object, *interfaces: Type[Interface]
+ self, credentials: ICredentials, mind: object, *interfaces: type[Interface]
) -> Deferred[_requestResult]:
"""
@param credentials: an implementor of
diff --git a/contrib/python/Twisted/py3/twisted/cred/strcred.py b/contrib/python/Twisted/py3/twisted/cred/strcred.py
index 574cd8e38e5..1330e47678e 100644
--- a/contrib/python/Twisted/py3/twisted/cred/strcred.py
+++ b/contrib/python/Twisted/py3/twisted/cred/strcred.py
@@ -13,10 +13,10 @@ Examples:
- memory:admin:asdf:user:lkj
- unix
"""
-
+from __future__ import annotations
import sys
-from typing import Optional, Sequence, Type
+from collections.abc import Sequence
from zope.interface import Attribute, Interface
@@ -142,7 +142,7 @@ class AuthOptionMixin:
will send all help-related output. Default: L{sys.stdout}
"""
- supportedInterfaces: Optional[Sequence[Type[Interface]]] = None
+ supportedInterfaces: Sequence[type[Interface]] | None = None
authOutput = sys.stdout
def supportsInterface(self, interface):
diff --git a/contrib/python/Twisted/py3/twisted/internet/__init__.py b/contrib/python/Twisted/py3/twisted/internet/__init__.py
index a3d851d1983..71a01f2afde 100644
--- a/contrib/python/Twisted/py3/twisted/internet/__init__.py
+++ b/contrib/python/Twisted/py3/twisted/internet/__init__.py
@@ -10,3 +10,30 @@ observers need not care about which event loop is running. Thus, it is possible
to use the same code for different loops, from Twisted's basic, yet portable,
select-based loop to the loops of various GUI toolkits like GTK+ or Tk.
"""
+
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+ from .interfaces import (
+ IReactorCore,
+ IReactorFDSet,
+ IReactorProcess,
+ IReactorTCP,
+ IReactorTime,
+ )
+
+ class _IReactorCommon(
+ IReactorCore,
+ IReactorTime,
+ IReactorFDSet,
+ IReactorTCP,
+ IReactorProcess,
+ ):
+ """
+ A make-believe interface supporting all the commonly-implemented parts
+ of the reactor. For other interfaces which may or may not be supplied
+ by the environment, i.e. IReactorSSL, adapt the reactor like
+ C{IReactorSSL(reactor)}.
+ """
+
+ reactor: _IReactorCommon
diff --git a/contrib/python/Twisted/py3/twisted/internet/_glibbase.py b/contrib/python/Twisted/py3/twisted/internet/_glibbase.py
index 587ec1bd05c..650fdc90dba 100644
--- a/contrib/python/Twisted/py3/twisted/internet/_glibbase.py
+++ b/contrib/python/Twisted/py3/twisted/internet/_glibbase.py
@@ -13,7 +13,7 @@ or glib2reactor or gtk2reactor for applications using legacy static bindings.
import sys
-from typing import Any, Callable, Dict, Set
+from typing import Any, Callable
from zope.interface import implementer
@@ -100,23 +100,23 @@ class GlibReactorBase(posixbase.PosixReactorBase, posixbase._PollLikeMixin):
Base class for GObject event loop reactors.
Notification for I/O events (reads and writes on file descriptors) is done
- by the gobject-based event loop. File descriptors are registered with
+ by the gobject-based event loop. File descriptors are registered with
gobject with the appropriate flags for read/write/disconnect notification.
Time-based events, the results of C{callLater} and C{callFromThread}, are
- handled differently. Rather than registering each event with gobject, a
+ handled differently. Rather than registering each event with gobject, a
single gobject timeout is registered for the earliest scheduled event, the
- output of C{reactor.timeout()}. For example, if there are timeouts in 1, 2
- and 3.4 seconds, a single timeout is registered for 1 second in the
- future. When this timeout is hit, C{_simulate} is called, which calls the
+ output of C{reactor.timeout()}. For example, if there are timeouts in 1, 2
+ and 3.4 seconds, a single timeout is registered for 1 second in the future.
+ When this timeout is hit, C{_simulate} is called, which calls the
appropriate Twisted-level handlers, and a new timeout is added to gobject
by the C{_reschedule} method.
To handle C{callFromThread} events, we use a custom waker that calls
C{_simulate} whenever it wakes up.
- @ivar _sources: A dictionary mapping L{FileDescriptor} instances to
- GSource handles.
+ @ivar _sources: A dictionary mapping L{FileDescriptor} instances to GSource
+ handles.
@ivar _reads: A set of L{FileDescriptor} instances currently monitored for
reading.
@@ -124,7 +124,8 @@ class GlibReactorBase(posixbase.PosixReactorBase, posixbase._PollLikeMixin):
@ivar _writes: A set of L{FileDescriptor} instances currently monitored for
writing.
- @ivar _simtag: A GSource handle for the next L{simulate} call.
+ @ivar _simtag: A GSource handle for the next L{GlibReactorBase._simulate}
+ call.
"""
# Install a waker that knows it needs to call C{_simulate} in order to run
@@ -134,9 +135,9 @@ class GlibReactorBase(posixbase.PosixReactorBase, posixbase._PollLikeMixin):
def __init__(self, glib_module: Any, gtk_module: Any, useGtk: bool = False) -> None:
self._simtag = None
- self._reads: Set[IReadDescriptor] = set()
- self._writes: Set[IWriteDescriptor] = set()
- self._sources: Dict[FileDescriptor, int] = {}
+ self._reads: set[IReadDescriptor] = set()
+ self._writes: set[IWriteDescriptor] = set()
+ self._sources: dict[FileDescriptor, int] = {}
self._glib = glib_module
self._POLL_DISCONNECTED = (
diff --git a/contrib/python/Twisted/py3/twisted/internet/_posixstdio.py b/contrib/python/Twisted/py3/twisted/internet/_posixstdio.py
index e99920ead3f..8d6c0b73f34 100644
--- a/contrib/python/Twisted/py3/twisted/internet/_posixstdio.py
+++ b/contrib/python/Twisted/py3/twisted/internet/_posixstdio.py
@@ -47,7 +47,7 @@ class StandardIO:
reactor: IReactorFDSet | None = None,
):
if reactor is None:
- from twisted.internet import reactor # type:ignore[assignment]
+ from twisted.internet import reactor
self.protocol: IProtocol = proto
self._writer = process.ProcessWriter(reactor, self, "write", stdout)
diff --git a/contrib/python/Twisted/py3/twisted/internet/_producer_helpers.py b/contrib/python/Twisted/py3/twisted/internet/_producer_helpers.py
index 7583a9e4594..f7f3103f60f 100644
--- a/contrib/python/Twisted/py3/twisted/internet/_producer_helpers.py
+++ b/contrib/python/Twisted/py3/twisted/internet/_producer_helpers.py
@@ -6,8 +6,6 @@
Helpers for working with producers.
"""
-from typing import List
-
from zope.interface import implementer
from twisted.internet.interfaces import IPushProducer
@@ -17,7 +15,7 @@ from twisted.logger import Logger
_log = Logger()
# This module exports nothing public, it's for internal Twisted use only.
-__all__: List[str] = []
+__all__: list[str] = []
@implementer(IPushProducer)
diff --git a/contrib/python/Twisted/py3/twisted/internet/_resolver.py b/contrib/python/Twisted/py3/twisted/internet/_resolver.py
index f4a56b4808f..98eb4ceadb3 100644
--- a/contrib/python/Twisted/py3/twisted/internet/_resolver.py
+++ b/contrib/python/Twisted/py3/twisted/internet/_resolver.py
@@ -7,8 +7,9 @@ IPv6-aware hostname resolution.
@see: L{IHostnameResolver}
"""
+from __future__ import annotations
-
+from collections.abc import Sequence
from socket import (
AF_INET,
AF_INET6,
@@ -20,17 +21,7 @@ from socket import (
gaierror,
getaddrinfo,
)
-from typing import (
- TYPE_CHECKING,
- Callable,
- List,
- NoReturn,
- Optional,
- Sequence,
- Tuple,
- Type,
- Union,
-)
+from typing import TYPE_CHECKING, Callable, NoReturn, Protocol
from zope.interface import implementer
@@ -95,15 +86,29 @@ _socktypeToType = {
}
-_GETADDRINFO_RESULT = List[
- Tuple[
- AddressFamily,
- SocketKind,
- int,
- str,
- Union[Tuple[str, int], Tuple[str, int, int, int]],
- ]
-]
+class _LikeGetAddrInfo(Protocol):
+ """
+ A callable matching the type signature of L{getaddrinfo}.
+ """
+
+ def __call__(
+ self,
+ host: bytes | str | None,
+ port: bytes | str | int | None,
+ family: int = AF_UNSPEC,
+ type: int = 0,
+ proto: int = 0,
+ flags: int = 0,
+ ) -> list[
+ tuple[
+ AddressFamily,
+ SocketKind,
+ int,
+ str,
+ tuple[str, int] | tuple[str, int, int, int] | tuple[int, bytes],
+ ]
+ ]:
+ ...
@implementer(IHostnameResolver)
@@ -116,8 +121,8 @@ class GAIResolver:
def __init__(
self,
reactor: IReactorThreads,
- getThreadPool: Optional[Callable[[], "ThreadPool"]] = None,
- getaddrinfo: Callable[[str, int, int, int], _GETADDRINFO_RESULT] = getaddrinfo,
+ getThreadPool: Callable[[], ThreadPool] | None = None,
+ getaddrinfo: _LikeGetAddrInfo = getaddrinfo,
):
"""
Create a L{GAIResolver}.
@@ -146,7 +151,7 @@ class GAIResolver:
resolutionReceiver: IResolutionReceiver,
hostName: str,
portNumber: int = 0,
- addressTypes: Optional[Sequence[Type[IAddress]]] = None,
+ addressTypes: Sequence[type[IAddress]] | None = None,
transportSemantics: str = "TCP",
) -> IHostResolution:
"""
@@ -170,27 +175,30 @@ class GAIResolver:
]
socketType = _transportToSocket[transportSemantics]
- def get() -> _GETADDRINFO_RESULT:
+ resolution = HostResolution(hostName)
+
+ async def resolveAndProcess() -> None:
+ resolutionReceiver.resolutionBegan(resolution)
try:
- return self._getaddrinfo(
- hostName, portNumber, addressFamily, socketType
+ names = await deferToThreadPool(
+ self._reactor,
+ pool,
+ self._getaddrinfo,
+ hostName,
+ portNumber,
+ addressFamily,
+ socketType,
)
except gaierror:
- return []
-
- d = deferToThreadPool(self._reactor, pool, get)
- resolution = HostResolution(hostName)
- resolutionReceiver.resolutionBegan(resolution)
-
- @d.addCallback
- def deliverResults(result: _GETADDRINFO_RESULT) -> None:
- for family, socktype, proto, cannoname, sockaddr in result:
+ names = []
+ for family, socktype, proto, cannoname, sockaddr in names:
addrType = _afToType[family]
resolutionReceiver.addressResolved(
addrType(_socktypeToType.get(socktype, "TCP"), *sockaddr)
)
resolutionReceiver.resolutionComplete()
+ Deferred.fromCoroutine(resolveAndProcess())
return resolution
@@ -213,7 +221,7 @@ class SimpleResolverComplexifier:
resolutionReceiver: IResolutionReceiver,
hostName: str,
portNumber: int = 0,
- addressTypes: Optional[Sequence[Type[IAddress]]] = None,
+ addressTypes: Sequence[type[IAddress]] | None = None,
transportSemantics: str = "TCP",
) -> IHostResolution:
"""
@@ -254,13 +262,15 @@ class SimpleResolverComplexifier:
)
)
.addErrback(
- lambda error: None
- if error.check(DNSLookupError)
- else self._log.failure(
- "while looking up {name} with {resolver}",
- error,
- name=hostName,
- resolver=self._simpleResolver,
+ lambda error: (
+ None
+ if error.check(DNSLookupError)
+ else self._log.failure(
+ "while looking up {name} with {resolver}",
+ error,
+ name=hostName,
+ resolver=self._simpleResolver,
+ )
)
)
.addCallback(lambda nothing: resolutionReceiver.resolutionComplete())
@@ -274,7 +284,7 @@ class FirstOneWins:
An L{IResolutionReceiver} which fires a L{Deferred} with its first result.
"""
- def __init__(self, deferred: "Deferred[str]"):
+ def __init__(self, deferred: Deferred[str]):
"""
@param deferred: The L{Deferred} to fire when the first resolution
result arrives.
@@ -327,7 +337,7 @@ class ComplexResolverSimplifier:
"""
self._nameResolver = nameResolver
- def getHostByName(self, name: str, timeouts: Sequence[int] = ()) -> "Deferred[str]":
+ def getHostByName(self, name: str, timeouts: Sequence[int] = ()) -> Deferred[str]:
"""
See L{IResolverSimple.getHostByName}
@@ -337,6 +347,6 @@ class ComplexResolverSimplifier:
@return: see L{IResolverSimple.getHostByName}
"""
- result: "Deferred[str]" = Deferred()
+ result: Deferred[str] = Deferred()
self._nameResolver.resolveHostName(FirstOneWins(result), name, 0, [IPv4Address])
return result
diff --git a/contrib/python/Twisted/py3/twisted/internet/_service_identity.py b/contrib/python/Twisted/py3/twisted/internet/_service_identity.py
new file mode 100644
index 00000000000..a311fbed72c
--- /dev/null
+++ b/contrib/python/Twisted/py3/twisted/internet/_service_identity.py
@@ -0,0 +1,42 @@
+"""
+Conditional imports to enable support for the C{service_identity} module back
+to version 18.1.0.
+"""
+
+# imported for the benefit of pydoctor
+import service_identity
+
+VerificationError = service_identity.VerificationError
+
+try:
+ __import__("service_identity.hazmat")
+except ImportError:
+ from sys import modules
+
+ for _oldAlias in "common", "_common":
+ try:
+ import service_identity
+
+ service_identity.hazmat = modules["service_identity.hazmat"] = getattr(
+ __import__(f"service_identity.{_oldAlias}"), _oldAlias
+ )
+ except ImportError:
+ pass
+from service_identity.hazmat import DNS_ID, IPAddress_ID, verify_service_identity
+
+try:
+ from service_identity.hazmat import ServiceID
+except ImportError:
+ ServiceID = object # type:ignore[assignment,misc]
+try:
+ from service_identity.pyopenssl import extract_patterns
+except ImportError:
+ from service_identity.pyopenssl import extract_ids as extract_patterns
+__all__ = [
+ "DNS_ID",
+ "IPAddress_ID",
+ "extract_patterns",
+ "ServiceID",
+ "VerificationError",
+ "verify_service_identity",
+]
diff --git a/contrib/python/Twisted/py3/twisted/internet/_signals.py b/contrib/python/Twisted/py3/twisted/internet/_signals.py
index 18793bfbba2..62856f01291 100644
--- a/contrib/python/Twisted/py3/twisted/internet/_signals.py
+++ b/contrib/python/Twisted/py3/twisted/internet/_signals.py
@@ -38,13 +38,14 @@ import errno
import os
import signal
import socket
+from collections.abc import Sequence
from types import FrameType
-from typing import Callable, Optional, Sequence
+from typing import Callable, Optional, Protocol
from zope.interface import Attribute, Interface, implementer
from attrs import define, frozen
-from typing_extensions import Protocol, TypeAlias
+from typing_extensions import TypeAlias
from twisted.internet.interfaces import IReadDescriptor
from twisted.python import failure, log, util
@@ -213,7 +214,7 @@ class _ChildSignalHandling:
_addInternalReader: Callable[[IReadDescriptor], object]
_removeInternalReader: Callable[[IReadDescriptor], object]
- _childWaker: Optional[_SIGCHLDWaker] = None
+ _childWaker: _SIGCHLDWaker | None = None
def install(self) -> None:
"""
diff --git a/contrib/python/Twisted/py3/twisted/internet/_sslverify.py b/contrib/python/Twisted/py3/twisted/internet/_sslverify.py
index 2095fe69eca..c6dabf6c4c4 100644
--- a/contrib/python/Twisted/py3/twisted/internet/_sslverify.py
+++ b/contrib/python/Twisted/py3/twisted/internet/_sslverify.py
@@ -6,14 +6,17 @@ from __future__ import annotations
import warnings
from binascii import hexlify
+from collections.abc import Sequence
from functools import lru_cache
from hashlib import md5
-from typing import Dict
+from typing import TYPE_CHECKING, Any, Callable, TypeVar
from zope.interface import Interface, implementer
from OpenSSL import SSL, crypto
from OpenSSL._util import lib as pyOpenSSLlib
+from OpenSSL.crypto import X509, PKey
+from OpenSSL.SSL import VERIFY_FAIL_IF_NO_PEER_CERT, VERIFY_PEER, Connection
import attr
from constantly import FlagConstant, Flags, NamedConstant, Names
@@ -27,6 +30,8 @@ from twisted.internet.interfaces import (
ICipher,
IOpenSSLClientConnectionCreator,
IOpenSSLContextFactory,
+ IOpenSSLServerConnectionCreator,
+ IProtocolNegotiationFactory,
)
from twisted.logger import Logger
from twisted.python.compat import nativeString
@@ -35,6 +40,17 @@ from twisted.python.failure import Failure
from twisted.python.randbytes import secureRandom
from twisted.python.util import nameToLabel
from ._idna import _idnaBytes
+from ._service_identity import (
+ DNS_ID,
+ IPAddress_ID,
+ ServiceID,
+ VerificationError,
+ extract_patterns,
+ verify_service_identity,
+)
+
+if TYPE_CHECKING:
+ from twisted.protocols.tls import TLSMemoryBIOProtocol
_log = Logger()
@@ -79,121 +95,24 @@ def _getExcludedTLSProtocols(oldest, newest):
@rtype: L{list} of L{TLSVersion} constants.
"""
versions = list(TLSVersion.iterconstants())
- excludedVersions = [x for x in versions[: versions.index(oldest)]]
-
- if newest:
- excludedVersions.extend([x for x in versions[versions.index(newest) :]])
-
+ excludedOlder = versions[: versions.index(oldest)]
+ excludedNewer = versions[versions.index(newest) + 1 :] if newest else []
+ excludedVersions = excludedOlder + excludedNewer
return excludedVersions
-class SimpleVerificationError(Exception):
- """
- Not a very useful verification error.
- """
-
-
-def simpleVerifyHostname(connection, hostname):
- """
- Check only the common name in the certificate presented by the peer and
- only for an exact match.
-
- This is to provide I{something} in the way of hostname verification to
- users who haven't installed C{service_identity}. This check is overly
- strict, relies on a deprecated TLS feature (you're supposed to ignore the
- commonName if the subjectAlternativeName extensions are present, I
- believe), and lots of valid certificates will fail.
-
- @param connection: the OpenSSL connection to verify.
- @type connection: L{OpenSSL.SSL.Connection}
-
- @param hostname: The hostname expected by the user.
- @type hostname: L{unicode}
-
- @raise twisted.internet.ssl.VerificationError: if the common name and
- hostname don't match.
- """
- commonName = connection.get_peer_certificate().get_subject().commonName
- if commonName != hostname:
- raise SimpleVerificationError(repr(commonName) + "!=" + repr(hostname))
-
-
-def simpleVerifyIPAddress(connection, hostname):
- """
- Always fails validation of IP addresses
-
- @param connection: the OpenSSL connection to verify.
- @type connection: L{OpenSSL.SSL.Connection}
-
- @param hostname: The hostname expected by the user.
- @type hostname: L{unicode}
-
- @raise twisted.internet.ssl.VerificationError: Always raised
- """
- raise SimpleVerificationError("Cannot verify certificate IP addresses")
-
-
-def _usablePyOpenSSL(version):
- """
- Check pyOpenSSL version string whether we can use it for host verification.
-
- @param version: A pyOpenSSL version string.
- @type version: L{str}
-
- @rtype: L{bool}
- """
- major, minor = (int(part) for part in version.split(".")[:2])
- return (major, minor) >= (0, 12)
-
-
-def _selectVerifyImplementation():
- """
- Determine if C{service_identity} is installed. If so, use it. If not, use
- simplistic and incorrect checking as implemented in
- L{simpleVerifyHostname}.
-
- @return: 2-tuple of (C{verify_hostname}, C{VerificationError})
- @rtype: L{tuple}
- """
-
- whatsWrong = (
- "Without the service_identity module, Twisted can perform only "
- "rudimentary TLS client hostname verification. Many valid "
- "certificate/hostname mappings may be rejected."
- )
-
- try:
- from service_identity import VerificationError
- from service_identity.pyopenssl import verify_hostname, verify_ip_address
-
- return verify_hostname, verify_ip_address, VerificationError
- except ImportError as e:
- warnings.warn_explicit(
- "You do not have a working installation of the "
- "service_identity module: '" + str(e) + "'. "
- "Please install it from "
- "<https://pypi.python.org/pypi/service_identity> and make "
- "sure all of its dependencies are satisfied. " + whatsWrong,
- # Unfortunately the lineno is required.
- category=UserWarning,
- filename="",
- lineno=0,
- )
-
- return simpleVerifyHostname, simpleVerifyIPAddress, SimpleVerificationError
-
-
-verifyHostname, verifyIPAddress, VerificationError = _selectVerifyImplementation()
-
-
class ProtocolNegotiationSupport(Flags):
"""
L{ProtocolNegotiationSupport} defines flags which are used to indicate the
- level of NPN/ALPN support provided by the TLS backend.
+ level of ALPN support provided by the TLS backend.
+
+ @cvar NOSUPPORT: There is no support for ALPN. This is exclusive with
+ L{ALPN}.
+
+ @cvar NPN: The implementation supports Next Protocol Negotiation. (This
+ flag is provided for compatibility only; Twisted no longer supports
+ Next Protocol Negotiation)
- @cvar NOSUPPORT: There is no support for NPN or ALPN. This is exclusive
- with both L{NPN} and L{ALPN}.
- @cvar NPN: The implementation supports Next Protocol Negotiation.
@cvar ALPN: The implementation supports Application Layer Protocol
Negotiation.
"""
@@ -211,28 +130,21 @@ ProtocolNegotiationSupport.NOSUPPORT = (
)
-def protocolNegotiationMechanisms():
+def protocolNegotiationMechanisms() -> FlagConstant:
"""
- Checks whether your versions of PyOpenSSL and OpenSSL are recent enough to
- support protocol negotiation, and if they are, what kind of protocol
- negotiation is supported.
+ Check whether the installed versions of pyOpenSSL and OpenSSL are recent
+ enough to support ALPN.
@return: A combination of flags from L{ProtocolNegotiationSupport} that
indicate which mechanisms for protocol negotiation are supported.
- @rtype: L{constantly.FlagConstant}
"""
+ # TODO: deprecate this, as it will always return ALPN and only ALPN on all
+ # supported versions of OpenSSL.
support = ProtocolNegotiationSupport.NOSUPPORT
ctx = SSL.Context(SSL.SSLv23_METHOD)
try:
- ctx.set_npn_advertise_callback(lambda c: None)
- except (AttributeError, NotImplementedError):
- pass
- else:
- support |= ProtocolNegotiationSupport.NPN
-
- try:
- ctx.set_alpn_select_callback(lambda c: None)
+ ctx.set_alpn_select_callback(lambda connection, protocols: protocols[0])
except (AttributeError, NotImplementedError):
pass
else:
@@ -258,7 +170,7 @@ _x509names = {
}
-class DistinguishedName(Dict[str, bytes]):
+class DistinguishedName(dict[str, bytes]):
"""
Identify and describe an entity.
@@ -334,13 +246,13 @@ class DistinguishedName(Dict[str, bytes]):
value = value.encode("ascii")
self[realAttr] = value
- def inspect(self):
+ def inspect(self) -> str:
"""
Return a multi-line, human-readable representation of this DN.
@rtype: L{str}
"""
- l = []
+ lines = []
lablen = 0
def uniqueValues(mapping):
@@ -351,11 +263,9 @@ class DistinguishedName(Dict[str, bytes]):
lablen = max(len(label), lablen)
v = getattr(self, k, None)
if v is not None:
- l.append((label, nativeString(v)))
+ lines.append((label, nativeString(v)))
lablen += 2
- for n, (label, attrib) in enumerate(l):
- l[n] = label.rjust(lablen) + ": " + attrib
- return "\n".join(l)
+ return "\n".join(label.rjust(lablen) + ": " + attrib for label, attrib in lines)
DN = DistinguishedName
@@ -426,6 +336,9 @@ def _handleattrhelper(Class, transport, methodName):
return Class(cert)
+_Self = TypeVar("_Self", bound="Certificate")
+
+
class Certificate(CertBase):
"""
An x509 certificate.
@@ -444,7 +357,12 @@ class Certificate(CertBase):
return NotImplemented
@classmethod
- def load(Class, requestData, format=crypto.FILETYPE_ASN1, args=()):
+ def load(
+ Class: type[_Self],
+ requestData: bytes,
+ format: int = crypto.FILETYPE_ASN1,
+ args: tuple[Any, ...] = (),
+ ) -> _Self:
"""
Load a certificate from an ASN.1- or PEM-format string.
@@ -465,7 +383,7 @@ class Certificate(CertBase):
return self.dump(crypto.FILETYPE_PEM)
@classmethod
- def loadPEM(Class, data):
+ def loadPEM(Class, data: bytes) -> Certificate:
"""
Load a certificate from a PEM-format data string.
@@ -761,7 +679,7 @@ class PublicKey:
class KeyPair(PublicKey):
@classmethod
- def load(Class, data, format=crypto.FILETYPE_ASN1):
+ def load(Class, data: bytes, format: int = crypto.FILETYPE_ASN1) -> KeyPair:
return Class(crypto.load_privatekey(format, data))
def dump(self, format=crypto.FILETYPE_ASN1):
@@ -1039,38 +957,6 @@ def platformTrust():
return OpenSSLDefaultPaths()
-def _tolerateErrors(wrapped):
- """
- Wrap up an C{info_callback} for pyOpenSSL so that if something goes wrong
- the error is immediately logged and the connection is dropped if possible.
-
- This wrapper exists because some versions of pyOpenSSL don't handle errors
- from callbacks at I{all}, and those which do write tracebacks directly to
- stderr rather than to a supplied logging system. This reports unexpected
- errors to the Twisted logging system.
-
- Also, this terminates the connection immediately if possible because if
- you've got bugs in your verification logic it's much safer to just give up.
-
- @param wrapped: A valid C{info_callback} for pyOpenSSL.
- @type wrapped: L{callable}
-
- @return: A valid C{info_callback} for pyOpenSSL that handles any errors in
- C{wrapped}.
- @rtype: L{callable}
- """
-
- def infoCallback(connection: SSL.Connection, where: int, ret: int) -> object:
- result = None
- with _log.failuresHandled("Error during info_callback") as op:
- result = wrapped(connection, where, ret)
- if (f := op.failure) is not None:
- connection.get_app_data().failVerification(f)
- return result
-
- return infoCallback
-
-
@implementer(IOpenSSLClientConnectionCreator)
class ClientTLSOptions:
"""
@@ -1079,17 +965,16 @@ class ClientTLSOptions:
Private implementation type (not exposed to applications) for public
L{optionsForClientTLS} API.
- @ivar _ctx: The context to use for new connections.
- @type _ctx: L{OpenSSL.SSL.Context}
+ @ivar _createConnection: A callable that creates a mostly-configured
+ OpenSSL connection, modulo the hostname stuff that L{ClientTLSOptions}
+ is responsible for.
@ivar _hostname: The hostname to verify, as specified by the application,
as some human-readable text.
- @type _hostname: L{unicode}
@ivar _hostnameBytes: The hostname to verify, decoded into IDNA-encoded
bytes. This is passed to APIs which think that hostnames are bytes,
such as OpenSSL's SNI implementation.
- @type _hostnameBytes: L{bytes}
@ivar _hostnameASCII: The hostname, as transcoded into IDNA ASCII-range
unicode code points. This is pre-transcoded because the
@@ -1097,26 +982,44 @@ class ClientTLSOptions:
C{idna} package from PyPI for internationalized domain names, rather
than working with Python's built-in (but sometimes broken) IDNA
encoding. ASCII values, however, will always work.
- @type _hostnameASCII: L{unicode}
@ivar _hostnameIsDnsName: Whether or not the C{_hostname} is a DNSName.
Will be L{False} if C{_hostname} is an IP address or L{True} if
C{_hostname} is a DNSName
- @type _hostnameIsDnsName: L{bool}
+
+ @ivar _sendServerName: Whether the hostname will be sent via the TLS
+ U{Server Name Indication
+ <https://www.rfc-editor.org/rfc/rfc3546#section-3.1>} extension.
"""
- def __init__(self, hostname, ctx):
+ _createConnection: Callable[[TLSMemoryBIOProtocol], SSL.Connection]
+ _hostname: str
+ _hostnameASCII: str
+ _hostnameIsDnsName: bool
+ _hostnameBytes: bytes
+ _sendServerName: bool
+
+ def __init__(
+ self,
+ createConnection: Callable[[TLSMemoryBIOProtocol], SSL.Connection],
+ hostname: str,
+ sendServerName: bool | None = None,
+ ) -> None:
"""
Initialize L{ClientTLSOptions}.
+ @param createConnection: A callable which can create a
+ mostly-configured L{SSL.Connection}, modulo hostname verification.
+
@param hostname: The hostname to verify as input by a human.
- @type hostname: L{unicode}
- @param ctx: an L{OpenSSL.SSL.Context} to use for new connections.
- @type ctx: L{OpenSSL.SSL.Context}.
+ @param sendServerName: Should the server name be sent to the peer?
+ C{None} means "follow the specification", which will send it if
+ it's a valid DNS name and refrain from sending it if it's an IP
+ address; C{True} means always send, and C{False} means never send.
"""
- self._ctx = ctx
self._hostname = hostname
+ self._createConnection = createConnection
if isIPAddress(hostname) or isIPv6Address(hostname):
self._hostnameBytes = hostname.encode("ascii")
@@ -1126,69 +1029,77 @@ class ClientTLSOptions:
self._hostnameIsDnsName = True
self._hostnameASCII = self._hostnameBytes.decode("ascii")
- ctx.set_info_callback(_tolerateErrors(self._identityVerifyingInfoCallback))
+ if sendServerName is None:
+ sendServerName = self._hostnameIsDnsName
+ self._sendServerName = sendServerName
- def clientConnectionForTLS(self, tlsProtocol):
+ def clientConnectionForTLS(self, tlsProtocol: TLSMemoryBIOProtocol) -> Connection:
"""
Create a TLS connection for a client.
- @note: This will call C{set_app_data} on its connection. If you're
- delegating to this implementation of this method, don't ever call
- C{set_app_data} or C{set_info_callback} on the returned connection,
- or you'll break the implementation of various features of this
- class.
-
@param tlsProtocol: the TLS protocol initiating the connection.
- @type tlsProtocol: L{twisted.protocols.tls.TLSMemoryBIOProtocol}
@return: the configured client connection.
- @rtype: L{OpenSSL.SSL.Connection}
"""
- context = self._ctx
- connection = SSL.Connection(context, None)
- connection.set_app_data(tlsProtocol)
+ connection = self._createConnection(tlsProtocol)
+ # Literal IPv4 and IPv6 addresses are not permitted as host names
+ # according to the RFCs
+ if self._sendServerName:
+ connection.set_tlsext_host_name(self._hostnameBytes)
+ callback = _verifyCB(tlsProtocol, self._hostnameIsDnsName, self._hostnameASCII)
+ connection.set_verify(VERIFY_PEER | VERIFY_FAIL_IF_NO_PEER_CERT, callback)
return connection
- def _identityVerifyingInfoCallback(self, connection, where, ret):
- """
- U{info_callback
- <http://pythonhosted.org/pyOpenSSL/api/ssl.html#OpenSSL.SSL.Context.set_info_callback>
- } for pyOpenSSL that verifies the hostname in the presented certificate
- matches the one passed to this L{ClientTLSOptions}.
- @param connection: the connection which is handshaking.
- @type connection: L{OpenSSL.SSL.Connection}
+def _verifyCB(
+ tlsProtocol: TLSMemoryBIOProtocol, hostIsDNS: bool, hostnameASCII: str
+) -> Callable[[Connection, X509, int, int, bool], bool]:
+ svcid: ServiceID
+ if hostIsDNS:
+ svcid = DNS_ID(hostnameASCII)
+ else:
+ svcid = IPAddress_ID(hostnameASCII)
- @param where: flags indicating progress through a TLS handshake.
- @type where: L{int}
+ weakProtoRef: TLSMemoryBIOProtocol | None = tlsProtocol
- @param ret: ignored
- @type ret: ignored
- """
- # Literal IPv4 and IPv6 addresses are not permitted
- # as host names according to the RFCs
- if where & SSL.SSL_CB_HANDSHAKE_START and self._hostnameIsDnsName:
- connection.set_tlsext_host_name(self._hostnameBytes)
- elif where & SSL.SSL_CB_HANDSHAKE_DONE:
+ def verifyCallback(
+ conn: Connection, cert: X509, err: int, depth: int, ok: bool
+ ) -> bool:
+ ourVerifyResult = ok
+ nonlocal weakProtoRef
+ try:
+ if depth != 0:
+ # We are only verifying the leaf certificate.
+ return ourVerifyResult
try:
- if self._hostnameIsDnsName:
- verifyHostname(connection, self._hostnameASCII)
- else:
- verifyIPAddress(connection, self._hostnameASCII)
+ verify_service_identity(extract_patterns(cert), [svcid], [])
except VerificationError:
+ ourVerifyResult = False
f = Failure()
- transport = connection.get_app_data()
- transport.failVerification(f)
+ assert weakProtoRef is not None
+ weakProtoRef.failVerification(f)
+ except BaseException:
+ # If we raise an exception *at all* during an OpenSSL callback, at
+ # best we lose the exception to getting dumped on stderr rather
+ # than getting logged, at worst it just disappears. So we catch
+ # *everything* here so we can get it normally logged.
+ _log.failure("while verifying certificate")
+ # Ensure that no reference remains to the protocol.
+ weakProtoRef = None
+ return ourVerifyResult
+
+ return verifyCallback
def optionsForClientTLS(
- hostname,
- trustRoot=None,
- clientCertificate=None,
- acceptableProtocols=None,
+ hostname: str,
+ trustRoot: IOpenSSLTrustRoot | Certificate | None = None,
+ clientCertificate: PrivateCertificate | None = None,
+ acceptableProtocols: Sequence[bytes] | None = None,
*,
- extraCertificateOptions=None,
-):
+ extraCertificateOptions: dict[str, Any] | None = None,
+ sendServerName: bool | None = None,
+) -> IOpenSSLClientConnectionCreator:
"""
Create a L{client connection creator <IOpenSSLClientConnectionCreator>} for
use with APIs such as L{SSL4ClientEndpoint
@@ -1198,44 +1109,43 @@ def optionsForClientTLS(
@since: 14.0
- @param hostname: The expected name of the remote host. This serves two
+ @param hostname: The expected name of the remote host. This serves two
purposes: first, and most importantly, it verifies that the certificate
received from the server correctly identifies the specified hostname.
The second purpose is to use the U{Server Name Indication extension
<https://en.wikipedia.org/wiki/Server_Name_Indication>} to indicate to
the server which certificate should be used.
- @type hostname: L{unicode}
- @param trustRoot: Specification of trust requirements of peers. This may be
- a L{Certificate} or the result of L{platformTrust}. By default it is
- L{platformTrust} and you probably shouldn't adjust it unless you really
- know what you're doing. Be aware that clients using this interface
- I{must} verify the server; you cannot explicitly pass L{None} since
- that just means to use L{platformTrust}.
- @type trustRoot: L{IOpenSSLTrustRoot}
+ @param trustRoot: Specification of trust requirements of peers. This may
+ be a L{Certificate} or the result of L{platformTrust}. By default it
+ is L{platformTrust} and you probably shouldn't adjust it unless you
+ really know what you're doing. Be aware that clients using this
+ interface I{must} verify the server; you cannot explicitly pass L{None}
+ since that just means to use L{platformTrust}.
@param clientCertificate: The certificate and private key that the client
- will use to authenticate to the server. If unspecified, the client will
- not authenticate.
- @type clientCertificate: L{PrivateCertificate}
+ will use to authenticate to the server. If unspecified, the client
+ will not authenticate.
@param acceptableProtocols: The protocols this peer is willing to speak
- after the TLS negotiation has completed, advertised over both ALPN and
- NPN. If this argument is specified, and no overlap can be found with
- the other peer, the connection will fail to be established. If the
- remote peer does not offer NPN or ALPN, the connection will be
- established, but no protocol wil be negotiated. Protocols earlier in
- the list are preferred over those later in the list.
- @type acceptableProtocols: L{list} of L{bytes}
+ after the TLS negotiation has completed, advertised over ALPN. If this
+ argument is specified, and no overlap can be found with the other peer,
+ the connection will fail to be established. If the remote peer does
+ not offer ALPN, the connection will be established, but no protocol wil
+ be negotiated. Protocols earlier in the list are preferred over those
+ later in the list.
+
+ @param extraCertificateOptions: A dictionary of additional keyword
+ arguments to be presented to L{CertificateOptions}. Please avoid using
+ this unless you absolutely need to; any time you need to pass an option
+ here that is a bug in this interface.
- @param extraCertificateOptions: A dictionary of additional keyword arguments
- to be presented to L{CertificateOptions}. Please avoid using this unless
- you absolutely need to; any time you need to pass an option here that is
- a bug in this interface.
- @type extraCertificateOptions: L{dict}
+ @param sendServerName: Should the server name be sent to the peer? C{None}
+ means "follow the specification", which will send it if it's a valid
+ DNS name and refrain from sending it if it's an IP address; C{True}
+ means always send, and C{False} means never send.
@return: A client connection creator.
- @rtype: L{IOpenSSLClientConnectionCreator}
"""
if extraCertificateOptions is None:
extraCertificateOptions = {}
@@ -1251,15 +1161,23 @@ def optionsForClientTLS(
privateKey=clientCertificate.privateKey.original,
certificate=clientCertificate.original,
)
+
certificateOptions = OpenSSLCertificateOptions(
trustRoot=trustRoot,
acceptableProtocols=acceptableProtocols,
**extraCertificateOptions,
)
- return ClientTLSOptions(hostname, certificateOptions.getContext())
+
+ return ClientTLSOptions(
+ certificateOptions._makeTLSConnection, hostname, sendServerName
+ )
-@implementer(IOpenSSLContextFactory)
+@implementer(
+ IOpenSSLServerConnectionCreator,
+ IOpenSSLClientConnectionCreator,
+ IOpenSSLContextFactory,
+)
class OpenSSLCertificateOptions:
"""
A L{CertificateOptions <twisted.internet.ssl.CertificateOptions>} specifies
@@ -1282,7 +1200,7 @@ class OpenSSLCertificateOptions:
# Factory for creating contexts. Configurable for testability.
_contextFactory = SSL.Context
- _context = None
+ _context: SSL.Context | None = None
_OP_NO_TLSv1_3 = _tlsDisableFlags[TLSVersion.TLSv1_3]
@@ -1290,38 +1208,41 @@ class OpenSSLCertificateOptions:
@_mutuallyExclusiveArguments(
[
- ["trustRoot", "requireCertificate"],
- ["trustRoot", "verify"],
- ["trustRoot", "caCerts"],
- ["method", "insecurelyLowerMinimumTo"],
- ["method", "raiseMinimumTo"],
- ["raiseMinimumTo", "insecurelyLowerMinimumTo"],
- ["method", "lowerMaximumSecurityTo"],
+ ("trustRoot", "requireCertificate"),
+ ("trustRoot", "verify"),
+ ("trustRoot", "caCerts"),
+ ("method", "insecurelyLowerMinimumTo"),
+ ("method", "raiseMinimumTo"),
+ ("raiseMinimumTo", "insecurelyLowerMinimumTo"),
+ ("method", "lowerMaximumSecurityTo"),
]
)
def __init__(
self,
- privateKey=None,
- certificate=None,
- method=None,
- verify=False,
- caCerts=None,
- verifyDepth=9,
- requireCertificate=True,
- verifyOnce=True,
- enableSingleUseKeys=True,
- enableSessions=False,
- fixBrokenPeers=False,
- enableSessionTickets=False,
- extraCertChain=None,
- acceptableCiphers=None,
- dhParameters=None,
- trustRoot=None,
- acceptableProtocols=None,
- raiseMinimumTo=None,
- insecurelyLowerMinimumTo=None,
- lowerMaximumSecurityTo=None,
- ):
+ privateKey: PKey | None = None,
+ certificate: X509 | None = None,
+ method: int | None = None,
+ verify: bool = False,
+ caCerts: list[X509] | None = None,
+ verifyDepth: int = 9,
+ requireCertificate: bool = True,
+ verifyOnce: bool = True,
+ enableSingleUseKeys: bool = True,
+ enableSessions: bool = False,
+ fixBrokenPeers: bool = False,
+ enableSessionTickets: bool = False,
+ extraCertChain: list[X509] | None = None,
+ acceptableCiphers: IAcceptableCiphers | None = None,
+ dhParameters: OpenSSLDiffieHellmanParameters | None = None,
+ trustRoot: IOpenSSLTrustRoot | None = None,
+ acceptableProtocols: list[bytes] | None = None,
+ raiseMinimumTo: NamedConstant | None = None,
+ insecurelyLowerMinimumTo: NamedConstant | None = None,
+ lowerMaximumSecurityTo: NamedConstant | None = None,
+ contextForServerName: (
+ Callable[[bytes | None], SSL.Context | None] | None
+ ) = None,
+ ) -> None:
"""
Create an OpenSSL context SSL connection context factory.
@@ -1395,7 +1316,6 @@ class OpenSSLCertificateOptions:
verification chain if the certificate authority that signed your
C{certificate} isn't widely supported. Do I{not} add
C{certificate} to it.
- @type extraCertChain: C{list} of L{OpenSSL.crypto.X509}
@param acceptableCiphers: Ciphers that are acceptable for connections.
Uses a secure default if left L{None}.
@@ -1404,8 +1324,6 @@ class OpenSSLCertificateOptions:
@param dhParameters: Key generation parameters that are required for
Diffie-Hellman key exchange. If this argument is left L{None},
C{EDH} ciphers are I{disabled} regardless of C{acceptableCiphers}.
- @type dhParameters: L{DiffieHellmanParameters
- <twisted.internet.ssl.DiffieHellmanParameters>}
@param trustRoot: Specification of trust requirements of peers. If
this argument is specified, the peer is verified. It requires a
@@ -1417,16 +1335,13 @@ class OpenSSLCertificateOptions:
those options in combination with this one will raise a
L{TypeError}.
- @type trustRoot: L{IOpenSSLTrustRoot}
-
@param acceptableProtocols: The protocols this peer is willing to speak
- after the TLS negotiation has completed, advertised over both ALPN
- and NPN. If this argument is specified, and no overlap can be
- found with the other peer, the connection will fail to be
- established. If the remote peer does not offer NPN or ALPN, the
- connection will be established, but no protocol wil be negotiated.
- Protocols earlier in the list are preferred over those later in the
- list.
+ after the TLS negotiation has completed, advertised over ALPN. If
+ this argument is specified, and no overlap can be found with the
+ other peer, the connection will fail to be established. If the
+ remote peer does not offer ALPN, the connection will be
+ established, but no protocol wil be negotiated. Protocols earlier
+ in the list are preferred over those later in the list.
@type acceptableProtocols: L{list} of L{bytes}
@param raiseMinimumTo: The minimum TLS version that you want to use, or
@@ -1453,6 +1368,10 @@ class OpenSSLCertificateOptions:
unless you are absolutely sure this is what you want.
@type lowerMaximumSecurityTo: L{TLSVersion} constant
+ @param contextForServerName: A callback to invoke with the server-name
+ indication field in the client handshake, which returns the other
+ context to switch to.
+
@raise ValueError: when C{privateKey} or C{certificate} are set without
setting the respective other.
@raise ValueError: when C{verify} is L{True} but C{caCerts} doesn't
@@ -1605,14 +1524,9 @@ class OpenSSLCertificateOptions:
self.verify = True
self.requireCertificate = True
trustRoot = IOpenSSLTrustRoot(trustRoot)
- self.trustRoot = trustRoot
-
- if acceptableProtocols is not None and not protocolNegotiationMechanisms():
- raise NotImplementedError(
- "No support for protocol negotiation on this platform."
- )
-
+ self.trustRoot: IOpenSSLTrustRoot | None = trustRoot
self._acceptableProtocols = acceptableProtocols
+ self._contextForServerName = contextForServerName
def __getstate__(self):
d = self.__dict__.copy()
@@ -1625,15 +1539,51 @@ class OpenSSLCertificateOptions:
def __setstate__(self, state):
self.__dict__ = state
- def getContext(self):
+ def getContext(self) -> SSL.Context:
"""
- Return an L{OpenSSL.SSL.Context} object.
+ Create and cache an L{SSL.Context} based on the parameters in this
+ L{twisted.internet.ssl.CertificateOptions}.
+
+ This is deprecated because it returns a cached, shared context which
+ cannot safely be shared, because the acceptable protocols may be
+ mutated.
"""
if self._context is None:
self._context = self._makeContext()
return self._context
- def _makeContext(self):
+ def serverConnectionForTLS(self, protocol: TLSMemoryBIOProtocol) -> SSL.Connection:
+ """
+ Construct a TLS connection for the server.
+ """
+ return self._makeTLSConnection(protocol)
+
+ def clientConnectionForTLS(self, protocol: TLSMemoryBIOProtocol) -> SSL.Connection:
+ """
+ Construct a TLS connection for the client.
+ """
+ return self._makeTLSConnection(protocol)
+
+ def _makeTLSConnection(self, protocol: TLSMemoryBIOProtocol) -> SSL.Connection:
+ """
+ Construct an OpenSSL Connection for either client or server.
+ """
+ ctx = self.getContext()
+ cxn = Connection(ctx)
+ if acceptable := protosFromProtocol(protocol, self._acceptableProtocols or ()):
+ cxn.set_alpn_protos(acceptable)
+ return cxn
+
+ def _makeContext(self, skipCiphers: bool = False) -> SSL.Context:
+ """
+ Make a new context (with no caching) based on the settings of this
+ Options.
+
+ @param skipCiphers: Don't set the cipher list, so as to avoid creating
+ a Connection and preventing further mutation. Just for a few small
+ tests, per the behavior described U{here
+ <https://github.com/twisted/twisted/issues/12500#issuecomment-3287771137>}.
+ """
ctx = self._contextFactory(self.method)
ctx.set_options(self._options)
ctx.set_mode(self._mode)
@@ -1648,6 +1598,9 @@ class OpenSSLCertificateOptions:
verifyFlags = SSL.VERIFY_NONE
if self.verify:
+ assert (
+ self.trustRoot is not None
+ ), "when the verify flag is set, trustRoot must be set"
verifyFlags = SSL.VERIFY_PEER
if self.requireCertificate:
verifyFlags |= SSL.VERIFY_FAIL_IF_NO_PEER_CERT
@@ -1680,22 +1633,33 @@ class OpenSSLCertificateOptions:
if self.dhParameters:
ctx.load_tmp_dh(self.dhParameters._dhFile.path)
- ctx.set_cipher_list(self._cipherString.encode("ascii"))
self._ecChooser.configureECDHCurve(ctx)
+ if self._contextForServerName is not None:
+ ctxForName = self._contextForServerName
- if self._acceptableProtocols:
- # Try to set NPN and ALPN. _acceptableProtocols cannot be set by
- # the constructor unless at least one mechanism is supported.
- _setAcceptableProtocols(ctx, self._acceptableProtocols)
+ def contextSelectionCallback(connection: SSL.Connection) -> None:
+ servername = connection.get_servername()
+ try:
+ newContext = ctxForName(servername)
+ except BaseException:
+ _log.failure("while looking up SNI context")
+ connection.get_app_data().abortConnection()
+ else:
+ if newContext is not None:
+ connection.set_context(newContext)
+ ctx.set_tlsext_servername_callback(contextSelectionCallback)
+ setupForALPN(ctx, self._acceptableProtocols or ())
+ if not skipCiphers:
+ ctx.set_cipher_list(self._cipherString.encode("ascii"))
return ctx
-OpenSSLCertificateOptions.__getstate__ = deprecated(
+OpenSSLCertificateOptions.__getstate__ = deprecated( # type:ignore[method-assign]
Version("Twisted", 15, 0, 0), "a real persistence system"
)(OpenSSLCertificateOptions.__getstate__)
-OpenSSLCertificateOptions.__setstate__ = deprecated(
+OpenSSLCertificateOptions.__setstate__ = deprecated( # type:ignore[method-assign]
Version("Twisted", 15, 0, 0), "a real persistence system"
)(OpenSSLCertificateOptions.__setstate__)
@@ -1960,61 +1924,54 @@ class OpenSSLDiffieHellmanParameters:
return cls(filePath)
-def _setAcceptableProtocols(context, acceptableProtocols):
+def protosFromProtocol(
+ tlsProto: TLSMemoryBIOProtocol, acceptableProtocols: Sequence[bytes]
+) -> Sequence[bytes]:
+ """
+ Get the union of the ALPN protocols from the given TLSMemoryBIOProtocol and
+ from the static list of acceptable protocols.
"""
- Called to set up the L{OpenSSL.SSL.Context} for doing NPN and/or ALPN
- negotiation.
+ tlsFactory = tlsProto.factory
+ assert tlsFactory is not None
+ ipnf = IProtocolNegotiationFactory(tlsFactory.wrappedFactory, None)
+ allAcceptableProtocols = tuple(acceptableProtocols or ())
+ if ipnf is not None:
+ allAcceptableProtocols = (
+ tuple(ipnf.acceptableProtocols()) + allAcceptableProtocols
+ )
+ return allAcceptableProtocols
- @param context: The context which is set up.
- @type context: L{OpenSSL.SSL.Context}
- @param acceptableProtocols: The protocols this peer is willing to speak
- after the TLS negotiation has completed, advertised over both ALPN and
- NPN. If this argument is specified, and no overlap can be found with
- the other peer, the connection will fail to be established. If the
- remote peer does not offer NPN or ALPN, the connection will be
- established, but no protocol wil be negotiated. Protocols earlier in
- the list are preferred over those later in the list.
- @type acceptableProtocols: L{list} of L{bytes}
+def setupForALPN(context: SSL.Context, acceptableProtocols: Sequence[bytes]) -> None:
"""
+ Called to set up the L{OpenSSL.SSL.Context} for doing ALPN negotiation.
- def protoSelectCallback(conn, protocols):
- """
- NPN client-side and ALPN server-side callback used to select
- the next protocol. Prefers protocols found earlier in
- C{_acceptableProtocols}.
+ @param context: The context which is being set up.
- @param conn: The context which is set up.
- @type conn: L{OpenSSL.SSL.Connection}
+ @param acceptableProtocols: The protocols that the host represented by
+ C{context} is willing to speak after TLS negotiation has completed,
+ which will be advertised by connections using this context, over ALPN.
- @param conn: Protocols advertised by the other side.
- @type conn: L{list} of L{bytes}
- """
- overlap = set(protocols) & set(acceptableProtocols)
+ If this argument is specified, and no overlap can be found with the
+ peer on a given connection, TLS negotiation of that connection will
+ fail, and it will not be established.
- for p in acceptableProtocols:
+ If a connection's peer does not offer ALPN, the connection will be
+ established, but no protocol will be negotiated. Protocols earlier in
+ the list are preferred over those later in the list.
+ """
+
+ def alpnSelect(conn: Connection, clientSentProtocols: Sequence[bytes]) -> bytes:
+ tlsProto = conn.get_app_data()
+ allAcceptableProtocols = protosFromProtocol(tlsProto, acceptableProtocols)
+ overlap = set(clientSentProtocols) & set(allAcceptableProtocols)
+ for p in allAcceptableProtocols:
if p in overlap:
return p
else:
+ # TODO: I think this should really be
+ # OpenSSL.SSL.NO_OVERLAPPING_PROTOCOLS which is a Python-specific
+ # sentinel object exposed by pyOpenSSL
return b""
- # If we don't actually have protocols to negotiate, don't set anything up.
- # Depending on OpenSSL version, failing some of the selection callbacks can
- # cause the handshake to fail, which is presumably not what was intended
- # here.
- if not acceptableProtocols:
- return
-
- supported = protocolNegotiationMechanisms()
-
- if supported & ProtocolNegotiationSupport.NPN:
-
- def npnAdvertiseCallback(conn):
- return acceptableProtocols
-
- context.set_npn_advertise_callback(npnAdvertiseCallback)
- context.set_npn_select_callback(protoSelectCallback)
-
- if supported & ProtocolNegotiationSupport.ALPN:
- context.set_alpn_select_callback(protoSelectCallback)
- context.set_alpn_protos(acceptableProtocols)
+ context.set_alpn_select_callback(alpnSelect)
diff --git a/contrib/python/Twisted/py3/twisted/internet/abstract.py b/contrib/python/Twisted/py3/twisted/internet/abstract.py
index 09bd751199c..642008a84fe 100644
--- a/contrib/python/Twisted/py3/twisted/internet/abstract.py
+++ b/contrib/python/Twisted/py3/twisted/internet/abstract.py
@@ -8,8 +8,8 @@ Support for generic select()able objects.
from __future__ import annotations
+from collections.abc import Iterable
from socket import AF_INET, AF_INET6, inet_pton
-from typing import Iterable, List, Optional, Union
from zope.interface import implementer
@@ -182,7 +182,7 @@ class FileDescriptor(_ConsumerMixin, _LogOwner):
SEND_LIMIT = 128 * 1024
- def __init__(self, reactor: Optional[interfaces.IReactorFDSet] = None):
+ def __init__(self, reactor: interfaces.IReactorFDSet | None = None):
"""
@param reactor: An L{IReactorFDSet} provider which this descriptor will
use to get readable and writeable event notifications. If no value
@@ -191,10 +191,10 @@ class FileDescriptor(_ConsumerMixin, _LogOwner):
if not reactor:
from twisted.internet import reactor as _reactor
- reactor = _reactor # type: ignore[assignment]
+ reactor = _reactor
self.reactor = reactor
# will be added to dataBuffer in doWrite
- self._tempDataBuffer: List[bytes] = []
+ self._tempDataBuffer: list[bytes] = []
self._tempDataLen = 0
def connectionLost(self, reason):
@@ -215,7 +215,7 @@ class FileDescriptor(_ConsumerMixin, _LogOwner):
self.stopReading()
self.stopWriting()
- def writeSomeData(self, data: bytes) -> Union[int, BaseException]:
+ def writeSomeData(self, data: bytes) -> int | BaseException:
"""
Write as much as possible of the given data, immediately.
diff --git a/contrib/python/Twisted/py3/twisted/internet/address.py b/contrib/python/Twisted/py3/twisted/internet/address.py
index d0ab1f69290..b832270b296 100644
--- a/contrib/python/Twisted/py3/twisted/internet/address.py
+++ b/contrib/python/Twisted/py3/twisted/internet/address.py
@@ -7,13 +7,12 @@ Address objects for network connections.
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 --git a/contrib/python/Twisted/py3/twisted/internet/asyncioreactor.py b/contrib/python/Twisted/py3/twisted/internet/asyncioreactor.py
index cd1cf65f05d..6e06544e6b4 100644
--- a/contrib/python/Twisted/py3/twisted/internet/asyncioreactor.py
+++ b/contrib/python/Twisted/py3/twisted/internet/asyncioreactor.py
@@ -6,11 +6,11 @@
asyncio-based reactor implementation.
"""
+from __future__ import annotations
import errno
import sys
-from asyncio import AbstractEventLoop, get_event_loop
-from typing import Dict, Optional, Type
+from asyncio import AbstractEventLoop, get_running_loop, new_event_loop, set_event_loop
from zope.interface import implementer
@@ -45,9 +45,13 @@ class AsyncioSelectorReactor(PosixReactorBase):
_asyncClosed = False
_log = Logger()
- def __init__(self, eventloop: Optional[AbstractEventLoop] = None):
+ def __init__(self, eventloop: AbstractEventLoop | None = None):
if eventloop is None:
- _eventloop: AbstractEventLoop = get_event_loop()
+ try:
+ _eventloop: AbstractEventLoop = get_running_loop()
+ except RuntimeError:
+ _eventloop = new_event_loop()
+ set_event_loop(_eventloop)
else:
_eventloop = eventloop
@@ -63,8 +67,8 @@ class AsyncioSelectorReactor(PosixReactorBase):
)
self._asyncioEventloop: AbstractEventLoop = _eventloop
- self._writers: Dict[Type[FileDescriptor], int] = {}
- self._readers: Dict[Type[FileDescriptor], int] = {}
+ self._writers: dict[type[FileDescriptor], int] = {}
+ self._readers: dict[type[FileDescriptor], int] = {}
self._continuousPolling = _ContinuousPolling(self)
self._scheduledAt = None
diff --git a/contrib/python/Twisted/py3/twisted/internet/base.py b/contrib/python/Twisted/py3/twisted/internet/base.py
index 2c38b80b0ca..418a7f1559a 100644
--- a/contrib/python/Twisted/py3/twisted/internet/base.py
+++ b/contrib/python/Twisted/py3/twisted/internet/base.py
@@ -6,28 +6,17 @@
Very basic functionality for a Reactor implementation.
"""
+from __future__ import annotations
import builtins
import socket # needed only for sync-dns
import warnings
from abc import ABC, abstractmethod
+from collections.abc import Sequence
from heapq import heapify, heappop, heappush
from traceback import format_stack
from types import FrameType
-from typing import (
- TYPE_CHECKING,
- Any,
- Callable,
- Dict,
- List,
- NewType,
- Optional,
- Sequence,
- Set,
- Tuple,
- Union,
- cast,
-)
+from typing import TYPE_CHECKING, Any, Callable, NewType, cast
from zope.interface import classImplements, implementer
@@ -55,7 +44,7 @@ from twisted.internet.interfaces import (
IWriteDescriptor,
_ISupportsExitSignalCapturing,
)
-from twisted.internet.protocol import ClientFactory
+from twisted.internet.protocol import ClientFactory, P
from twisted.logger import Logger
from twisted.python import reflect
from twisted.python.failure import Failure
@@ -89,19 +78,19 @@ class DelayedCall:
# enable .debug to record creator call stack, and it will be logged if
# an exception occurs while the function is being run
debug = False
- _repr: Optional[str] = None
+ _repr: str | None = None
# In debug mode, the call stack at the time of instantiation.
- creator: Optional[Sequence[str]] = None
+ creator: Sequence[str] | None = None
def __init__(
self,
time: float,
func: Callable[..., Any],
args: Sequence[object],
- kw: Dict[str, object],
- cancel: Callable[["DelayedCall"], None],
- reset: Callable[["DelayedCall"], None],
+ kw: dict[str, object],
+ cancel: Callable[[DelayedCall], None],
+ reset: Callable[[DelayedCall], None],
seconds: Callable[[], float] = runtimeSeconds,
) -> None:
"""
@@ -213,7 +202,7 @@ class DelayedCall:
"""
return not (self.cancelled or self.called)
- def __le__(self, other: "DelayedCall") -> bool:
+ def __le__(self, other: DelayedCall) -> bool:
"""
Implement C{<=} operator between two L{DelayedCall} instances.
@@ -222,7 +211,7 @@ class DelayedCall:
"""
return self.time <= other.time
- def __lt__(self, other: "DelayedCall") -> bool:
+ def __lt__(self, other: DelayedCall) -> bool:
"""
Implement C{<} operator between two L{DelayedCall} instances.
@@ -293,10 +282,10 @@ class ThreadedResolver:
delivered.
"""
- def __init__(self, reactor: "ReactorBase") -> None:
+ def __init__(self, reactor: ReactorBase) -> None:
self.reactor = reactor
- self._runningQueries: Dict[
- Deferred[str], Tuple[Deferred[str], IDelayedCall]
+ self._runningQueries: dict[
+ Deferred[str], tuple[Deferred[str], IDelayedCall]
] = {}
def _fail(self, name: str, err: str) -> Failure:
@@ -309,7 +298,7 @@ class ThreadedResolver:
userDeferred.errback(self._fail(name, "timeout error"))
def _checkTimeout(
- self, result: Union[str, Failure], name: str, lookupDeferred: Deferred[str]
+ self, result: str | Failure, name: str, lookupDeferred: Deferred[str]
) -> None:
try:
userDeferred, cancelCall = self._runningQueries[lookupDeferred]
@@ -371,12 +360,12 @@ class BlockingResolver:
_ThreePhaseEventTriggerCallable = Callable[..., Any]
-_ThreePhaseEventTrigger = Tuple[
- _ThreePhaseEventTriggerCallable, Tuple[object, ...], Dict[str, object]
+_ThreePhaseEventTrigger = tuple[
+ _ThreePhaseEventTriggerCallable, tuple[object, ...], dict[str, object]
]
_ThreePhaseEventTriggerHandle = NewType(
"_ThreePhaseEventTriggerHandle",
- Tuple[str, _ThreePhaseEventTriggerCallable, Tuple[object, ...], Dict[str, object]],
+ tuple[str, _ThreePhaseEventTriggerCallable, tuple[object, ...], dict[str, object]],
)
@@ -411,9 +400,9 @@ class _ThreePhaseEvent:
"""
def __init__(self) -> None:
- self.before: List[_ThreePhaseEventTrigger] = []
- self.during: List[_ThreePhaseEventTrigger] = []
- self.after: List[_ThreePhaseEventTrigger] = []
+ self.before: list[_ThreePhaseEventTrigger] = []
+ self.during: list[_ThreePhaseEventTrigger] = []
+ self.after: list[_ThreePhaseEventTrigger] = []
self.state = "BASE"
def addTrigger(
@@ -494,7 +483,7 @@ class _ThreePhaseEvent:
"""
self.state = "BEFORE"
self.finishedBefore = []
- beforeResults: List[Deferred[object]] = []
+ beforeResults: list[Deferred[object]] = []
while self.before:
callable, args, kwargs = self.before.pop(0)
self.finishedBefore.append((callable, args, kwargs))
@@ -568,8 +557,8 @@ class PluggableResolverMixin:
return self._nameResolver
-_SystemEventID = NewType("_SystemEventID", Tuple[str, _ThreePhaseEventTriggerHandle])
-_ThreadCall = Tuple[Callable[..., Any], Tuple[object, ...], Dict[str, object]]
+_SystemEventID = NewType("_SystemEventID", tuple[str, _ThreePhaseEventTriggerHandle])
+_ThreadCall = tuple[Callable[..., Any], tuple[object, ...], dict[str, object]]
_DEFAULT_DELAYED_CALL_LOGGING_HANDLER = _log.failureHandler("while handling timed call")
@@ -622,10 +611,10 @@ class ReactorBase(PluggableResolverMixin):
def __init__(self) -> None:
super().__init__()
- self.threadCallQueue: List[_ThreadCall] = []
- self._eventTriggers: Dict[str, _ThreePhaseEvent] = {}
- self._pendingTimedCalls: List[DelayedCall] = []
- self._newTimedCalls: List[DelayedCall] = []
+ self.threadCallQueue: list[_ThreadCall] = []
+ self._eventTriggers: dict[str, _ThreePhaseEvent] = {}
+ self._pendingTimedCalls: list[DelayedCall] = []
+ self._newTimedCalls: list[DelayedCall] = []
self._cancellations = 0
self.running = False
self._started = False
@@ -633,7 +622,7 @@ class ReactorBase(PluggableResolverMixin):
self._startedBefore = False
# reactor internal readers, e.g. the waker.
# Using Any as the type here… unable to find a suitable defined interface
- self._internalReaders: Set[Any] = set()
+ self._internalReaders: set[Any] = set()
self.waker: Any = None
# Arrange for the running attribute to change to True at the right time
@@ -726,7 +715,7 @@ class ReactorBase(PluggableResolverMixin):
# if the waker isn't installed, the reactor isn't running, and
# therefore doesn't need to be woken up
- def doIteration(self, delay: Optional[float]) -> None:
+ def doIteration(self, delay: float | None) -> None:
"""
Do one iteration over the readers and writers which have been added.
"""
@@ -754,17 +743,17 @@ class ReactorBase(PluggableResolverMixin):
reflect.qual(self.__class__) + " did not implement removeWriter"
)
- def removeAll(self) -> List[Union[IReadDescriptor, IWriteDescriptor]]:
+ def removeAll(self) -> list[IReadDescriptor | IWriteDescriptor]:
raise NotImplementedError(
reflect.qual(self.__class__) + " did not implement removeAll"
)
- def getReaders(self) -> List[IReadDescriptor]:
+ def getReaders(self) -> list[IReadDescriptor]:
raise NotImplementedError(
reflect.qual(self.__class__) + " did not implement getReaders"
)
- def getWriters(self) -> List[IWriteDescriptor]:
+ def getWriters(self) -> list[IWriteDescriptor]:
raise NotImplementedError(
reflect.qual(self.__class__) + " did not implement getWriters"
)
@@ -804,7 +793,7 @@ class ReactorBase(PluggableResolverMixin):
self.running = False
self.addSystemEventTrigger("during", "startup", self._reallyStartRunning)
- def sigInt(self, number: int, frame: Optional[FrameType] = None) -> None:
+ def sigInt(self, number: int, frame: FrameType | None = None) -> None:
"""
Handle a SIGINT interrupt.
@@ -815,7 +804,7 @@ class ReactorBase(PluggableResolverMixin):
self.callFromThread(self.stop)
self._exitSignal = number
- def sigBreak(self, number: int, frame: Optional[FrameType] = None) -> None:
+ def sigBreak(self, number: int, frame: FrameType | None = None) -> None:
"""
Handle a SIGBREAK interrupt.
@@ -826,7 +815,7 @@ class ReactorBase(PluggableResolverMixin):
self.callFromThread(self.stop)
self._exitSignal = number
- def sigTerm(self, number: int, frame: Optional[FrameType] = None) -> None:
+ def sigTerm(self, number: int, frame: FrameType | None = None) -> None:
"""
Handle a SIGTERM interrupt.
@@ -892,7 +881,7 @@ class ReactorBase(PluggableResolverMixin):
def callWhenRunning(
self, callable: Callable[..., Any], *args: object, **kwargs: object
- ) -> Optional[_SystemEventID]:
+ ) -> _SystemEventID | None:
"""
See twisted.internet.interfaces.IReactorCore.callWhenRunning.
"""
@@ -1021,7 +1010,7 @@ class ReactorBase(PluggableResolverMixin):
heappush(self._pendingTimedCalls, call)
self._newTimedCalls = []
- def timeout(self) -> Optional[float]:
+ def timeout(self) -> float | None:
"""
Determine the longest time the reactor may sleep (waiting on I/O
notification, perhaps) before it must wake up to service a time-related
@@ -1235,7 +1224,7 @@ class BaseConnector(ABC):
factoryStarted = 0
def __init__(
- self, factory: ClientFactory, timeout: float, reactor: ReactorBase
+ self, factory: ClientFactory[P], timeout: float, reactor: ReactorBase
) -> None:
self.state = "disconnected"
self.reactor = reactor
@@ -1251,7 +1240,7 @@ class BaseConnector(ABC):
self.transport.loseConnection()
@abstractmethod
- def _makeTransport(self) -> "Client":
+ def _makeTransport(self) -> Client:
pass
def connect(self) -> None:
@@ -1263,7 +1252,7 @@ class BaseConnector(ABC):
if not self.factoryStarted:
self.factory.doStart()
self.factoryStarted = 1
- self.transport: Optional[Client] = self._makeTransport()
+ self.transport: Client | None = self._makeTransport()
if self.timeout is not None:
self.timeoutID = self.reactor.callLater(
self.timeout, self.transport.failIfNotConnected, error.TimeoutError()
@@ -1288,7 +1277,7 @@ class BaseConnector(ABC):
pass
del self.timeoutID
- def buildProtocol(self, addr: IAddress) -> Optional[IProtocol]:
+ def buildProtocol(self, addr: IAddress) -> IProtocol | None:
self.state = "connected"
self.cancelTimeout()
return self.factory.buildProtocol(addr)
@@ -1340,9 +1329,9 @@ class BasePort(abstract.FileDescriptor):
fdesc._setCloseOnExec(s.fileno())
return s
- def doWrite(self) -> Optional[Failure]:
+ def doWrite(self) -> Failure | None:
"""Raises a RuntimeError"""
raise RuntimeError("doWrite called on a %s" % reflect.qual(self.__class__))
-__all__: List[str] = []
+__all__: list[str] = []
diff --git a/contrib/python/Twisted/py3/twisted/internet/defer.py b/contrib/python/Twisted/py3/twisted/internet/defer.py
index 1892eb483ee..d88014215fc 100644
--- a/contrib/python/Twisted/py3/twisted/internet/defer.py
+++ b/contrib/python/Twisted/py3/twisted/internet/defer.py
@@ -14,27 +14,19 @@ import traceback
import warnings
from abc import ABC, abstractmethod
from asyncio import AbstractEventLoop, Future, iscoroutine
+from collections.abc import Awaitable, Coroutine, Generator, Iterable, Mapping, Sequence
from contextvars import Context as _Context, copy_context as _copy_context
from enum import Enum
from functools import wraps
-from sys import exc_info, implementation
+from sys import exc_info
from types import CoroutineType, GeneratorType, MappingProxyType, TracebackType
from typing import (
TYPE_CHECKING,
Any,
- Awaitable,
Callable,
- Coroutine,
- Generator,
Generic,
- Iterable,
- List,
- Mapping,
+ Literal,
NoReturn,
- Optional,
- Sequence,
- Tuple,
- Type,
TypeVar,
Union,
cast,
@@ -43,12 +35,12 @@ from typing import (
import attr
from incremental import Version
-from typing_extensions import Concatenate, Literal, ParamSpec, Self
+from typing_extensions import Concatenate, ParamSpec, Self, TypeAlias
from twisted.internet.interfaces import IDelayedCall, IReactorTime
from twisted.logger import Logger
from twisted.python import lockfile
-from twisted.python.compat import _PYPY, cmp, comparable
+from twisted.python.compat import cmp, comparable
from twisted.python.deprecate import deprecated, deprecatedProperty, warnAboutFunction
from twisted.python.failure import Failure, _extraneous
@@ -58,9 +50,6 @@ log = Logger()
_T = TypeVar("_T")
_P = ParamSpec("_P")
-# See use in _inlineCallbacks for explanation and removal timeline.
-_oldPypyStack = _PYPY and implementation.version < (7, 3, 14)
-
class AlreadyCalledError(Exception):
"""
@@ -101,7 +90,7 @@ def logError(err: Failure) -> Failure:
return err
-def succeed(result: _T) -> "Deferred[_T]":
+def succeed(result: _T) -> Deferred[_T]:
"""
Return a L{Deferred} that has already had C{.callback(result)} called.
@@ -125,7 +114,7 @@ def succeed(result: _T) -> "Deferred[_T]":
return d
-def fail(result: Optional[Union[Failure, BaseException]] = None) -> "Deferred[Any]":
+def fail(result: Failure | BaseException | None = None) -> Deferred[Any]:
"""
Return a L{Deferred} that has already had C{.errback(result)} called.
@@ -143,7 +132,7 @@ def fail(result: Optional[Union[Failure, BaseException]] = None) -> "Deferred[An
def execute(
callable: Callable[_P, _T], *args: _P.args, **kwargs: _P.kwargs
-) -> "Deferred[_T]":
+) -> Deferred[_T]:
"""
Create a L{Deferred} from a callable and arguments.
@@ -162,7 +151,7 @@ def execute(
@overload
def maybeDeferred(
f: Callable[_P, Deferred[_T]], *args: _P.args, **kwargs: _P.kwargs
-) -> "Deferred[_T]":
+) -> Deferred[_T]:
...
@@ -171,22 +160,22 @@ def maybeDeferred(
f: Callable[_P, Coroutine[Deferred[Any], Any, _T]],
*args: _P.args,
**kwargs: _P.kwargs,
-) -> "Deferred[_T]":
+) -> Deferred[_T]:
...
@overload
def maybeDeferred(
f: Callable[_P, _T], *args: _P.args, **kwargs: _P.kwargs
-) -> "Deferred[_T]":
+) -> Deferred[_T]:
...
def maybeDeferred(
- f: Callable[_P, Union[Deferred[_T], Coroutine[Deferred[Any], Any, _T], _T]],
+ f: Callable[_P, Deferred[_T] | Coroutine[Deferred[Any], Any, _T] | _T],
*args: _P.args,
**kwargs: _P.kwargs,
-) -> "Deferred[_T]":
+) -> Deferred[_T]:
"""
Invoke a function that may or may not return a L{Deferred} or coroutine.
@@ -249,7 +238,7 @@ def maybeDeferred(
Version("Twisted", 17, 1, 0),
replacement="twisted.internet.defer.Deferred.addTimeout",
)
-def timeout(deferred: "Deferred[object]") -> None:
+def timeout(deferred: Deferred[object]) -> None:
deferred.errback(Failure(TimeoutError("Callback timed out")))
@@ -326,15 +315,15 @@ DeferredCallback = Callable[..., object]
# Callable[[Failure], object] is next best, but disallows valid callback signatures
DeferredErrback = Callable[..., object]
-_CallbackOrderedArguments = Tuple[object, ...]
-_CallbackKeywordArguments = Mapping[str, object]
-_CallbackChain = Tuple[
- Tuple[
+_CallbackOrderedArguments: TypeAlias = tuple[object, ...]
+_CallbackKeywordArguments: TypeAlias = Mapping[str, object]
+_CallbackChain: TypeAlias = tuple[
+ tuple[
Union[DeferredCallback, Literal[_Sentinel._CONTINUE]],
_CallbackOrderedArguments,
_CallbackKeywordArguments,
],
- Tuple[
+ tuple[
Union[DeferredErrback, DeferredCallback, Literal[_Sentinel._CONTINUE]],
_CallbackOrderedArguments,
_CallbackKeywordArguments,
@@ -353,9 +342,9 @@ class DebugInfo:
Deferred debug helper.
"""
- failResult: Optional[Failure] = None
- creator: Optional[List[str]] = None
- invoker: Optional[List[str]] = None
+ failResult: Failure | None = None
+ creator: list[str] | None = None
+ invoker: list[str] | None = None
def _getDebugTracebacks(self) -> str:
info = ""
@@ -429,7 +418,7 @@ class Deferred(Awaitable[_SelfResultT]):
called = False
paused = 0
- _debugInfo: Optional[DebugInfo] = None
+ _debugInfo: DebugInfo | None = None
_suppressAlreadyCalled = False
# Are we currently running a user-installed callback? Meant to prevent
@@ -441,10 +430,10 @@ class Deferred(Awaitable[_SelfResultT]):
# sets it directly.
debug = False
- _chainedTo: "Optional[Deferred[Any]]" = None
+ _chainedTo: Deferred[Any] | None = None
def __init__(
- self, canceller: Optional[Callable[["Deferred[Any]"], None]] = None
+ self, canceller: Callable[[Deferred[Any]], None] | None = None
) -> None:
"""
Initialize a L{Deferred}.
@@ -469,42 +458,42 @@ class Deferred(Awaitable[_SelfResultT]):
@type canceller: a 1-argument callable which takes a L{Deferred}. The
return result is ignored.
"""
- self._callbacks: List[_CallbackChain] = []
+ self._callbacks: list[_CallbackChain] = []
self._canceller = canceller
if self.debug:
self._debugInfo = DebugInfo()
self._debugInfo.creator = traceback.format_stack()[:-1]
@deprecatedProperty(Version("Twisted", 25, 5, 0))
- def callbacks(self) -> List[_CallbackChain]:
+ def callbacks(self) -> list[_CallbackChain]:
return self._callbacks
def addCallbacks(
self,
- callback: Union[
- Callable[..., _NextResultT],
- Callable[..., Deferred[_NextResultT]],
- Callable[..., Failure],
- Callable[
+ callback: (
+ Callable[..., _NextResultT]
+ | Callable[..., Deferred[_NextResultT]]
+ | Callable[..., Failure]
+ | Callable[
...,
- Union[_NextResultT, Deferred[_NextResultT], Failure],
- ],
- ],
- errback: Union[
- Callable[..., _NextResultT],
- Callable[..., Deferred[_NextResultT]],
- Callable[..., Failure],
- Callable[
+ _NextResultT | Deferred[_NextResultT] | Failure,
+ ]
+ ),
+ errback: (
+ Callable[..., _NextResultT]
+ | Callable[..., Deferred[_NextResultT]]
+ | Callable[..., Failure]
+ | Callable[
...,
- Union[_NextResultT, Deferred[_NextResultT], Failure],
- ],
- None,
- ] = None,
- callbackArgs: Tuple[Any, ...] = (),
+ _NextResultT | Deferred[_NextResultT] | Failure,
+ ]
+ | None
+ ) = None,
+ callbackArgs: tuple[Any, ...] = (),
callbackKeywords: Mapping[str, Any] = _NONE_KWARGS,
errbackArgs: _CallbackOrderedArguments = (),
errbackKeywords: _CallbackKeywordArguments = _NONE_KWARGS,
- ) -> "Deferred[_NextResultT]":
+ ) -> Deferred[_NextResultT]:
"""
Add a pair of callbacks (success and error) to this L{Deferred}.
@@ -572,7 +561,7 @@ class Deferred(Awaitable[_SelfResultT]):
self,
callback: Callable[
Concatenate[_SelfResultT, _P],
- Union[Failure, Deferred[_NextResultT]],
+ Failure | Deferred[_NextResultT],
],
*args: _P.args,
**kwargs: _P.kwargs,
@@ -582,7 +571,7 @@ class Deferred(Awaitable[_SelfResultT]):
@overload
def addCallback(
self,
- callback: Callable[Concatenate[_SelfResultT, _P], Union[Failure, _NextResultT]],
+ callback: Callable[Concatenate[_SelfResultT, _P], Failure | _NextResultT],
*args: _P.args,
**kwargs: _P.kwargs,
) -> Deferred[_NextResultT]:
@@ -602,7 +591,7 @@ class Deferred(Awaitable[_SelfResultT]):
self,
callback: Callable[
Concatenate[_SelfResultT, _P],
- Union[Deferred[_NextResultT], _NextResultT],
+ Deferred[_NextResultT] | _NextResultT,
],
*args: _P.args,
**kwargs: _P.kwargs,
@@ -618,7 +607,7 @@ class Deferred(Awaitable[_SelfResultT]):
) -> Deferred[_NextResultT]:
...
- def addCallback(self, callback: Any, *args: Any, **kwargs: Any) -> "Deferred[Any]":
+ def addCallback(self, callback: Any, *args: Any, **kwargs: Any) -> Deferred[Any]:
"""
Convenience method for adding just a callback.
@@ -639,7 +628,7 @@ class Deferred(Awaitable[_SelfResultT]):
errback: Callable[Concatenate[Failure, _P], Deferred[_NextResultT]],
*args: _P.args,
**kwargs: _P.kwargs,
- ) -> "Deferred[Union[_SelfResultT, _NextResultT]]":
+ ) -> Deferred[_SelfResultT | _NextResultT]:
...
@overload
@@ -648,7 +637,7 @@ class Deferred(Awaitable[_SelfResultT]):
errback: Callable[Concatenate[Failure, _P], Failure],
*args: _P.args,
**kwargs: _P.kwargs,
- ) -> "Deferred[Union[_SelfResultT]]":
+ ) -> Deferred[_SelfResultT]:
...
@overload
@@ -657,10 +646,10 @@ class Deferred(Awaitable[_SelfResultT]):
errback: Callable[Concatenate[Failure, _P], _NextResultT],
*args: _P.args,
**kwargs: _P.kwargs,
- ) -> "Deferred[Union[_SelfResultT, _NextResultT]]":
+ ) -> Deferred[_SelfResultT | _NextResultT]:
...
- def addErrback(self, errback: Any, *args: Any, **kwargs: Any) -> "Deferred[Any]":
+ def addErrback(self, errback: Any, *args: Any, **kwargs: Any) -> Deferred[Any]:
"""
Convenience method for adding just an errback.
@@ -678,7 +667,7 @@ class Deferred(Awaitable[_SelfResultT]):
@overload
def addBoth(
self,
- callback: Callable[Concatenate[Union[_SelfResultT, Failure], _P], Failure],
+ callback: Callable[Concatenate[_SelfResultT | Failure, _P], Failure],
*args: _P.args,
**kwargs: _P.kwargs,
) -> Deferred[_NextResultT]:
@@ -688,8 +677,8 @@ class Deferred(Awaitable[_SelfResultT]):
def addBoth(
self,
callback: Callable[
- Concatenate[Union[_SelfResultT, Failure], _P],
- Union[Failure, Deferred[_NextResultT]],
+ Concatenate[_SelfResultT | Failure, _P],
+ Failure | Deferred[_NextResultT],
],
*args: _P.args,
**kwargs: _P.kwargs,
@@ -700,7 +689,7 @@ class Deferred(Awaitable[_SelfResultT]):
def addBoth(
self,
callback: Callable[
- Concatenate[Union[_SelfResultT, Failure], _P], Union[Failure, _NextResultT]
+ Concatenate[_SelfResultT | Failure, _P], Failure | _NextResultT
],
*args: _P.args,
**kwargs: _P.kwargs,
@@ -711,7 +700,7 @@ class Deferred(Awaitable[_SelfResultT]):
def addBoth(
self,
callback: Callable[
- Concatenate[Union[_SelfResultT, Failure], _P], Deferred[_NextResultT]
+ Concatenate[_SelfResultT | Failure, _P], Deferred[_NextResultT]
],
*args: _P.args,
**kwargs: _P.kwargs,
@@ -722,8 +711,8 @@ class Deferred(Awaitable[_SelfResultT]):
def addBoth(
self,
callback: Callable[
- Concatenate[Union[_SelfResultT, Failure], _P],
- Union[Deferred[_NextResultT], _NextResultT],
+ Concatenate[_SelfResultT | Failure, _P],
+ Deferred[_NextResultT] | _NextResultT,
],
*args: _P.args,
**kwargs: _P.kwargs,
@@ -733,7 +722,7 @@ class Deferred(Awaitable[_SelfResultT]):
@overload
def addBoth(
self,
- callback: Callable[Concatenate[Union[_SelfResultT, Failure], _P], _NextResultT],
+ callback: Callable[Concatenate[_SelfResultT | Failure, _P], _NextResultT],
*args: _P.args,
**kwargs: _P.kwargs,
) -> Deferred[_NextResultT]:
@@ -748,7 +737,7 @@ class Deferred(Awaitable[_SelfResultT]):
) -> Deferred[_SelfResultT]:
...
- def addBoth(self, callback: Any, *args: Any, **kwargs: Any) -> "Deferred[Any]":
+ def addBoth(self, callback: Any, *args: Any, **kwargs: Any) -> Deferred[Any]:
"""
Convenience method for adding a single callable as both a callback
and an errback.
@@ -771,13 +760,14 @@ class Deferred(Awaitable[_SelfResultT]):
self,
timeout: float,
clock: IReactorTime,
- onTimeoutCancel: Optional[
+ onTimeoutCancel: None
+ | (
Callable[
- [Union[_SelfResultT, Failure], float],
- Union[_NextResultT, Failure],
+ [_SelfResultT | Failure, float],
+ _NextResultT | Failure,
]
- ] = None,
- ) -> "Deferred[Union[_SelfResultT, _NextResultT]]":
+ ) = None,
+ ) -> Deferred[_SelfResultT | _NextResultT]:
"""
Time out this L{Deferred} by scheduling it to be cancelled after
C{timeout} seconds.
@@ -815,8 +805,8 @@ class Deferred(Awaitable[_SelfResultT]):
delayedCall = clock.callLater(timeout, timeItOut)
def convertCancelled(
- result: Union[_SelfResultT, Failure],
- ) -> Union[_SelfResultT, _NextResultT, Failure]:
+ result: _SelfResultT | Failure,
+ ) -> _SelfResultT | _NextResultT | Failure:
# if C{deferred} was timed out, call the translation function,
# if provided, otherwise just use L{cancelledToTimedOutError}
if timedOut[0]:
@@ -833,12 +823,12 @@ class Deferred(Awaitable[_SelfResultT]):
# Note: Mypy cannot infer this type, apparently thanks to the ambiguity
# of _SelfResultT / _NextResultT both being unbound. Explicitly
# annotating it seems to do the trick though.
- converted: Deferred[Union[_SelfResultT, _NextResultT]] = self.addBoth(
+ converted: Deferred[_SelfResultT | _NextResultT] = self.addBoth(
convertCancelled
)
return converted.addBoth(cancelTimeout)
- def chainDeferred(self, d: "Deferred[_SelfResultT]") -> "Deferred[None]":
+ def chainDeferred(self, d: Deferred[_SelfResultT]) -> Deferred[None]:
"""
Chain another L{Deferred} to this L{Deferred}.
@@ -865,7 +855,7 @@ class Deferred(Awaitable[_SelfResultT]):
d._chainedTo = self
return self.addCallbacks(d.callback, d.errback)
- def callback(self, result: Union[_SelfResultT, Failure]) -> None:
+ def callback(self, result: _SelfResultT | Failure) -> None:
"""
Run all success callbacks that have been added to this L{Deferred}.
@@ -890,7 +880,7 @@ class Deferred(Awaitable[_SelfResultT]):
"""
self._startRunCallbacks(result)
- def errback(self, fail: Optional[Union[Failure, BaseException]] = None) -> None:
+ def errback(self, fail: Failure | BaseException | None = None) -> None:
"""
Run all error callbacks that have been added to this L{Deferred}.
@@ -1039,7 +1029,7 @@ class Deferred(Awaitable[_SelfResultT]):
# added its _continuation() to the callbacks list of a second Deferred
# and then that second Deferred being fired. ie, if ever had _chainedTo
# set to something other than None, you might end up on this stack.
- chain: List[Deferred[Any]] = [self]
+ chain: list[Deferred[Any]] = [self]
while chain:
current = chain[-1]
@@ -1198,7 +1188,7 @@ class Deferred(Awaitable[_SelfResultT]):
__await__ = __iter__
- def asFuture(self, loop: AbstractEventLoop) -> "Future[_SelfResultT]":
+ def asFuture(self, loop: AbstractEventLoop) -> Future[_SelfResultT]:
"""
Adapt this L{Deferred} into a L{Future} which is bound to C{loop}.
@@ -1215,7 +1205,7 @@ class Deferred(Awaitable[_SelfResultT]):
"""
future = loop.create_future()
- def checkCancel(futureAgain: "Future[_SelfResultT]") -> None:
+ def checkCancel(futureAgain: Future[_SelfResultT]) -> None:
if futureAgain.cancelled():
self.cancel()
@@ -1233,7 +1223,7 @@ class Deferred(Awaitable[_SelfResultT]):
return future
@classmethod
- def fromFuture(cls, future: "Future[_SelfResultT]") -> "Deferred[_SelfResultT]":
+ def fromFuture(cls, future: Future[_SelfResultT]) -> Deferred[_SelfResultT]:
"""
Adapt a L{Future} to a L{Deferred}.
@@ -1270,7 +1260,7 @@ class Deferred(Awaitable[_SelfResultT]):
def uncancel(
result: _SelfResultT,
- ) -> Union[_SelfResultT, Deferred[_SelfResultT]]:
+ ) -> _SelfResultT | Deferred[_SelfResultT]:
if result is futureCancel:
nonlocal actual
actual = Deferred()
@@ -1285,11 +1275,8 @@ class Deferred(Awaitable[_SelfResultT]):
@classmethod
def fromCoroutine(
cls,
- coro: Union[
- Coroutine[Deferred[Any], Any, _T],
- Generator[Deferred[Any], Any, _T],
- ],
- ) -> "Deferred[_T]":
+ coro: (Coroutine[Deferred[Any], Any, _T] | Generator[Deferred[Any], Any, _T]),
+ ) -> Deferred[_T]:
"""
Schedule the execution of a coroutine that awaits on L{Deferred}s,
wrapping it in a L{Deferred} that will fire on success/failure of the
@@ -1332,7 +1319,7 @@ class Deferred(Awaitable[_SelfResultT]):
return _cancellableInlineCallbacks(coro)
raise NotACoroutineError(f"{coro!r} is not a coroutine")
- def __init_subclass__(cls: Type[Deferred[Any]], **kwargs: Any):
+ def __init_subclass__(cls: type[Deferred[Any]], **kwargs: Any):
# Whenever a subclass is created, record it in L{_DEFERRED_SUBCLASSES}
# so we can emulate C{isinstance()} more efficiently.
_DEFERRED_SUBCLASSES.append(cls)
@@ -1342,11 +1329,11 @@ _DEFERRED_SUBCLASSES = [Deferred]
def ensureDeferred(
- coro: Union[
- Coroutine[Deferred[Any], Any, _T],
- Generator[Deferred[Any], Any, _T],
- Deferred[_T],
- ]
+ coro: (
+ Coroutine[Deferred[Any], Any, _T]
+ | Generator[Deferred[Any], Any, _T]
+ | Deferred[_T]
+ ),
) -> Deferred[_T]:
"""
Schedule the execution of a coroutine that awaits/yields from L{Deferred}s,
@@ -1413,9 +1400,9 @@ class FirstError(Exception):
return -1
-_DeferredListSingleResultT = Tuple[_SelfResultT, int]
-_DeferredListResultItemT = Tuple[bool, _SelfResultT]
-_DeferredListResultListT = List[_DeferredListResultItemT[_SelfResultT]]
+_DeferredListSingleResultT = tuple[_SelfResultT, int]
+_DeferredListResultItemT = tuple[bool, _SelfResultT]
+_DeferredListResultListT = list[_DeferredListResultItemT[_SelfResultT]]
if TYPE_CHECKING:
# The result type is different depending on whether fireOnOneCallback
@@ -1446,10 +1433,10 @@ if TYPE_CHECKING:
fireOnOneCallback: bool = False,
fireOnOneErrback: bool = False,
consumeErrors: bool = False,
- ) -> Union[
- Deferred[_DeferredListSingleResultT[_SelfResultT]],
- Deferred[_DeferredListResultListT[_SelfResultT]],
- ]:
+ ) -> (
+ Deferred[_DeferredListSingleResultT[_SelfResultT]]
+ | Deferred[_DeferredListResultListT[_SelfResultT]]
+ ):
...
DeferredList = _DeferredList
@@ -1521,7 +1508,7 @@ class DeferredList( # type: ignore[no-redef] # noqa:F811
# Note this contains optional result values as the DeferredList is
# processing its results, even though the callback result will not,
# which is why we aren't using _DeferredListResultListT here.
- self.resultList: List[Optional[_DeferredListResultItemT[Any]]] = [None] * len(
+ self.resultList: list[_DeferredListResultItemT[Any] | None] = [None] * len(
self._deferredList
)
"""
@@ -1555,7 +1542,7 @@ class DeferredList( # type: ignore[no-redef] # noqa:F811
def _cbDeferred(
self, result: _SelfResultT, index: int, succeeded: bool
- ) -> Optional[_SelfResultT]:
+ ) -> _SelfResultT | None:
"""
(internal) Callback for when one of my deferreds fires.
"""
@@ -1600,8 +1587,8 @@ class DeferredList( # type: ignore[no-redef] # noqa:F811
def _parseDeferredListResult(
- resultList: List[_DeferredListResultItemT[_T]], fireOnOneErrback: bool = False, /
-) -> List[_T]:
+ resultList: list[_DeferredListResultItemT[_T]], fireOnOneErrback: bool = False, /
+) -> list[_T]:
if __debug__:
for result in resultList:
assert result is not None
@@ -1612,7 +1599,7 @@ def _parseDeferredListResult(
def gatherResults(
deferredList: Iterable[Deferred[_T]], consumeErrors: bool = False
-) -> Deferred[List[_T]]:
+) -> Deferred[list[_T]]:
"""
Returns, via a L{Deferred}, a list with the results of the given
L{Deferred}s - in effect, a "join" of multiple deferred operations.
@@ -1645,7 +1632,7 @@ class FailureGroup(Exception):
"""
def __init__(self, failures: Sequence[Failure]) -> None:
- super(FailureGroup, self).__init__()
+ super().__init__()
self.failures = failures
@@ -1664,7 +1651,7 @@ def race(ds: Sequence[Deferred[_T]]) -> Deferred[tuple[int, _T]]:
# shouldn't be. Even though it "completed" it isn't really done - the
# caller will still be using it for something. If we cancelled it,
# cancellation could propagate down to them.
- winner: Optional[Deferred[_T]] = None
+ winner: Deferred[_T] | None = None
# The cancellation function for the Deferred this function returns.
def cancel(result: Deferred[_T]) -> None:
@@ -1773,16 +1760,13 @@ class _CancellationStatus(Generic[_SelfResultT]):
"""
deferred: Deferred[_SelfResultT]
- waitingOn: Optional[Deferred[_SelfResultT]] = None
+ waitingOn: Deferred[_SelfResultT] | None = None
def _gotResultInlineCallbacks(
r: object,
- waiting: List[Any],
- gen: Union[
- Generator[Deferred[Any], Any, _T],
- Coroutine[Deferred[Any], Any, _T],
- ],
+ waiting: list[Any],
+ gen: (Generator[Deferred[Any], Any, _T] | Coroutine[Deferred[Any], Any, _T]),
status: _CancellationStatus[_T],
context: _Context,
) -> None:
@@ -1806,10 +1790,7 @@ def _gotResultInlineCallbacks(
@_extraneous
def _inlineCallbacks(
result: object,
- gen: Union[
- Generator[Deferred[Any], Any, _T],
- Coroutine[Deferred[Any], Any, _T],
- ],
+ gen: (Generator[Deferred[Any], Any, _T] | Coroutine[Deferred[Any], Any, _T]),
status: _CancellationStatus[_T],
context: _Context,
) -> None:
@@ -1839,7 +1820,7 @@ def _inlineCallbacks(
# recursion.
# waiting for result? # result
- waiting: List[Any] = [True, None]
+ waiting: list[Any] = [True, None]
stopIteration: bool = False
callbackValue: Any = None
@@ -1870,27 +1851,18 @@ def _inlineCallbacks(
# The traceback starts in this frame (the one for
# _inlineCallbacks); the next one down should be the application
# code.
- excInfo = exc_info()
- assert excInfo is not None
+ appCodeTrace: TracebackType
- traceback = excInfo[2]
- assert traceback is not None
-
- appCodeTrace = traceback.tb_next
- assert appCodeTrace is not None
-
- if _oldPypyStack:
- # PyPy versions through 7.3.13 add an extra frame; 7.3.14 fixed
- # this discrepancy with CPython. This code can be removed once
- # we no longer need to support PyPy 7.3.13 or older.
- appCodeTrace = appCodeTrace.tb_next
- assert appCodeTrace is not None
+ # Here and everywhere below, we ignore the theoretical possibility
+ # that this is the end of the traceback (it can effectively never
+ # be).
+ appCodeTrace = exc_info()[2].tb_next # type:ignore[union-attr,assignment]
if isFailure:
# If we invoked this generator frame by throwing an exception
# into it, then throwExceptionIntoGenerator will consume an
# additional stack frame itself, so we need to skip that too.
- appCodeTrace = appCodeTrace.tb_next
+ appCodeTrace = appCodeTrace.tb_next # type:ignore[assignment]
assert appCodeTrace is not None
# Now that we've identified the frame being exited by the
@@ -2026,10 +1998,7 @@ def _handleCancelInlineCallbacks(
def _cancellableInlineCallbacks(
- gen: Union[
- Generator[Deferred[Any], object, _T],
- Coroutine[Deferred[Any], object, _T],
- ]
+ gen: (Generator[Deferred[Any], object, _T] | Coroutine[Deferred[Any], object, _T]),
) -> Deferred[_T]:
"""
Make an C{@}L{inlineCallbacks} cancellable.
@@ -2056,7 +2025,7 @@ class _InternalInlineCallbacksCancelledError(Exception):
def inlineCallbacks(
- f: Callable[_P, Generator[Deferred[Any], Any, _T]]
+ f: Callable[_P, Generator[Deferred[Any], Any, _T]],
) -> Callable[_P, Deferred[_T]]:
"""
L{inlineCallbacks} helps you write L{Deferred}-using code that looks like a
@@ -2143,7 +2112,7 @@ def inlineCallbacks(
class _ConcurrencyPrimitive(ABC):
def __init__(self: Self) -> None:
- self.waiting: List[Deferred[Self]] = []
+ self.waiting: list[Deferred[Self]] = []
def _releaseAndReturn(self, r: _T) -> _T:
self.release()
@@ -2178,7 +2147,7 @@ class _ConcurrencyPrimitive(ABC):
def run(
self: Self,
/,
- f: Callable[_P, Union[Deferred[_T], Coroutine[Deferred[Any], Any, _T], _T]],
+ f: Callable[_P, Deferred[_T] | Coroutine[Deferred[Any], Any, _T] | _T],
*args: _P.args,
**kwargs: _P.kwargs,
) -> Deferred[_T]:
@@ -2213,9 +2182,9 @@ class _ConcurrencyPrimitive(ABC):
def __aexit__(
self,
- __exc_type: Optional[Type[BaseException]],
- __exc_value: Optional[BaseException],
- __traceback: Optional[TracebackType],
+ __exc_type: type[BaseException] | None,
+ __exc_value: BaseException | None,
+ __traceback: TracebackType | None,
) -> Deferred[Literal[False]]:
self.release()
# We return False to indicate that we have not consumed the
@@ -2388,11 +2357,9 @@ class DeferredQueue(Generic[_T]):
for no limit.
"""
- def __init__(
- self, size: Optional[int] = None, backlog: Optional[int] = None
- ) -> None:
- self.waiting: List[Deferred[_T]] = []
- self.pending: List[_T] = []
+ def __init__(self, size: int | None = None, backlog: int | None = None) -> None:
+ self.waiting: list[Deferred[_T]] = []
+ self.pending: list[_T] = []
self.size = size
self.backlog = backlog
@@ -2466,10 +2433,10 @@ class DeferredFilesystemLock(lockfile.FilesystemLock):
"""
_interval = 1
- _tryLockCall: Optional[IDelayedCall] = None
- _timeoutCall: Optional[IDelayedCall] = None
+ _tryLockCall: IDelayedCall | None = None
+ _timeoutCall: IDelayedCall | None = None
- def __init__(self, name: str, scheduler: Optional[IReactorTime] = None) -> None:
+ def __init__(self, name: str, scheduler: IReactorTime | None = None) -> None:
"""
@param name: The name of the lock to acquire
@param scheduler: An object which provides L{IReactorTime}
@@ -2483,7 +2450,7 @@ class DeferredFilesystemLock(lockfile.FilesystemLock):
self._scheduler = scheduler
- def deferUntilLocked(self, timeout: Optional[float] = None) -> Deferred[None]:
+ def deferUntilLocked(self, timeout: float | None = None) -> Deferred[None]:
"""
Wait until we acquire this lock. This method is not safe for
concurrent use.
@@ -2503,7 +2470,7 @@ class DeferredFilesystemLock(lockfile.FilesystemLock):
)
)
- def _cancelLock(reason: Union[Failure, Exception]) -> None:
+ def _cancelLock(reason: Failure | Exception) -> None:
"""
Cancel a L{DeferredFilesystemLock.deferUntilLocked} call.
diff --git a/contrib/python/Twisted/py3/twisted/internet/endpoints.py b/contrib/python/Twisted/py3/twisted/internet/endpoints.py
index b73b2acc100..3da186aaae5 100644
--- a/contrib/python/Twisted/py3/twisted/internet/endpoints.py
+++ b/contrib/python/Twisted/py3/twisted/internet/endpoints.py
@@ -1,4 +1,4 @@
-# -*- test-case-name: twisted.internet.test.test_endpoints.HostnameEndpointMemoryIPv4ReactorTests.test_errorsLogged -*-
+# -*- test-case-name: twisted.internet.test.test_endpoints -*-
# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.
@@ -18,13 +18,15 @@ import os
import re
import socket
import warnings
-from typing import Any, Callable, Iterable, List, Optional, Sequence, Tuple, Type, Union
+from collections.abc import Iterable, Sequence
+from typing import Any, Callable, ClassVar, Protocol as TypingProtocol, TypeVar, Union
from unicodedata import normalize
from zope.interface import directlyProvides, implementer
from constantly import NamedConstant, Names
from incremental import Version
+from typing_extensions import ParamSpec
from twisted.internet import defer, error, fdesc, interfaces, threads
from twisted.internet.abstract import isIPv6Address
@@ -39,13 +41,17 @@ from twisted.internet.interfaces import (
IHostnameResolver,
IHostResolution,
IOpenSSLClientConnectionCreator,
+ IOpenSSLServerConnectionCreator,
IProtocol,
IProtocolFactory,
+ IReactorCore,
IReactorPluggableNameResolver,
IReactorSocket,
+ IReactorTime,
IResolutionReceiver,
IStreamClientEndpoint,
IStreamClientEndpointStringParserWithReactor,
+ IStreamServerEndpoint,
IStreamServerEndpointStringParser,
)
from twisted.internet.protocol import ClientFactory, Factory, ProcessProtocol, Protocol
@@ -72,7 +78,9 @@ from ._idna import _idnaBytes, _idnaText
try:
from OpenSSL.SSL import Error as SSLError
-
+except ImportError:
+ TLSMemoryBIOFactory = None
+else:
from twisted.internet.ssl import (
Certificate,
CertificateOptions,
@@ -81,10 +89,13 @@ try:
optionsForClientTLS,
trustRootFromCertificates,
)
+ from twisted.protocols._sni import (
+ SNIConnectionCreator,
+ TLSServerEndpoint as _TLSServerEndpoint,
+ autoReloadingDirectoryOfPEMs,
+ )
from twisted.protocols.tls import TLSMemoryBIOFactory as _TLSMemoryBIOFactory
-except ImportError:
- TLSMemoryBIOFactory = None
-else:
+
TLSMemoryBIOFactory = _TLSMemoryBIOFactory
__all__ = [
@@ -104,10 +115,38 @@ __all__ = [
"HostnameEndpoint",
"StandardErrorBehavior",
"connectProtocol",
+ "wrapServerTLS",
"wrapClientTLS",
]
+_P = ParamSpec("_P")
+_R = TypeVar("_R")
+
+
+class _DeferToThreadFunction(TypingProtocol):
+ def __call__(
+ self, f: Callable[_P, _R], *args: _P.args, **kwds: _P.kwargs
+ ) -> defer.Deferred[_R]:
+ ...
+
+
+def _staticmethod(f: Callable[_P, _R]) -> Callable[_P, _R]:
+ """
+ Wraps generic function as a staticmethod while preserving its signature for mypy.
+
+ Mypy cannot correctly infer the type when a generic function is directly passed to
+ staticmethod, resulting in an incorrect type annotation like 'staticmethod[Never, Never]'.
+
+ This helper function ensures that mypy understands resulting type as a Callable
+ with the same signature as original function.
+
+ See follow example on mypy playground to test if the workaround is still necessary:
+ https://mypy-play.net/?mypy=latest&python=3.12&gist=130fad37723b5d2d4685e6b2fadabe6a
+ """
+ return staticmethod(f)
+
+
class _WrappingProtocol(Protocol):
"""
Wrap another protocol in order to notify my user when a connection has
@@ -204,7 +243,8 @@ class _WrappingFactory(ClientFactory):
"""
# Type is wrong. See https://twistedmatrix.com/trac/ticket/10005#ticket
- protocol = _WrappingProtocol # type: ignore[assignment]
+
+ protocol = _WrappingProtocol
def __init__(self, wrappedFactory: IProtocolFactory) -> None:
"""
@@ -640,7 +680,9 @@ class TCP6ClientEndpoint:
"""
_getaddrinfo = staticmethod(socket.getaddrinfo)
- _deferToThread = staticmethod(threads.deferToThread)
+ _deferToThread: ClassVar[_DeferToThreadFunction] = _staticmethod(
+ threads.deferToThread
+ )
_GAI_ADDRESS = 4
_GAI_ADDRESS_HOST = 0
@@ -699,15 +741,15 @@ class TCP6ClientEndpoint:
return defer.fail()
-_gairesult = List[
- Tuple[
+_gairesult = list[
+ tuple[
socket.AddressFamily,
socket.SocketKind,
int,
str,
Union[
- Tuple[str, int],
- Tuple[str, int, int, int],
+ tuple[str, int],
+ tuple[str, int, int, int],
],
]
]
@@ -744,7 +786,7 @@ class _SimpleHostnameResolver:
resolutionReceiver: IResolutionReceiver,
hostName: str,
portNumber: int = 0,
- addressTypes: Optional[Sequence[Type[IAddress]]] = None,
+ addressTypes: Sequence[type[IAddress]] | None = None,
transportSemantics: str = "TCP",
) -> IHostResolution:
"""
@@ -816,7 +858,9 @@ class HostnameEndpoint:
"""
_getaddrinfo = staticmethod(socket.getaddrinfo)
- _deferToThread = staticmethod(threads.deferToThread)
+ _deferToThread: ClassVar[_DeferToThreadFunction] = _staticmethod(
+ threads.deferToThread
+ )
_DEFAULT_ATTEMPT_DELAY = 0.3
def __init__(
@@ -1585,8 +1629,8 @@ class _SystemdParser:
self,
reactor: IReactorSocket,
domain: str,
- index: Optional[str] = None,
- name: Optional[str] = None,
+ index: str | None = None,
+ name: str | None = None,
) -> AdoptedStreamServerEndpoint:
"""
Internal parser function for L{_parseServer} to convert the string
@@ -2279,24 +2323,21 @@ class _WrapperServerEndpoint:
def wrapClientTLS(
connectionCreator: IOpenSSLClientConnectionCreator,
wrappedEndpoint: IStreamClientEndpoint,
-) -> _WrapperEndpoint:
+ clock: IReactorTime | None = None,
+) -> IStreamClientEndpoint:
"""
- Wrap an endpoint which upgrades to TLS as soon as the connection is
- established.
+ Wrap a stream client endpoint which such that it upgrades to TLS as soon as
+ the wrapped connection is established.
@since: 16.0
@param connectionCreator: The TLS options to use when connecting; see
L{twisted.internet.ssl.optionsForClientTLS} for how to construct this.
- @type connectionCreator:
- L{twisted.internet.interfaces.IOpenSSLClientConnectionCreator}
@param wrappedEndpoint: The endpoint to wrap.
- @type wrappedEndpoint: An L{IStreamClientEndpoint} provider.
@return: an endpoint that provides transport level encryption layered on
top of C{wrappedEndpoint}
- @rtype: L{twisted.internet.interfaces.IStreamClientEndpoint}
"""
if TLSMemoryBIOFactory is None:
raise NotImplementedError(
@@ -2305,11 +2346,36 @@ def wrapClientTLS(
return _WrapperEndpoint(
wrappedEndpoint,
lambda protocolFactory: TLSMemoryBIOFactory(
- connectionCreator, True, protocolFactory
+ connectionCreator,
+ True,
+ protocolFactory,
+ clock=clock,
),
)
+def wrapServerTLS(
+ connectionCreator: IOpenSSLServerConnectionCreator,
+ wrappedEndpoint: IStreamServerEndpoint,
+ clock: IReactorTime | None = None,
+) -> IStreamServerEndpoint:
+ """
+ Wrap a server endpoint in a TLS configuration.
+
+ @param connectionCreator: The policy to create server connections. See
+ L{twisted.internet.ssl.CertificateOptions}.
+
+ @param wrappedEndpoint: The transport server endpoint. See
+ L{TCP6ServerEndpoint}.
+
+ @param clock: The clock interface used to schedule TLS buffered writes.
+
+ @return: an endpoint that listens with TLS encryption added to
+ C{wrappedEndpoint}
+ """
+ return _TLSServerEndpoint(wrappedEndpoint, connectionCreator, clock)
+
+
def _parseClientTLS(
reactor: Any,
host: bytes | str,
@@ -2419,3 +2485,38 @@ class _TLSClientEndpointParser:
@rtype: L{IStreamClientEndpoint}
"""
return _parseClientTLS(reactor, *args, **kwargs)
+
+
+@implementer(IPlugin, IStreamServerEndpointStringParser)
+class _TLSServerEndpointParser:
+ """
+ TLS server endpoint parser.
+ """
+
+ prefix: str = "tls"
+
+ def _actualParseStreamServer(
+ self,
+ reactor: IReactorCore,
+ path: str,
+ port: str = "443",
+ backlog: str = "50",
+ interface: str = "::",
+ ) -> IStreamServerEndpoint:
+ """
+ Actual parsing method, with detailed signature breaking out all
+ parameters.
+ """
+ p = FilePath(path)
+ return wrapServerTLS(
+ SNIConnectionCreator(autoReloadingDirectoryOfPEMs(p)),
+ TCP6ServerEndpoint(reactor, int(port), int(backlog), interface),
+ )
+
+ def parseStreamServer(
+ self, reactor: IReactorCore, *args: Any, **kwargs: Any
+ ) -> IStreamServerEndpoint:
+ """
+ Parse a TLS stream server endpoint.
+ """
+ return self._actualParseStreamServer(reactor, *args, **kwargs)
diff --git a/contrib/python/Twisted/py3/twisted/internet/interfaces.py b/contrib/python/Twisted/py3/twisted/internet/interfaces.py
index 28ab9bffbd2..6cf273e4600 100644
--- a/contrib/python/Twisted/py3/twisted/internet/interfaces.py
+++ b/contrib/python/Twisted/py3/twisted/internet/interfaces.py
@@ -8,20 +8,8 @@ Maintainer: Itamar Shtull-Trauring
"""
from __future__ import annotations
-from typing import (
- TYPE_CHECKING,
- Any,
- AnyStr,
- Callable,
- Iterable,
- List,
- Mapping,
- Optional,
- Sequence,
- Tuple,
- Type,
- Union,
-)
+from collections.abc import Iterable, Mapping, Sequence
+from typing import TYPE_CHECKING, Any, AnyStr, Callable
from zope.interface import Attribute, Interface
@@ -46,6 +34,7 @@ if TYPE_CHECKING:
ConnectedDatagramProtocol,
DatagramProtocol,
Factory,
+ P,
ServerFactory,
)
from twisted.internet.ssl import ClientContextFactory
@@ -104,7 +93,7 @@ class IConnector(Interface):
class IResolverSimple(Interface):
- def getHostByName(name: str, timeout: Sequence[int] = ()) -> "Deferred[str]":
+ def getHostByName(name: str, timeout: Sequence[int] = ()) -> Deferred[str]:
"""
Resolve the domain name C{name} into an IP address.
@@ -192,7 +181,7 @@ class IHostnameResolver(Interface):
resolutionReceiver: IResolutionReceiver,
hostName: str,
portNumber: int = 0,
- addressTypes: Optional[Sequence[Type[IAddress]]] = None,
+ addressTypes: Sequence[type[IAddress]] | None = None,
transportSemantics: str = "TCP",
) -> IHostResolution:
"""
@@ -222,8 +211,8 @@ class IHostnameResolver(Interface):
class IResolver(IResolverSimple):
def query(
- query: "Query", timeout: Sequence[int]
- ) -> "Deferred[Tuple[RRHeader, RRHeader, RRHeader]]":
+ query: Query, timeout: Sequence[int]
+ ) -> Deferred[tuple[RRHeader, RRHeader, RRHeader]]:
"""
Dispatch C{query} to the method which can handle its type.
@@ -243,7 +232,7 @@ class IResolver(IResolverSimple):
def lookupAddress(
name: str, timeout: Sequence[int]
- ) -> "Deferred[Tuple[RRHeader, RRHeader, RRHeader]]":
+ ) -> Deferred[tuple[RRHeader, RRHeader, RRHeader]]:
"""
Perform an A record lookup.
@@ -262,7 +251,7 @@ class IResolver(IResolverSimple):
def lookupAddress6(
name: str, timeout: Sequence[int]
- ) -> "Deferred[Tuple[RRHeader, RRHeader, RRHeader]]":
+ ) -> Deferred[tuple[RRHeader, RRHeader, RRHeader]]:
"""
Perform an A6 record lookup.
@@ -281,7 +270,7 @@ class IResolver(IResolverSimple):
def lookupIPV6Address(
name: str, timeout: Sequence[int]
- ) -> "Deferred[Tuple[RRHeader, RRHeader, RRHeader]]":
+ ) -> Deferred[tuple[RRHeader, RRHeader, RRHeader]]:
"""
Perform an AAAA record lookup.
@@ -300,7 +289,7 @@ class IResolver(IResolverSimple):
def lookupMailExchange(
name: str, timeout: Sequence[int]
- ) -> "Deferred[Tuple[RRHeader, RRHeader, RRHeader]]":
+ ) -> Deferred[tuple[RRHeader, RRHeader, RRHeader]]:
"""
Perform an MX record lookup.
@@ -319,7 +308,7 @@ class IResolver(IResolverSimple):
def lookupNameservers(
name: str, timeout: Sequence[int]
- ) -> "Deferred[Tuple[RRHeader, RRHeader, RRHeader]]":
+ ) -> Deferred[tuple[RRHeader, RRHeader, RRHeader]]:
"""
Perform an NS record lookup.
@@ -338,7 +327,7 @@ class IResolver(IResolverSimple):
def lookupCanonicalName(
name: str, timeout: Sequence[int]
- ) -> "Deferred[Tuple[RRHeader, RRHeader, RRHeader]]":
+ ) -> Deferred[tuple[RRHeader, RRHeader, RRHeader]]:
"""
Perform a CNAME record lookup.
@@ -357,7 +346,7 @@ class IResolver(IResolverSimple):
def lookupMailBox(
name: str, timeout: Sequence[int]
- ) -> "Deferred[Tuple[RRHeader, RRHeader, RRHeader]]":
+ ) -> Deferred[tuple[RRHeader, RRHeader, RRHeader]]:
"""
Perform an MB record lookup.
@@ -376,7 +365,7 @@ class IResolver(IResolverSimple):
def lookupMailGroup(
name: str, timeout: Sequence[int]
- ) -> "Deferred[Tuple[RRHeader, RRHeader, RRHeader]]":
+ ) -> Deferred[tuple[RRHeader, RRHeader, RRHeader]]:
"""
Perform an MG record lookup.
@@ -395,7 +384,7 @@ class IResolver(IResolverSimple):
def lookupMailRename(
name: str, timeout: Sequence[int]
- ) -> "Deferred[Tuple[RRHeader, RRHeader, RRHeader]]":
+ ) -> Deferred[tuple[RRHeader, RRHeader, RRHeader]]:
"""
Perform an MR record lookup.
@@ -414,7 +403,7 @@ class IResolver(IResolverSimple):
def lookupPointer(
name: str, timeout: Sequence[int]
- ) -> "Deferred[Tuple[RRHeader, RRHeader, RRHeader]]":
+ ) -> Deferred[tuple[RRHeader, RRHeader, RRHeader]]:
"""
Perform a PTR record lookup.
@@ -433,7 +422,7 @@ class IResolver(IResolverSimple):
def lookupAuthority(
name: str, timeout: Sequence[int]
- ) -> "Deferred[Tuple[RRHeader, RRHeader, RRHeader]]":
+ ) -> Deferred[tuple[RRHeader, RRHeader, RRHeader]]:
"""
Perform an SOA record lookup.
@@ -452,7 +441,7 @@ class IResolver(IResolverSimple):
def lookupNull(
name: str, timeout: Sequence[int]
- ) -> "Deferred[Tuple[RRHeader, RRHeader, RRHeader]]":
+ ) -> Deferred[tuple[RRHeader, RRHeader, RRHeader]]:
"""
Perform a NULL record lookup.
@@ -471,7 +460,7 @@ class IResolver(IResolverSimple):
def lookupWellKnownServices(
name: str, timeout: Sequence[int]
- ) -> "Deferred[Tuple[RRHeader, RRHeader, RRHeader]]":
+ ) -> Deferred[tuple[RRHeader, RRHeader, RRHeader]]:
"""
Perform a WKS record lookup.
@@ -490,7 +479,7 @@ class IResolver(IResolverSimple):
def lookupHostInfo(
name: str, timeout: Sequence[int]
- ) -> "Deferred[Tuple[RRHeader, RRHeader, RRHeader]]":
+ ) -> Deferred[tuple[RRHeader, RRHeader, RRHeader]]:
"""
Perform a HINFO record lookup.
@@ -509,7 +498,7 @@ class IResolver(IResolverSimple):
def lookupMailboxInfo(
name: str, timeout: Sequence[int]
- ) -> "Deferred[Tuple[RRHeader, RRHeader, RRHeader]]":
+ ) -> Deferred[tuple[RRHeader, RRHeader, RRHeader]]:
"""
Perform an MINFO record lookup.
@@ -528,7 +517,7 @@ class IResolver(IResolverSimple):
def lookupText(
name: str, timeout: Sequence[int]
- ) -> "Deferred[Tuple[RRHeader, RRHeader, RRHeader]]":
+ ) -> Deferred[tuple[RRHeader, RRHeader, RRHeader]]:
"""
Perform a TXT record lookup.
@@ -547,7 +536,7 @@ class IResolver(IResolverSimple):
def lookupResponsibility(
name: str, timeout: Sequence[int]
- ) -> "Deferred[Tuple[RRHeader, RRHeader, RRHeader]]":
+ ) -> Deferred[tuple[RRHeader, RRHeader, RRHeader]]:
"""
Perform an RP record lookup.
@@ -566,7 +555,7 @@ class IResolver(IResolverSimple):
def lookupAFSDatabase(
name: str, timeout: Sequence[int]
- ) -> "Deferred[Tuple[RRHeader, RRHeader, RRHeader]]":
+ ) -> Deferred[tuple[RRHeader, RRHeader, RRHeader]]:
"""
Perform an AFSDB record lookup.
@@ -585,7 +574,7 @@ class IResolver(IResolverSimple):
def lookupService(
name: str, timeout: Sequence[int]
- ) -> "Deferred[Tuple[RRHeader, RRHeader, RRHeader]]":
+ ) -> Deferred[tuple[RRHeader, RRHeader, RRHeader]]:
"""
Perform an SRV record lookup.
@@ -604,7 +593,7 @@ class IResolver(IResolverSimple):
def lookupAllRecords(
name: str, timeout: Sequence[int]
- ) -> "Deferred[Tuple[RRHeader, RRHeader, RRHeader]]":
+ ) -> Deferred[tuple[RRHeader, RRHeader, RRHeader]]:
"""
Perform an ALL_RECORD lookup.
@@ -623,7 +612,7 @@ class IResolver(IResolverSimple):
def lookupSenderPolicy(
name: str, timeout: Sequence[int]
- ) -> "Deferred[Tuple[RRHeader, RRHeader, RRHeader]]":
+ ) -> Deferred[tuple[RRHeader, RRHeader, RRHeader]]:
"""
Perform a SPF record lookup.
@@ -642,7 +631,7 @@ class IResolver(IResolverSimple):
def lookupNamingAuthorityPointer(
name: str, timeout: Sequence[int]
- ) -> "Deferred[Tuple[RRHeader, RRHeader, RRHeader]]":
+ ) -> Deferred[tuple[RRHeader, RRHeader, RRHeader]]:
"""
Perform a NAPTR record lookup.
@@ -661,7 +650,7 @@ class IResolver(IResolverSimple):
def lookupZone(
name: str, timeout: Sequence[int]
- ) -> "Deferred[Tuple[RRHeader, RRHeader, RRHeader]]":
+ ) -> Deferred[tuple[RRHeader, RRHeader, RRHeader]]:
"""
Perform an AXFR record lookup.
@@ -689,10 +678,10 @@ class IResolver(IResolverSimple):
class IReactorTCP(Interface):
def listenTCP(
port: int,
- factory: "ServerFactory",
+ factory: ServerFactory,
backlog: int = 50,
interface: str = "",
- ) -> "IListeningPort":
+ ) -> IListeningPort:
"""
Connects a given protocol factory to the given numeric TCP/IP port.
@@ -714,9 +703,9 @@ class IReactorTCP(Interface):
def connectTCP(
host: str,
port: int,
- factory: "ClientFactory",
+ factory: ClientFactory[P],
timeout: float = 30.0,
- bindAddress: Optional[Tuple[str, int]] = None,
+ bindAddress: tuple[str, int] | None = None,
) -> IConnector:
"""
Connect a TCP client.
@@ -741,10 +730,10 @@ class IReactorSSL(Interface):
def connectSSL(
host: str,
port: int,
- factory: "ClientFactory",
- contextFactory: "ClientContextFactory",
+ factory: ClientFactory[P],
+ contextFactory: ClientContextFactory,
timeout: float,
- bindAddress: Optional[Tuple[str, int]],
+ bindAddress: tuple[str, int] | None,
) -> IConnector:
"""
Connect a client Protocol to a remote SSL socket.
@@ -763,11 +752,11 @@ class IReactorSSL(Interface):
def listenSSL(
port: int,
- factory: "ServerFactory",
- contextFactory: "IOpenSSLContextFactory",
- backlog: int,
- interface: str,
- ) -> "IListeningPort":
+ factory: ServerFactory[P],
+ contextFactory: IOpenSSLContextFactory,
+ backlog: int = 50,
+ interface: str = "",
+ ) -> IListeningPort:
"""
Connects a given protocol factory to the given numeric TCP/IP port.
The connection is a SSL one, using contexts created by the context
@@ -788,7 +777,7 @@ class IReactorUNIX(Interface):
def connectUNIX(
address: str,
- factory: "ClientFactory",
+ factory: ClientFactory,
timeout: float = 30,
checkPID: bool = False,
) -> IConnector:
@@ -808,11 +797,11 @@ class IReactorUNIX(Interface):
def listenUNIX(
address: str,
- factory: "Factory",
+ factory: Factory,
backlog: int = 50,
mode: int = 0o666,
wantPID: bool = False,
- ) -> "IListeningPort":
+ ) -> IListeningPort:
"""
Listen on a UNIX socket.
@@ -836,10 +825,10 @@ class IReactorUNIXDatagram(Interface):
def connectUNIXDatagram(
address: str,
- protocol: "ConnectedDatagramProtocol",
+ protocol: ConnectedDatagramProtocol,
maxPacketSize: int,
mode: int,
- bindAddress: Optional[Tuple[str, int]],
+ bindAddress: tuple[str, int] | None,
) -> IConnector:
"""
Connect a client protocol to a datagram UNIX socket.
@@ -857,8 +846,8 @@ class IReactorUNIXDatagram(Interface):
"""
def listenUNIXDatagram(
- address: str, protocol: "DatagramProtocol", maxPacketSize: int, mode: int
- ) -> "IListeningPort":
+ address: str, protocol: DatagramProtocol, maxPacketSize: int, mode: int
+ ) -> IListeningPort:
"""
Listen on a datagram UNIX socket.
@@ -880,7 +869,7 @@ class IReactorWin32Events(Interface):
@since: 10.2
"""
- def addEvent(event: object, fd: "FileDescriptor", action: str) -> None:
+ def addEvent(event: object, fd: FileDescriptor, action: str) -> None:
"""
Add a new win32 event to the event loop.
@@ -906,8 +895,8 @@ class IReactorUDP(Interface):
"""
def listenUDP(
- port: int, protocol: "DatagramProtocol", interface: str, maxPacketSize: int
- ) -> "IListeningPort":
+ port: int, protocol: DatagramProtocol, interface: str, maxPacketSize: int
+ ) -> IListeningPort:
"""
Connects a given L{DatagramProtocol} to the given numeric UDP port.
@@ -932,7 +921,7 @@ class IReactorMulticast(Interface):
def listenMulticast(
port: int,
- protocol: "DatagramProtocol",
+ protocol: DatagramProtocol,
interface: str = "",
maxPacketSize: int = 8192,
listenMultiple: bool = False,
@@ -1002,8 +991,8 @@ class IReactorSocket(Interface):
"""
def adoptStreamPort(
- fileDescriptor: int, addressFamily: "AddressFamily", factory: "ServerFactory"
- ) -> "IListeningPort":
+ fileDescriptor: int, addressFamily: AddressFamily, factory: ServerFactory
+ ) -> IListeningPort:
"""
Add an existing listening I{SOCK_STREAM} socket to the reactor to
monitor for new connections to accept and handle.
@@ -1030,7 +1019,7 @@ class IReactorSocket(Interface):
"""
def adoptStreamConnection(
- fileDescriptor: int, addressFamily: "AddressFamily", factory: "ServerFactory"
+ fileDescriptor: int, addressFamily: AddressFamily, factory: ServerFactory
) -> None:
"""
Add an existing connected I{SOCK_STREAM} socket to the reactor to
@@ -1060,10 +1049,10 @@ class IReactorSocket(Interface):
def adoptDatagramPort(
fileDescriptor: int,
- addressFamily: "AddressFamily",
- protocol: "DatagramProtocol",
+ addressFamily: AddressFamily,
+ protocol: DatagramProtocol,
maxPacketSize: int,
- ) -> "IListeningPort":
+ ) -> IListeningPort:
"""
Add an existing listening I{SOCK_DGRAM} socket to the reactor to
monitor for read and write readiness.
@@ -1092,16 +1081,16 @@ class IReactorSocket(Interface):
class IReactorProcess(Interface):
def spawnProcess(
- processProtocol: "IProcessProtocol",
- executable: Union[bytes, str],
- args: Sequence[Union[bytes, str]],
- env: Optional[Mapping[AnyStr, AnyStr]] = None,
- path: Union[None, bytes, str] = None,
- uid: Optional[int] = None,
- gid: Optional[int] = None,
+ processProtocol: IProcessProtocol,
+ executable: bytes | str,
+ args: Sequence[bytes | str],
+ env: Mapping[AnyStr, AnyStr] | None = None,
+ path: None | bytes | str = None,
+ uid: int | None = None,
+ gid: int | None = None,
usePTY: bool = False,
- childFDs: Optional[Mapping[int, Union[int, str]]] = None,
- ) -> "IProcessTransport":
+ childFDs: Mapping[int, int | str] | None = None,
+ ) -> IProcessTransport:
"""
Spawn a process, with a process protocol.
@@ -1202,7 +1191,7 @@ class IReactorTime(Interface):
def callLater(
delay: float, callable: Callable[..., Any], *args: object, **kwargs: object
- ) -> "IDelayedCall":
+ ) -> IDelayedCall:
"""
Call a function later.
@@ -1217,7 +1206,7 @@ class IReactorTime(Interface):
C{reset()} methods.
"""
- def getDelayedCalls() -> Sequence["IDelayedCall"]:
+ def getDelayedCalls() -> Sequence[IDelayedCall]:
"""
See L{twisted.internet.interfaces.IReactorTime.getDelayedCalls}
"""
@@ -1331,7 +1320,7 @@ class IReactorThreads(IReactorFromThreads, IReactorInThreads):
Internally, this should use a thread pool and dispatch methods to them.
"""
- def getThreadPool() -> "ThreadPool":
+ def getThreadPool() -> ThreadPool:
"""
Return the threadpool used by L{IReactorInThreads.callInThread}.
Create it first if necessary.
@@ -1354,7 +1343,7 @@ class IReactorCore(Interface):
"I{during shutdown} and C{False} the rest of the time."
)
- def resolve(name: str, timeout: Sequence[int] = (1, 3, 11, 45)) -> "Deferred[str]":
+ def resolve(name: str, timeout: Sequence[int] = (1, 3, 11, 45)) -> Deferred[str]:
"""
Asynchronously resolve a hostname to a single IPv4 address.
@@ -1474,7 +1463,7 @@ class IReactorCore(Interface):
def callWhenRunning(
callable: Callable[..., Any], *args: object, **kwargs: object
- ) -> Optional[Any]:
+ ) -> Any | None:
"""
Call a function when the reactor is running.
@@ -1566,7 +1555,7 @@ class IReactorFDSet(Interface):
(or at least similarly opaque IDs returned from a .fileno() method)
"""
- def addReader(reader: "IReadDescriptor") -> None:
+ def addReader(reader: IReadDescriptor) -> None:
"""
I add reader to the set of file descriptors to get read events for.
@@ -1575,7 +1564,7 @@ class IReactorFDSet(Interface):
L{removeReader}.
"""
- def addWriter(writer: "IWriteDescriptor") -> None:
+ def addWriter(writer: IWriteDescriptor) -> None:
"""
I add writer to the set of file descriptors to get write events for.
@@ -1584,17 +1573,17 @@ class IReactorFDSet(Interface):
L{removeWriter}.
"""
- def removeReader(reader: "IReadDescriptor") -> None:
+ def removeReader(reader: IReadDescriptor) -> None:
"""
Removes an object previously added with L{addReader}.
"""
- def removeWriter(writer: "IWriteDescriptor") -> None:
+ def removeWriter(writer: IWriteDescriptor) -> None:
"""
Removes an object previously added with L{addWriter}.
"""
- def removeAll() -> List[Union["IReadDescriptor", "IWriteDescriptor"]]:
+ def removeAll() -> list[IReadDescriptor | IWriteDescriptor]:
"""
Remove all readers and writers.
@@ -1604,7 +1593,7 @@ class IReactorFDSet(Interface):
which were removed.
"""
- def getReaders() -> List["IReadDescriptor"]:
+ def getReaders() -> list[IReadDescriptor]:
"""
Return the list of file descriptors currently monitored for input
events by the reactor.
@@ -1612,7 +1601,7 @@ class IReactorFDSet(Interface):
@return: the list of file descriptors monitored for input events.
"""
- def getWriters() -> List["IWriteDescriptor"]:
+ def getWriters() -> list[IWriteDescriptor]:
"""
Return the list file descriptors currently monitored for output events
by the reactor.
@@ -1635,7 +1624,7 @@ class IListeningPort(Interface):
port number).
"""
- def stopListening() -> Optional["Deferred[None]"]:
+ def stopListening() -> Deferred[None] | None:
"""
Stop listening on this port.
@@ -1703,7 +1692,7 @@ class IReadDescriptor(IFileDescriptor):
This interface is generally used in conjunction with L{IReactorFDSet}.
"""
- def doRead() -> Optional[Failure]:
+ def doRead() -> Failure | None:
"""
Some data is available for reading on your descriptor.
@@ -1720,7 +1709,7 @@ class IWriteDescriptor(IFileDescriptor):
This interface is generally used in conjunction with L{IReactorFDSet}.
"""
- def doWrite() -> Optional[Failure]:
+ def doWrite() -> Failure | None:
"""
Some data can be written to your descriptor.
@@ -1775,7 +1764,7 @@ class IConsumer(Interface):
A consumer consumes data from a producer.
"""
- def registerProducer(producer: "IProducer", streaming: bool) -> None:
+ def registerProducer(producer: IProducer, streaming: bool) -> None:
"""
Register to receive data from a producer.
@@ -1905,7 +1894,7 @@ class IProtocol(Interface):
of one of those).
"""
- def makeConnection(transport: "ITransport") -> None:
+ def makeConnection(transport: ITransport) -> None:
"""
Make a connection to a transport and a server.
"""
@@ -1928,7 +1917,7 @@ class IProcessProtocol(Interface):
Interface for process-related event handlers.
"""
- def makeConnection(process: "IProcessTransport") -> None:
+ def makeConnection(process: IProcessTransport) -> None:
"""
Called when the process has been created.
@@ -2060,7 +2049,7 @@ class IProtocolFactory(Interface):
Interface for protocol factories.
"""
- def buildProtocol(addr: IAddress) -> Optional[IProtocol]:
+ def buildProtocol(addr: IAddress) -> IProtocol | None:
"""
Called when a connection has been established to addr.
@@ -2203,12 +2192,12 @@ class ITCPTransport(ITransport):
to allow detection of lost peers in a non-infinite amount of time.
"""
- def getHost() -> Union["IPv4Address", "IPv6Address"]:
+ def getHost() -> IPv4Address | IPv6Address:
"""
Returns L{IPv4Address} or L{IPv6Address}.
"""
- def getPeer() -> Union["IPv4Address", "IPv6Address"]:
+ def getPeer() -> IPv4Address | IPv6Address:
"""
Returns L{IPv4Address} or L{IPv6Address}.
"""
@@ -2255,8 +2244,8 @@ class IOpenSSLServerConnectionCreator(Interface):
"""
def serverConnectionForTLS(
- tlsProtocol: "TLSMemoryBIOProtocol",
- ) -> "OpenSSLConnection":
+ tlsProtocol: TLSMemoryBIOProtocol,
+ ) -> OpenSSLConnection:
"""
Create a connection for the given server protocol.
@@ -2279,8 +2268,8 @@ class IOpenSSLClientConnectionCreator(Interface):
"""
def clientConnectionForTLS(
- tlsProtocol: "TLSMemoryBIOProtocol",
- ) -> "OpenSSLConnection":
+ tlsProtocol: TLSMemoryBIOProtocol,
+ ) -> OpenSSLConnection:
"""
Create a connection for the given client protocol.
@@ -2301,7 +2290,7 @@ class IProtocolNegotiationFactory(Interface):
@see: L{twisted.internet.ssl}
"""
- def acceptableProtocols() -> List[bytes]:
+ def acceptableProtocols() -> list[bytes]:
"""
Returns a list of protocols that can be spoken by the connection
factory in the form of ALPN tokens, as laid out in the IANA registry
@@ -2321,7 +2310,7 @@ class IOpenSSLContextFactory(Interface):
@see: L{twisted.internet.ssl}
"""
- def getContext() -> "OpenSSLContext":
+ def getContext() -> OpenSSLContext:
"""
Returns a TLS context object, suitable for securing a TLS connection.
This context object will be appropriately customized for the connection
@@ -2339,9 +2328,9 @@ class ITLSTransport(ITCPTransport):
"""
def startTLS(
- contextFactory: Union[
- IOpenSSLClientConnectionCreator, IOpenSSLServerConnectionCreator
- ]
+ contextFactory: (
+ IOpenSSLClientConnectionCreator | IOpenSSLServerConnectionCreator
+ ),
) -> None:
"""
Initiate TLS negotiation.
@@ -2373,20 +2362,19 @@ class ISSLTransport(ITCPTransport):
class INegotiated(ISSLTransport):
"""
- A TLS based transport that supports using ALPN/NPN to negotiate the
- protocol to be used inside the encrypted tunnel.
+ A TLS based transport that supports using ALPN to negotiate the protocol to
+ be used inside the encrypted tunnel.
"""
- negotiatedProtocol = Attribute(
+ negotiatedProtocol: bytes | None = Attribute(
"""
- The protocol selected to be spoken using ALPN/NPN. The result from ALPN
- is preferred to the result from NPN if both were used. If the remote
- peer does not support ALPN or NPN, or neither NPN or ALPN are available
- on this machine, will be L{None}. Otherwise, will be the name of the
- selected protocol as C{bytes}. Note that until the handshake has
- completed this property may incorrectly return L{None}: wait until data
- has been received before trusting it (see
- https://twistedmatrix.com/trac/ticket/6024).
+ The protocol selected to be spoken using ALPN. If the remote peer does
+ not support ALPN, or ALPN is not available via local TLS libraries,
+ C{negotiatedProtocol} will be L{None}. Otherwise,
+ C{negotiatedProtocol} will be the name of the selected protocol as
+ C{bytes}. Until the TLS handshake has completed, this property may
+ incorrectly return L{None}: wait until data has been received before
+ trusting it. See U{https://github.com/twisted/twisted/issues/6024}.
"""
)
@@ -2404,7 +2392,7 @@ class IAcceptableCiphers(Interface):
A list of acceptable ciphers for a TLS context.
"""
- def selectCiphers(availableCiphers: Tuple[ICipher]) -> Tuple[ICipher]:
+ def selectCiphers(availableCiphers: tuple[ICipher]) -> tuple[ICipher]:
"""
Choose which ciphers to allow to be negotiated on a TLS connection.
@@ -2469,7 +2457,7 @@ class IProcessTransport(ITransport):
Close stdin, stderr and stdout.
"""
- def signalProcess(signalID: Union[str, int]) -> None:
+ def signalProcess(signalID: str | int) -> None:
"""
Send a signal to the process.
@@ -2516,7 +2504,7 @@ class IUDPTransport(Interface):
Transport for UDP DatagramProtocols.
"""
- def write(packet: bytes, addr: Optional[Tuple[str, int]]) -> None:
+ def write(packet: bytes, addr: tuple[str, int] | None) -> None:
"""
Write packet to given address.
@@ -2541,14 +2529,14 @@ class IUDPTransport(Interface):
@param port: port to connect to.
"""
- def getHost() -> Union["IPv4Address", "IPv6Address"]:
+ def getHost() -> IPv4Address | IPv6Address:
"""
Get this port's host address.
@return: an address describing the listening port.
"""
- def stopListening() -> Optional["Deferred[None]"]:
+ def stopListening() -> Deferred[None] | None:
"""
Stop listening on this port.
@@ -2581,7 +2569,7 @@ class IUNIXDatagramTransport(Interface):
Write packet to given address.
"""
- def getHost() -> "UNIXAddress":
+ def getHost() -> UNIXAddress:
"""
Returns L{UNIXAddress}.
"""
@@ -2597,12 +2585,12 @@ class IUNIXDatagramConnectedTransport(Interface):
Write packet to address we are connected to.
"""
- def getHost() -> "UNIXAddress":
+ def getHost() -> UNIXAddress:
"""
Returns L{UNIXAddress}.
"""
- def getPeer() -> "UNIXAddress":
+ def getPeer() -> UNIXAddress:
"""
Returns L{UNIXAddress}.
"""
@@ -2649,7 +2637,7 @@ class IMulticastTransport(IUDPTransport):
Set time to live on multicast packets.
"""
- def joinGroup(addr: str, interface: str = "") -> "Deferred[None]":
+ def joinGroup(addr: str, interface: str = "") -> Deferred[None]:
"""
Join a multicast group. Returns L{Deferred} of success or failure.
@@ -2657,7 +2645,7 @@ class IMulticastTransport(IUDPTransport):
L{error.MulticastJoinError}.
"""
- def leaveGroup(addr: str, interface: str = "") -> "Deferred[None]":
+ def leaveGroup(addr: str, interface: str = "") -> Deferred[None]:
"""
Leave multicast group, return L{Deferred} of success.
"""
@@ -2671,7 +2659,7 @@ class IStreamClientEndpoint(Interface):
@since: 10.1
"""
- def connect(protocolFactory: IProtocolFactory) -> "Deferred[IProtocol]":
+ def connect(protocolFactory: IProtocolFactory) -> Deferred[IProtocol]:
"""
Connect the C{protocolFactory} to the location specified by this
L{IStreamClientEndpoint} provider.
@@ -2692,7 +2680,7 @@ class IStreamServerEndpoint(Interface):
@since: 10.1
"""
- def listen(protocolFactory: IProtocolFactory) -> "Deferred[IListeningPort]":
+ def listen(protocolFactory: IProtocolFactory) -> Deferred[IListeningPort]:
"""
Listen with C{protocolFactory} at the location specified by this
L{IStreamServerEndpoint} provider.
diff --git a/contrib/python/Twisted/py3/twisted/internet/iocpreactor/reactor.py b/contrib/python/Twisted/py3/twisted/internet/iocpreactor/reactor.py
index 976d1096e72..363b1c9273a 100644
--- a/contrib/python/Twisted/py3/twisted/internet/iocpreactor/reactor.py
+++ b/contrib/python/Twisted/py3/twisted/internet/iocpreactor/reactor.py
@@ -10,7 +10,6 @@ Reactor that uses IO completion ports
import socket
import sys
import warnings
-from typing import Tuple, Type
from zope.interface import implementer
@@ -27,7 +26,7 @@ except ImportError:
TLSMemoryBIOFactory = None
# Either pyOpenSSL isn't installed, or it is too old for this code to work.
# The reactor won't provide IReactorSSL.
- _extraInterfaces: Tuple[Type[interfaces.IReactorSSL], ...] = ()
+ _extraInterfaces: tuple[type[interfaces.IReactorSSL], ...] = ()
warnings.warn(
"pyOpenSSL 0.10 or newer is required for SSL support in iocpreactor. "
"It is missing, so the reactor will not support SSL APIs."
diff --git a/contrib/python/Twisted/py3/twisted/internet/iocpreactor/tcp.py b/contrib/python/Twisted/py3/twisted/internet/iocpreactor/tcp.py
index ef72b7ed7e5..8bc3c1330f7 100644
--- a/contrib/python/Twisted/py3/twisted/internet/iocpreactor/tcp.py
+++ b/contrib/python/Twisted/py3/twisted/internet/iocpreactor/tcp.py
@@ -10,7 +10,7 @@ from __future__ import annotations
import errno
import socket
import struct
-from typing import TYPE_CHECKING, Optional, Union
+from typing import TYPE_CHECKING
from zope.interface import classImplements, implementer
@@ -356,8 +356,8 @@ class Server(Connection):
self,
sock: socket.socket,
protocol: IProtocol,
- clientAddr: Union[IPv4Address, IPv6Address],
- serverAddr: Union[IPv4Address, IPv6Address],
+ clientAddr: IPv4Address | IPv6Address,
+ serverAddr: IPv4Address | IPv6Address,
sessionno: int,
reactor: IOCPReactor,
):
@@ -422,7 +422,7 @@ class Port(_SocketCloser, _LogOwner):
# Actual port number being listened on, only set to a non-None
# value when we are actually listening.
- _realPortNumber: Optional[int] = None
+ _realPortNumber: int | None = None
# A string describing the connections which will be created by this port.
# Normally this is C{"TCP"}, since this is a TCP port, but when the TLS
diff --git a/contrib/python/Twisted/py3/twisted/internet/iocpreactor/udp.py b/contrib/python/Twisted/py3/twisted/internet/iocpreactor/udp.py
index 744efa03e1b..53c9e9176fd 100644
--- a/contrib/python/Twisted/py3/twisted/internet/iocpreactor/udp.py
+++ b/contrib/python/Twisted/py3/twisted/internet/iocpreactor/udp.py
@@ -11,7 +11,7 @@ import errno
import socket
import struct
import warnings
-from typing import TYPE_CHECKING, Optional
+from typing import TYPE_CHECKING
from zope.interface import implementer
@@ -53,7 +53,7 @@ class Port(abstract.FileHandle):
# Actual port number being listened on, only set to a non-None
# value when we are actually listening.
- _realPortNumber: Optional[int] = None
+ _realPortNumber: int | None = None
def __init__(
self,
diff --git a/contrib/python/Twisted/py3/twisted/internet/posixbase.py b/contrib/python/Twisted/py3/twisted/internet/posixbase.py
index d9fa8a9a554..19fc69ffb6f 100644
--- a/contrib/python/Twisted/py3/twisted/internet/posixbase.py
+++ b/contrib/python/Twisted/py3/twisted/internet/posixbase.py
@@ -10,7 +10,7 @@ from __future__ import annotations
import socket
import sys
-from typing import Sequence
+from collections.abc import Sequence
from zope.interface import classImplements, implementer
@@ -30,7 +30,7 @@ from twisted.internet.interfaces import (
IReactorUNIXDatagram,
)
from twisted.internet.main import CONNECTION_DONE, CONNECTION_LOST
-from twisted.internet.protocol import ClientFactory
+from twisted.internet.protocol import ClientFactory, P
from twisted.python import failure, log
from twisted.python.runtime import platform, platformType
from ._signals import (
@@ -370,7 +370,7 @@ class PosixReactorBase(_DisconnectSelectableMixin, ReactorBase):
self,
host: str,
port: int,
- factory: "ClientFactory",
+ factory: ClientFactory[P],
timeout: float = 30.0,
bindAddress: tuple[str, int] | None = None,
) -> IConnector:
diff --git a/contrib/python/Twisted/py3/twisted/internet/process.py b/contrib/python/Twisted/py3/twisted/internet/process.py
index ff7684e358b..d47494ab194 100644
--- a/contrib/python/Twisted/py3/twisted/internet/process.py
+++ b/contrib/python/Twisted/py3/twisted/internet/process.py
@@ -20,7 +20,7 @@ import stat
import sys
import traceback
from collections import defaultdict
-from typing import TYPE_CHECKING, Dict, List, Optional, Tuple
+from typing import TYPE_CHECKING
_PS_CLOSE: int
_PS_DUP2: int
@@ -66,7 +66,7 @@ else:
# here for backwards compatibility:
ProcessExitedAlready = error.ProcessExitedAlready
-reapProcessHandlers: Dict[int, _BaseProcess] = {}
+reapProcessHandlers: dict[int, _BaseProcess] = {}
def reapAllProcesses() -> None:
@@ -285,7 +285,7 @@ class _BaseProcess(BaseProcess):
Base class for Process and PTYProcess.
"""
- status: Optional[int] = None
+ status: int | None = None
pid = None
def reapProcess(self):
@@ -632,11 +632,11 @@ def _listOpenFDs():
def _getFileActions(
- fdState: List[Tuple[int, bool]],
- childToParentFD: Dict[int, int],
+ fdState: list[tuple[int, bool]],
+ childToParentFD: dict[int, int],
doClose: int,
doDup2: int,
-) -> List[Tuple[int, ...]]:
+) -> list[tuple[int, ...]]:
"""
Get the C{file_actions} parameter for C{posix_spawn} based on the
parameters describing the current process state.
@@ -649,7 +649,7 @@ def _getFileActions(
@param doDup2: the integer to use for the 'dup2' instruction
"""
fdStateDict = dict(fdState)
- parentToChildren: Dict[int, List[int]] = defaultdict(list)
+ parentToChildren: dict[int, list[int]] = defaultdict(list)
for inChild, inParent in childToParentFD.items():
parentToChildren[inParent].append(inChild)
allocated = set(fdStateDict)
@@ -664,7 +664,7 @@ def _getFileActions(
allocated.add(nextFD)
return nextFD
- result: List[Tuple[int, ...]] = []
+ result: list[tuple[int, ...]] = []
relocations = {}
for inChild, inParent in sorted(childToParentFD.items()):
# The parent FD will later be reused by a child FD.
diff --git a/contrib/python/Twisted/py3/twisted/internet/protocol.py b/contrib/python/Twisted/py3/twisted/internet/protocol.py
index bd1e647302b..b2373b656b3 100644
--- a/contrib/python/Twisted/py3/twisted/internet/protocol.py
+++ b/contrib/python/Twisted/py3/twisted/internet/protocol.py
@@ -11,23 +11,71 @@ Twisted. The Protocol class contains some introductory material.
from __future__ import annotations
import random
-from typing import Any, Callable, Optional
+from typing import Any, Callable, Generic, Protocol as TypingProtocol
from zope.interface import implementer
+from typing_extensions import ParamSpec, Self, TypeVar
+
from twisted.internet import defer, error, interfaces
from twisted.internet.interfaces import (
IAddress,
+ IConnector,
IMulticastTransport,
ITransport,
IUDPTransport,
)
from twisted.logger import _loggerFor
from twisted.python import components, failure, log
+from twisted.python.failure import Failure
+
+P = TypeVar("P", bound="_ProtoWithFactory", default="_ProtoWithFactory")
+SomeProtocol = TypeVar("SomeProtocol")
+_FactoryParams = ParamSpec("_FactoryParams")
+R = TypeVar("R", bound="Factory")
+_Value = TypeVar("_Value", covariant=True)
+
+
+class _LSPViolationHelper(Generic[_Value]):
+ def __get__( # type:ignore[empty-body]
+ self, instance: object, owner: type[object] | None = None
+ ) -> _Value:
+ ...
+
+ def __set__(self, instance: object, value: Any) -> None:
+ ...
+
+ def __delete__(self, instance: object) -> None:
+ ...
+
+
+@implementer(interfaces.IProtocol)
+class _ProtoWithFactory(TypingProtocol):
+ # factory: _LSPViolationHelper[Factory[Self]]
+
+ @property
+ def factory(self) -> Factory[Self]:
+ ...
+
+ @factory.setter
+ def factory(self, value: Any) -> None:
+ ...
+
+ def dataReceived(self, data: bytes) -> None:
+ ...
+
+ def connectionLost(self, reason: Failure) -> None:
+ ...
+
+ def makeConnection(self, transport: ITransport) -> None:
+ ...
+
+ def connectionMade(self) -> None:
+ ...
@implementer(interfaces.IProtocolFactory, interfaces.ILoggingContext)
-class Factory:
+class Factory(Generic[P]):
"""
This is a factory which produces protocols.
@@ -35,13 +83,18 @@ class Factory:
self.protocol.
"""
- protocol: "Optional[Callable[[], Protocol]]" = None
+ protocol: Callable[..., P] | None = None
numPorts = 0
noisy = True
@classmethod
- def forProtocol(cls, protocol, *args, **kwargs):
+ def forProtocol(
+ cls: Callable[_FactoryParams, R],
+ protocol: Callable[[], P],
+ *args: _FactoryParams.args,
+ **kwargs: _FactoryParams.kwargs,
+ ) -> Factory[P]:
"""
Create a factory for the given protocol.
@@ -58,7 +111,7 @@ class Factory:
"""
factory = cls(*args, **kwargs)
factory.protocol = protocol
- return factory
+ return factory # type:ignore[return-value]
def logPrefix(self):
"""
@@ -118,7 +171,7 @@ class Factory:
directly.
"""
- def buildProtocol(self, addr: IAddress) -> "Optional[Protocol]":
+ def buildProtocol(self, addr: IAddress | None) -> P | None:
"""
Create an instance of a subclass of Protocol.
@@ -139,7 +192,7 @@ class Factory:
return p
-class ClientFactory(Factory):
+class ClientFactory(Factory[P]):
"""
A Protocol factory for clients.
@@ -147,7 +200,7 @@ class ClientFactory(Factory):
reactors.
"""
- def startedConnecting(self, connector):
+ def startedConnecting(self, connector: IConnector) -> None:
"""
Called when a connection has been started.
@@ -156,22 +209,18 @@ class ClientFactory(Factory):
@param connector: a Connector object.
"""
- def clientConnectionFailed(self, connector, reason):
+ def clientConnectionFailed(self, connector: IConnector, reason: Failure) -> None:
"""
Called when a connection has failed to connect.
It may be useful to call connector.connect() - this will reconnect.
-
- @type reason: L{twisted.python.failure.Failure}
"""
- def clientConnectionLost(self, connector, reason):
+ def clientConnectionLost(self, connector: IConnector, reason: Failure) -> None:
"""
Called when an established connection is lost.
It may be useful to call connector.connect() - this will reconnect.
-
- @type reason: L{twisted.python.failure.Failure}
"""
@@ -415,7 +464,6 @@ class ReconnectingClientFactory(ClientFactory):
log.msg("Abandoning %s after %d retries." % (connector, self.retries))
return
- self.delay = min(self.delay * self.factor, self.maxDelay)
if self.jitter:
self.delay = random.normalvariate(self.delay, self.delay * self.jitter)
@@ -438,6 +486,8 @@ class ReconnectingClientFactory(ClientFactory):
self.clock = reactor
self._callID = self.clock.callLater(self.delay, reconnector)
+ self.delay = min(self.delay * self.factor, self.maxDelay)
+
def stopTrying(self):
"""
Put a stop to any attempt to reconnect in progress.
@@ -484,7 +534,7 @@ class ReconnectingClientFactory(ClientFactory):
return state
-class ServerFactory(Factory):
+class ServerFactory(Factory[P]):
"""
Subclass this to indicate that your protocol.Factory is only usable for servers.
"""
@@ -500,7 +550,7 @@ class BaseProtocol:
"""
connected = 0
- transport: Optional[ITransport] = None
+ transport: ITransport | None = None
def makeConnection(self, transport):
"""
@@ -549,7 +599,7 @@ class Protocol(BaseProtocol):
see the L{twisted.protocols.basic} module for a few of them.
"""
- factory: Optional[Factory] = None
+ factory: Any = None
def logPrefix(self):
"""
@@ -628,7 +678,7 @@ class ProcessProtocol(BaseProtocol):
stdin, stdout, and stderr file descriptors.
"""
- transport: Optional[interfaces.IProcessTransport] = None
+ transport: interfaces.IProcessTransport | None = None
def childDataReceived(self, childFD: int, data: bytes) -> None:
if childFD == 1:
diff --git a/contrib/python/Twisted/py3/twisted/internet/reactor.py b/contrib/python/Twisted/py3/twisted/internet/reactor.py
index 00f1ef6e012..6be09f5c8f6 100644
--- a/contrib/python/Twisted/py3/twisted/internet/reactor.py
+++ b/contrib/python/Twisted/py3/twisted/internet/reactor.py
@@ -3,32 +3,93 @@
"""
The reactor is the Twisted event loop within Twisted, the loop which drives
-applications using Twisted. The reactor provides APIs for networking,
+applications using Twisted. The reactor provides APIs for networking,
threading, dispatching events, and more.
-The default reactor depends on the platform and will be installed if this
-module is imported without another reactor being explicitly installed
-beforehand. Regardless of which reactor is installed, importing this module is
-the correct way to get a reference to it.
+This module is the way to access the current global reactor, by doing::
-New application code should prefer to pass and accept the reactor as a
-parameter where it is needed, rather than relying on being able to import this
-module to get a reference. This simplifies unit testing and may make it easier
-to one day support multiple reactors (as a performance enhancement), though
-this is not currently possible.
+ from twisted.internet import reactor
+
+The specific capabilities of the default reactor depends on your platform, and
+will be installed if this module is imported without another reactor being
+explicitly installed beforehand. Regardless of which reactor is installed,
+importing this module is the way to get a reference to it.
+
+In order to minimize dependencies on mutable global state, new application code
+should prefer to pass and accept the reactor as a parameter where it is needed,
+rather than relying on being able to import this module to get a reference.
+
+To get the reactor at the beginning of your program without using this global
+import, use L{twisted.internet.task.react}, like so::
+
+ # If you need a custom reactor, you can install it at the beginning:
+ from twisted.internet.custom_reactor import install
+ install()
+
+ from twisted.internet.task import react
+ from twisted.internet.defer import Deferred
+
+ async def main(reactor: IReactorTCP) -> None:
+ reactor.listenTCP(...)
+ await Deferred() # wait forever
+
+ if __name__ == '__main__':
+ react(main, ())
+
+This simplifies testing and may make it easier to one day support multiple
+reactors.
+
+@note: Another reason to prefer getting the reactor as a parameter is that type
+ information about the object you get from C{from twisted.internet import
+ reactor} is always slightly inaccurate. At runtime, interfaces are only
+ contextually provided depending upon the installed reactor, the installed
+ libraries, and the capabilities of your platform, so there is no "correct"
+ type for this object to statically be. Thus, it provides a conglomeration
+ of many of the most common reactor interfaces which are usually available.
+
+ To be fully correct in new code, it's best to always adapt the reactor to
+ your desired interface in order to interrogate its capabilities; for
+ example, if you need to call a method on
+ L{IReactorSSL<twisted.internet.interfaces.IReactorSSL>} but provide useful
+ error messages if it's not available, you can do something like this::
+
+ from twisted.internet import reactor
+
+ if (sslReactor := IReactorSSL(reactor, None)) is not None:
+ sslReactor.listenSSL(...)
+ else:
+ print("TLS support not available.")
@see: L{IReactorCore<twisted.internet.interfaces.IReactorCore>}
+
@see: L{IReactorTime<twisted.internet.interfaces.IReactorTime>}
+
@see: L{IReactorProcess<twisted.internet.interfaces.IReactorProcess>}
+
@see: L{IReactorTCP<twisted.internet.interfaces.IReactorTCP>}
+
@see: L{IReactorSSL<twisted.internet.interfaces.IReactorSSL>}
+
@see: L{IReactorUDP<twisted.internet.interfaces.IReactorUDP>}
+
@see: L{IReactorMulticast<twisted.internet.interfaces.IReactorMulticast>}
+
@see: L{IReactorUNIX<twisted.internet.interfaces.IReactorUNIX>}
+
@see: L{IReactorUNIXDatagram<twisted.internet.interfaces.IReactorUNIXDatagram>}
+
@see: L{IReactorFDSet<twisted.internet.interfaces.IReactorFDSet>}
+
+@see: L{IReactorSocket<twisted.internet.interfaces.IReactorSocket>}
+
+@see: L{IReactorWin32Events<twisted.internet.interfaces.IReactorWin32Events>}
+
@see: L{IReactorThreads<twisted.internet.interfaces.IReactorThreads>}
-@see: L{IReactorPluggableResolver<twisted.internet.interfaces.IReactorPluggableResolver>}
+
+@see:
+ L{IReactorPluggableResolver<twisted.internet.interfaces.IReactorPluggableResolver>}
+
+@see: L{IReactorDaemonize<twisted.internet.interfaces.IReactorDaemonize>}
"""
diff --git a/contrib/python/Twisted/py3/twisted/internet/selectreactor.py b/contrib/python/Twisted/py3/twisted/internet/selectreactor.py
index ee233f7f10b..0e7235d0f5b 100644
--- a/contrib/python/Twisted/py3/twisted/internet/selectreactor.py
+++ b/contrib/python/Twisted/py3/twisted/internet/selectreactor.py
@@ -11,7 +11,7 @@ import select
import sys
from errno import EBADF, EINTR
from time import sleep
-from typing import Callable, Type, TypeVar
+from typing import Callable, TypeVar
from zope.interface import implementer
@@ -48,7 +48,7 @@ else:
try:
from twisted.internet.win32eventreactor import _ThreadedWin32EventsMixin
except ImportError:
- _extraBase: Type[object] = object
+ _extraBase: type[object] = object
else:
_extraBase = _ThreadedWin32EventsMixin
@@ -153,7 +153,7 @@ class SelectReactor(posixbase.PosixReactorBase, _extraBase): # type: ignore[mis
for selectable in selectables:
# if this was disconnected in another thread, kill it.
# ^^^^ --- what the !@#*? serious! -exarkun
- if selectable not in fdset: # type:ignore[operator]
+ if selectable not in fdset:
continue
# This for pausing input when we're not ready for more.
_logrun(selectable, _drdw, selectable, method)
diff --git a/contrib/python/Twisted/py3/twisted/internet/task.py b/contrib/python/Twisted/py3/twisted/internet/task.py
index 0319d24a7a4..dba790febff 100644
--- a/contrib/python/Twisted/py3/twisted/internet/task.py
+++ b/contrib/python/Twisted/py3/twisted/internet/task.py
@@ -5,24 +5,13 @@
"""
Scheduling utility methods and classes.
"""
-
+from __future__ import annotations
import sys
import time
import warnings
-from typing import (
- Callable,
- Coroutine,
- Iterable,
- Iterator,
- List,
- NoReturn,
- Optional,
- Sequence,
- TypeVar,
- Union,
- cast,
-)
+from collections.abc import Coroutine, Iterable, Iterator, Sequence
+from typing import Any, Callable, Generic, NoReturn, TypeVar, cast
from zope.interface import implementer
@@ -67,13 +56,13 @@ class LoopingCall:
to L{LoopingCall.start}.
"""
- call: Optional[IDelayedCall] = None
+ call: IDelayedCall | None = None
running = False
- _deferred: Optional[Deferred["LoopingCall"]] = None
- interval: Optional[float] = None
+ _deferred: Deferred[LoopingCall] | None = None
+ interval: float | None = None
_runAtStart = False
- starttime: Optional[float] = None
- _realLastTime: Optional[float] = None
+ starttime: float | None = None
+ _realLastTime: float | None = None
def __init__(self, f: Callable[..., object], *a: object, **kw: object) -> None:
self.f = f
@@ -84,7 +73,7 @@ class LoopingCall:
self.clock = cast(IReactorTime, reactor)
@property
- def deferred(self) -> Optional[Deferred["LoopingCall"]]:
+ def deferred(self) -> Deferred[LoopingCall] | None:
"""
DEPRECATED. L{Deferred} fired when loop stops or fails.
@@ -100,7 +89,7 @@ class LoopingCall:
return self._deferred
@classmethod
- def withCount(cls, countCallable: Callable[[int], object]) -> "LoopingCall":
+ def withCount(cls, countCallable: Callable[[int], object]) -> LoopingCall:
"""
An alternate constructor for L{LoopingCall} that makes available the
number of calls which should have occurred since it was last invoked.
@@ -176,7 +165,7 @@ class LoopingCall:
intervalNum = int(elapsedTime / self.interval)
return intervalNum
- def start(self, interval: float, now: bool = True) -> Deferred["LoopingCall"]:
+ def start(self, interval: float, now: bool = True) -> Deferred[LoopingCall]:
"""
Start running function every interval seconds.
@@ -379,15 +368,18 @@ def _defaultScheduler(callable: Callable[[], None]) -> IDelayedCall:
return cast(IReactorTime, reactor).callLater(_EPSILON, callable)
-_TaskResultT = TypeVar("_TaskResultT")
+_TaskIteratorT = TypeVar("_TaskIteratorT", bound=Iterator[object])
-class CooperativeTask:
+class CooperativeTask(Generic[_TaskIteratorT]):
"""
A L{CooperativeTask} is a task object inside a L{Cooperator}, which can be
paused, resumed, and stopped. It can also have its completion (or
termination) monitored.
+ It is generic over a task iterator type, L{_TaskIteratorT}, which is the
+ type of the iterator passed to it and also the type of its result.
+
@see: L{Cooperator.cooperate}
@ivar _iterator: the iterator to iterate when this L{CooperativeTask} is
@@ -411,7 +403,9 @@ class CooperativeTask:
"""
def __init__(
- self, iterator: Iterator[_TaskResultT], cooperator: "Cooperator"
+ self,
+ iterator: _TaskIteratorT,
+ cooperator: Cooperator,
) -> None:
"""
A private constructor: to create a new L{CooperativeTask}, see
@@ -419,13 +413,13 @@ class CooperativeTask:
"""
self._iterator = iterator
self._cooperator = cooperator
- self._deferreds: List[Deferred[Iterator[_TaskResultT]]] = []
+ self._deferreds: list[Deferred[_TaskIteratorT]] = []
self._pauseCount = 0
- self._completionState: Optional[SchedulerError] = None
- self._completionResult: Optional[Union[Iterator[_TaskResultT], Failure]] = None
+ self._completionState: SchedulerError | None = None
+ self._completionResult: _TaskIteratorT | Failure | None = None
cooperator._addTask(self)
- def whenDone(self) -> Deferred[Iterator[_TaskResultT]]:
+ def whenDone(self) -> Deferred[_TaskIteratorT]:
"""
Get a L{Deferred} notification of when this task is complete.
@@ -437,7 +431,7 @@ class CooperativeTask:
@rtype: L{Deferred}
"""
- d: Deferred[Iterator[_TaskResultT]] = Deferred()
+ d: Deferred[_TaskIteratorT] = Deferred()
if self._completionState is None:
self._deferreds.append(d)
else:
@@ -474,7 +468,7 @@ class CooperativeTask:
def _completeWith(
self,
completionState: SchedulerError,
- deferredResult: Union[Iterator[_TaskResultT], Failure],
+ deferredResult: _TaskIteratorT | Failure,
) -> None:
"""
@param completionState: a L{SchedulerError} exception or a subclass
@@ -594,25 +588,28 @@ class Cooperator:
stepped as soon as they are added, or if they will be queued up until
L{Cooperator.start} is called.
"""
- self._tasks: List[CooperativeTask] = []
- self._metarator: Iterator[CooperativeTask] = iter(())
+ self._tasks: list[CooperativeTask[Iterator[object]]] = []
+ self._metarator: Iterator[CooperativeTask[Iterator[object]]] = iter(())
self._terminationPredicateFactory = terminationPredicateFactory
self._scheduler = scheduler
- self._delayedCall: Optional[IDelayedCall] = None
+ self._delayedCall: IDelayedCall | None = None
self._stopped = False
self._started = started
def coiterate(
self,
- iterator: Iterator[_TaskResultT],
- doneDeferred: Optional[Deferred[Iterator[_TaskResultT]]] = None,
- ) -> Deferred[Iterator[_TaskResultT]]:
+ iterator: _TaskIteratorT,
+ doneDeferred: Deferred[_TaskIteratorT] | None = None,
+ ) -> Deferred[_TaskIteratorT]:
"""
Add an iterator to the list of iterators this L{Cooperator} is
currently running.
- Equivalent to L{cooperate}, but returns a L{Deferred} that will
- be fired when the task is done.
+ Equivalent to L{cooperate}, but returns a L{Deferred} that will be
+ fired when the task is done.
+
+ @note: The type of value yielded by the given iterator must match that
+ of the other iterators added to this cooperator.
@param doneDeferred: If specified, this will be the Deferred used as
the completion deferred. It is suggested that you use the default,
@@ -622,13 +619,11 @@ class Cooperator:
"""
if doneDeferred is None:
doneDeferred = Deferred()
- whenDone: Deferred[Iterator[_TaskResultT]] = CooperativeTask(
- iterator, self
- ).whenDone()
+ whenDone: Deferred[_TaskIteratorT] = CooperativeTask(iterator, self).whenDone()
whenDone.chainDeferred(doneDeferred)
return doneDeferred
- def cooperate(self, iterator: Iterator[_TaskResultT]) -> CooperativeTask:
+ def cooperate(self, iterator: _TaskIteratorT) -> CooperativeTask[_TaskIteratorT]:
"""
Start running the given iterator as a long-running cooperative task, by
calling next() on it as a periodic timed event.
@@ -639,7 +634,7 @@ class Cooperator:
"""
return CooperativeTask(iterator, self)
- def _addTask(self, task: CooperativeTask) -> None:
+ def _addTask(self, task: CooperativeTask[Any]) -> None:
"""
Add a L{CooperativeTask} object to this L{Cooperator}.
"""
@@ -651,7 +646,7 @@ class Cooperator:
self._tasks.append(task)
self._reschedule()
- def _removeTask(self, task: CooperativeTask) -> None:
+ def _removeTask(self, task: CooperativeTask[Any]) -> None:
"""
Remove a L{CooperativeTask} from this L{Cooperator}.
"""
@@ -661,7 +656,7 @@ class Cooperator:
self._delayedCall.cancel()
self._delayedCall = None
- def _tasksWhileNotStopped(self) -> Iterable[CooperativeTask]:
+ def _tasksWhileNotStopped(self) -> Iterable[CooperativeTask[Iterator[object]]]:
"""
Yield all L{CooperativeTask} objects in a loop as long as this
L{Cooperator}'s termination condition has not been met.
@@ -729,7 +724,7 @@ class Cooperator:
_theCooperator = Cooperator()
-def coiterate(iterator: Iterator[_T]) -> Deferred[Iterator[_T]]:
+def coiterate(iterator: _TaskIteratorT) -> Deferred[_TaskIteratorT]:
"""
Cooperatively iterate over the given iterator, dividing runtime between it
and all other iterators which have been passed to this function and not yet
@@ -742,7 +737,7 @@ def coiterate(iterator: Iterator[_T]) -> Deferred[Iterator[_T]]:
return _theCooperator.coiterate(iterator)
-def cooperate(iterator: Iterator[_T]) -> CooperativeTask:
+def cooperate(iterator: _TaskIteratorT) -> CooperativeTask[_TaskIteratorT]:
"""
Start running the given iterator as a long-running cooperative task, by
calling next() on it as a periodic timed event.
@@ -771,7 +766,7 @@ class Clock:
rightNow = 0.0
def __init__(self) -> None:
- self.calls: List[DelayedCall] = []
+ self.calls: list[DelayedCall] = []
def seconds(self) -> float:
"""
@@ -841,7 +836,7 @@ class Clock:
def deferLater(
clock: IReactorTime,
delay: float,
- callable: Optional[Callable[..., _T]] = None,
+ callable: Callable[..., _T] | None = None,
*args: object,
**kw: object,
) -> Deferred[_T]:
@@ -880,10 +875,10 @@ def deferLater(
def react(
main: Callable[
...,
- Union[Deferred[_T], Coroutine["Deferred[_T]", object, _T]],
+ Deferred[_T] | Coroutine[Deferred[_T], object, _T],
],
argv: Iterable[object] = (),
- _reactor: Optional[IReactorCore] = None,
+ _reactor: IReactorCore | None = None,
) -> NoReturn:
"""
Call C{main} and run the reactor until the L{Deferred} it returns fires or
diff --git a/contrib/python/Twisted/py3/twisted/internet/tcp.py b/contrib/python/Twisted/py3/twisted/internet/tcp.py
index 7bc4d1eaac8..7a22be2163f 100644
--- a/contrib/python/Twisted/py3/twisted/internet/tcp.py
+++ b/contrib/python/Twisted/py3/twisted/internet/tcp.py
@@ -14,12 +14,11 @@ import os
import socket
import struct
import sys
-from typing import Any, Callable, ClassVar, List, Optional, Union
+from typing import TYPE_CHECKING, Any, Callable, ClassVar, Protocol as TypingProtocol
from zope.interface import Interface, implementer
import attr
-import typing_extensions
from twisted.internet.interfaces import (
IHalfCloseableProtocol,
@@ -29,7 +28,7 @@ from twisted.internet.interfaces import (
ISystemHandle,
ITCPTransport,
)
-from twisted.internet.protocol import ClientFactory
+from twisted.internet.protocol import ClientFactory, P
from twisted.logger import ILogObserver, LogEvent, Logger
from twisted.python import deprecate, versions
from twisted.python.runtime import platformType
@@ -57,11 +56,30 @@ except ImportError:
pass
-if platformType == "win32":
- # no such thing as WSAEPERM or error code 10001
+if platformType != "win32":
+ from errno import (
+ EAGAIN,
+ EALREADY,
+ ECONNABORTED,
+ EINPROGRESS,
+ EINVAL,
+ EISCONN,
+ EMFILE,
+ ENFILE,
+ ENOBUFS,
+ ENOMEM,
+ EPERM,
+ EWOULDBLOCK,
+ )
+ from os import strerror
+elif not TYPE_CHECKING: # pragma: no branch
+ # Need a coverage annotation here because we will never fall through this
+ # branch as TYPE_CHECKING is always False.
+
+ # No such thing as WSAEPERM or error code 10001
# according to winsock.h or MSDN
- EPERM = object()
- from errno import ( # type: ignore[attr-defined]
+ EPERM = object() # type:ignore[assignment]
+ from errno import ( # type: ignore[no-redef,attr-defined]
WSAEALREADY as EALREADY,
WSAEINPROGRESS as EINPROGRESS,
WSAEINVAL as EINVAL,
@@ -72,28 +90,13 @@ if platformType == "win32":
)
# No such thing as WSAENFILE, either.
- ENFILE = object()
+ ENFILE = object() # type:ignore[assignment]
# Nor ENOMEM
- ENOMEM = object()
+ ENOMEM = object() # type:ignore[assignment]
EAGAIN = EWOULDBLOCK
- from errno import WSAECONNRESET as ECONNABORTED # type: ignore[attr-defined]
+ from errno import WSAECONNRESET as ECONNABORTED # type: ignore[no-redef,attr-defined]
from twisted.python.win32 import formatError as strerror
-else:
- from errno import EPERM
- from errno import EINVAL
- from errno import EWOULDBLOCK
- from errno import EINPROGRESS
- from errno import EALREADY
- from errno import EISCONN
- from errno import ENOBUFS
- from errno import EMFILE
- from errno import ENFILE
- from errno import ENOMEM
- from errno import EAGAIN
- from errno import ECONNABORTED
-
- from os import strerror
from errno import errorcode
@@ -795,9 +798,9 @@ class Server(_TLSServerMixin, Connection):
_base = Connection
- _addressType: Union[
- type[address.IPv4Address], type[address.IPv6Address]
- ] = address.IPv4Address
+ _addressType: (
+ type[address.IPv4Address] | type[address.IPv6Address]
+ ) = address.IPv4Address
def __init__(
self,
@@ -960,7 +963,7 @@ class _IFileDescriptorReservation(Interface):
"""
-class _HasClose(typing_extensions.Protocol):
+class _HasClose(TypingProtocol):
def close(self) -> object:
...
@@ -980,7 +983,7 @@ class _FileDescriptorReservation:
_log: ClassVar[Logger] = Logger()
_fileFactory: Callable[[], _HasClose]
- _fileDescriptor: Optional[_HasClose] = attr.ib(init=False, default=None)
+ _fileDescriptor: _HasClose | None = attr.ib(init=False, default=None)
def available(self):
"""
@@ -1132,7 +1135,7 @@ class _BuffersLogs:
_namespace: str
_observer: ILogObserver
- _logs: List[LogEvent] = attr.ib(default=attr.Factory(list))
+ _logs: list[LogEvent] = attr.ib(default=attr.Factory(list))
def __enter__(self):
"""
@@ -1277,7 +1280,7 @@ class Port(base.BasePort, _SocketCloser):
# Actual port number being listened on, only set to a non-None
# value when we are actually listening.
- _realPortNumber: Optional[int] = None
+ _realPortNumber: int | None = None
# An externally initialized socket that we will use, rather than creating
# our own.
@@ -1523,7 +1526,7 @@ class Connector(base.BaseConnector):
self,
host: str,
port: int | str,
- factory: ClientFactory,
+ factory: ClientFactory[P],
timeout: float,
bindAddress: str | tuple[str, int] | None,
reactor: Any = None,
diff --git a/contrib/python/Twisted/py3/twisted/internet/testing.py b/contrib/python/Twisted/py3/twisted/internet/testing.py
index a7e1de67286..0b82638da6d 100644
--- a/contrib/python/Twisted/py3/twisted/internet/testing.py
+++ b/contrib/python/Twisted/py3/twisted/internet/testing.py
@@ -7,22 +7,12 @@ Assorted functionality which is commonly useful when writing unit tests.
"""
from __future__ import annotations
-import typing
+from collections.abc import Coroutine, Generator, Iterator, Sequence
from dataclasses import dataclass
from io import BytesIO
from socket import AF_INET, AF_INET6
from time import time
-from typing import (
- Any,
- Callable,
- Coroutine,
- Generator,
- Iterator,
- Sequence,
- TypeVar,
- Union,
- overload,
-)
+from typing import Any, Callable, Protocol, TypeVar, overload
from zope.interface import implementedBy, implementer
from zope.interface.verify import verifyClass
@@ -42,6 +32,7 @@ from twisted.internet.interfaces import (
IHostResolution,
IListeningPort,
IProtocol,
+ IProtocolFactory,
IPushProducer,
IReactorCore,
IReactorFDSet,
@@ -53,6 +44,7 @@ from twisted.internet.interfaces import (
IResolutionReceiver,
ITransport,
)
+from twisted.internet.protocol import ClientFactory
from twisted.internet.task import Clock
from twisted.logger import ILogObserver, LogEvent, LogPublisher
from twisted.protocols import basic
@@ -79,7 +71,7 @@ __all__ = [
_P = ParamSpec("_P")
-class _ProtocolConnectionMadeHaver(typing.Protocol):
+class _ProtocolConnectionMadeHaver(Protocol):
"""
Explicit stipulation of the implicit requirement of L{AccumulatingProtocol}'s factory.
"""
@@ -539,6 +531,8 @@ class MemoryReactor:
"""
nameResolver: IHostnameResolver
+ tcpServers: list[tuple[int, IProtocolFactory, int, str]]
+ tcpClients: list[tuple[str, int, ClientFactory, int, str | None]]
def __init__(self):
"""
@@ -547,9 +541,9 @@ class MemoryReactor:
self.hasInstalled = False
self.running = False
- self.hasRun = True
- self.hasStopped = True
- self.hasCrashed = True
+ self.hasRun = False
+ self.hasStopped = False
+ self.hasCrashed = False
self.whenRunningHooks = []
self.triggers = {}
@@ -665,8 +659,20 @@ class MemoryReactor:
"""
Fake L{IReactorCore.callWhenRunning}.
Keeps a list of invocations to make in C{self.whenRunningHooks}.
+
+ If the reactor has not started, the callable will be scheduled
+ to run when it does start. Otherwise, the callable will be invoked
+ immediately.
"""
- self.whenRunningHooks.append((callable, args, kw))
+ # Normally, we would only key off of `self.running`, but the `MemoryReactor` is
+ # a bit unique in the fact that it stops itself immediately after calling
+ # `MemoryReactor.run()`. We're going to consider it good enough if the reactor
+ # has ever been started (`self.hasRun`).
+ if self.running or self.hasRun:
+ callable(*args, **kw)
+ return None
+ else:
+ self.whenRunningHooks.append((callable, args, kw))
def adoptStreamPort(self, fileno, addressFamily, factory):
"""
@@ -1079,11 +1085,11 @@ _T = TypeVar("_T")
def _benchmarkWithReactor(
test_target: Callable[
[],
- Union[
- Coroutine[Deferred[Any], Any, _T],
- Generator[Deferred[Any], Any, _T],
- Deferred[_T],
- ],
+ (
+ Coroutine[Deferred[Any], Any, _T]
+ | Generator[Deferred[Any], Any, _T]
+ | Deferred[_T]
+ ),
],
) -> Callable[[Any], None]: # pragma: no cover
"""
@@ -1135,8 +1141,9 @@ def _runReactor(callback: Callable[[], Deferred[_T]]) -> None: # pragma: no cov
deferred = callback()
deferred.addErrback(errors.append)
- deferred.addBoth(lambda _: reactor.callLater(0, _stopReactor, reactor)) # type: ignore[attr-defined]
- reactor.run(installSignalHandlers=False) # type: ignore[attr-defined]
+ deferred.addBoth(lambda _: reactor.callLater(0, _stopReactor, reactor))
+ # installSignalHandlers is technically not in IReactorCore
+ reactor.run(installSignalHandlers=False) # type:ignore[call-arg]
if errors: # pragma: no cover
# Make sure the test fails in a visible way:
diff --git a/contrib/python/Twisted/py3/twisted/internet/threads.py b/contrib/python/Twisted/py3/twisted/internet/threads.py
index e9a49cbea81..296b066f4cc 100644
--- a/contrib/python/Twisted/py3/twisted/internet/threads.py
+++ b/contrib/python/Twisted/py3/twisted/internet/threads.py
@@ -10,12 +10,12 @@ For basic support see reactor threading API docs.
from __future__ import annotations
import queue as Queue
-from typing import Callable, TypeVar
+from typing import Callable, TypeVar, cast
from typing_extensions import ParamSpec
from twisted.internet import defer
-from twisted.internet.interfaces import IReactorFromThreads
+from twisted.internet.interfaces import IReactorFromThreads, IReactorThreads
from twisted.python import failure
from twisted.python.threadpool import ThreadPool
@@ -65,7 +65,11 @@ def deferToThreadPool(
return d
-def deferToThread(f, *args, **kwargs):
+def deferToThread(
+ f: Callable[_P, _R],
+ *args: _P.args,
+ **kwargs: _P.kwargs,
+) -> defer.Deferred[_R]:
"""
Run a function in a thread and return the result as a Deferred.
@@ -79,7 +83,9 @@ def deferToThread(f, *args, **kwargs):
"""
from twisted.internet import reactor
- return deferToThreadPool(reactor, reactor.getThreadPool(), f, *args, **kwargs)
+ reactor_ = cast(IReactorThreads, reactor)
+
+ return deferToThreadPool(reactor_, reactor_.getThreadPool(), f, *args, **kwargs)
def _runMultiple(tupleList):
@@ -101,7 +107,12 @@ def callMultipleInThread(tupleList):
reactor.callInThread(_runMultiple, tupleList)
-def blockingCallFromThread(reactor, f, *a, **kw):
+def blockingCallFromThread(
+ reactor: IReactorFromThreads,
+ f: Callable[_P, _R] | Callable[_P, defer.Deferred[_R]],
+ *a: _P.args,
+ **kw: _P.kwargs,
+) -> _R:
"""
Run a function in the reactor from a thread, and wait for the result
synchronously. If the function returns a L{Deferred}, wait for its
@@ -123,7 +134,7 @@ def blockingCallFromThread(reactor, f, *a, **kw):
C{blockingCallFromThread} will raise that failure's exception (see
L{Failure.raiseException}).
"""
- queue = Queue.Queue()
+ queue: Queue.Queue[_R] = Queue.Queue()
def _callFromThread():
result = defer.maybeDeferred(f, *a, **kw)
diff --git a/contrib/python/Twisted/py3/twisted/internet/udp.py b/contrib/python/Twisted/py3/twisted/internet/udp.py
index 6ef67f323ce..bb96f278711 100644
--- a/contrib/python/Twisted/py3/twisted/internet/udp.py
+++ b/contrib/python/Twisted/py3/twisted/internet/udp.py
@@ -19,7 +19,6 @@ from __future__ import annotations
# System Imports
import socket
import warnings
-from typing import Optional
from zope.interface import implementer
@@ -87,7 +86,7 @@ class Port(base.BasePort):
socketType: socket.SocketKind = socket.SOCK_DGRAM
maxThroughput = 256 * 1024
- _realPortNumber: Optional[int] = None
+ _realPortNumber: int | None = None
_preexistingSocket = None
def __init__(self, port, proto, interface="", maxPacketSize=8192, reactor=None):
@@ -466,7 +465,7 @@ class MulticastPort(MulticastMixin, Port):
def createInternetSocket(self) -> socket.socket:
"""
Override L{Port.createInternetSocket} to configure the socket to honor
- the C{listenMultiple} argument to L{IReactorMulticast.listenMultiple}.
+ the C{listenMultiple} argument to L{IReactorMulticast.listenMulticast}.
"""
skt = Port.createInternetSocket(self)
if self.listenMultiple:
diff --git a/contrib/python/Twisted/py3/twisted/internet/unix.py b/contrib/python/Twisted/py3/twisted/internet/unix.py
index b33ef299747..e89b4862867 100644
--- a/contrib/python/Twisted/py3/twisted/internet/unix.py
+++ b/contrib/python/Twisted/py3/twisted/internet/unix.py
@@ -10,13 +10,13 @@ End users shouldn't use this module directly - use the reactor APIs instead.
Maintainer: Itamar Shtull-Trauring
"""
+from __future__ import annotations
import os
import socket
import stat
import struct
from errno import EAGAIN, ECONNREFUSED, EINTR, EMSGSIZE, ENOBUFS, EWOULDBLOCK
-from typing import Optional, Type
from zope.interface import implementedBy, implementer, implementer_only
@@ -66,7 +66,7 @@ class _SendmsgMixin:
registered producer, if there is one.
"""
- _writeSomeDataBase: Optional[Type[FileDescriptor]] = None
+ _writeSomeDataBase: type[FileDescriptor] | None = None
_fileDescriptorBufferSize = 64
def __init__(self):
diff --git a/contrib/python/Twisted/py3/twisted/logger/_buffer.py b/contrib/python/Twisted/py3/twisted/logger/_buffer.py
index d5e514f18be..5dc4c3970f6 100644
--- a/contrib/python/Twisted/py3/twisted/logger/_buffer.py
+++ b/contrib/python/Twisted/py3/twisted/logger/_buffer.py
@@ -6,8 +6,10 @@
Log observer that maintains a buffer.
"""
+from __future__ import annotations
+
from collections import deque
-from typing import Deque, Optional
+from typing import Deque
from zope.interface import implementer
@@ -34,7 +36,7 @@ class LimitedHistoryLogObserver:
>>>
"""
- def __init__(self, size: Optional[int] = _DEFAULT_BUFFER_MAXIMUM) -> None:
+ def __init__(self, size: int | None = _DEFAULT_BUFFER_MAXIMUM) -> None:
"""
@param size: The maximum number of events to buffer. If L{None}, the
buffer is unbounded.
diff --git a/contrib/python/Twisted/py3/twisted/logger/_capture.py b/contrib/python/Twisted/py3/twisted/logger/_capture.py
index 9d3ce0e3abf..a40e6799aef 100644
--- a/contrib/python/Twisted/py3/twisted/logger/_capture.py
+++ b/contrib/python/Twisted/py3/twisted/logger/_capture.py
@@ -6,8 +6,9 @@
Context manager for capturing logs.
"""
+from collections.abc import Iterator, Sequence
from contextlib import contextmanager
-from typing import Iterator, List, Sequence, cast
+from typing import cast
from twisted.logger import globalLogPublisher
from ._interfaces import ILogObserver, LogEvent
@@ -15,7 +16,7 @@ from ._interfaces import ILogObserver, LogEvent
@contextmanager
def capturedLogs() -> Iterator[Sequence[LogEvent]]:
- events: List[LogEvent] = []
+ events: list[LogEvent] = []
observer = cast(ILogObserver, events.append)
globalLogPublisher.addObserver(observer)
diff --git a/contrib/python/Twisted/py3/twisted/logger/_file.py b/contrib/python/Twisted/py3/twisted/logger/_file.py
index 43ae32cd29a..e44c6332330 100644
--- a/contrib/python/Twisted/py3/twisted/logger/_file.py
+++ b/contrib/python/Twisted/py3/twisted/logger/_file.py
@@ -6,7 +6,9 @@
File log observer.
"""
-from typing import IO, Any, Callable, Optional
+from __future__ import annotations
+
+from typing import IO, Any, Callable
from zope.interface import implementer
@@ -22,7 +24,7 @@ class FileLogObserver:
"""
def __init__(
- self, outFile: IO[Any], formatEvent: Callable[[LogEvent], Optional[str]]
+ self, outFile: IO[Any], formatEvent: Callable[[LogEvent], str | None]
) -> None:
"""
@param outFile: A file-like object. Ideally one should be passed which
@@ -30,7 +32,7 @@ class FileLogObserver:
@param formatEvent: A callable that formats an event.
"""
if ioType(outFile) is not str:
- self._encoding: Optional[str] = "utf-8"
+ self._encoding: str | None = "utf-8"
else:
self._encoding = None
@@ -54,7 +56,7 @@ class FileLogObserver:
def textFileLogObserver(
- outFile: IO[Any], timeFormat: Optional[str] = timeFormatRFC3339
+ outFile: IO[Any], timeFormat: str | None = timeFormatRFC3339
) -> FileLogObserver:
"""
Create a L{FileLogObserver} that emits text to a specified (writable)
@@ -69,7 +71,7 @@ def textFileLogObserver(
@return: A file log observer.
"""
- def formatEvent(event: LogEvent) -> Optional[str]:
+ def formatEvent(event: LogEvent) -> str | None:
return formatEventAsClassicLogText(
event, formatTime=lambda e: formatTime(e, timeFormat)
)
diff --git a/contrib/python/Twisted/py3/twisted/logger/_filter.py b/contrib/python/Twisted/py3/twisted/logger/_filter.py
index 07e443849a1..1b32e87f70d 100644
--- a/contrib/python/Twisted/py3/twisted/logger/_filter.py
+++ b/contrib/python/Twisted/py3/twisted/logger/_filter.py
@@ -6,8 +6,8 @@
Filtering log observer.
"""
+from collections.abc import Iterable
from functools import partial
-from typing import Dict, Iterable
from zope.interface import Interface, implementer
@@ -138,7 +138,7 @@ class LogLevelFilterPredicate:
"""
@param defaultLogLevel: The default minimum log level.
"""
- self._logLevelsByNamespace: Dict[str, NamedConstant] = {}
+ self._logLevelsByNamespace: dict[str, NamedConstant] = {}
self.defaultLogLevel = defaultLogLevel
self.clearLogLevels()
diff --git a/contrib/python/Twisted/py3/twisted/logger/_flatten.py b/contrib/python/Twisted/py3/twisted/logger/_flatten.py
index b79476aa248..68a6f5fa7c0 100644
--- a/contrib/python/Twisted/py3/twisted/logger/_flatten.py
+++ b/contrib/python/Twisted/py3/twisted/logger/_flatten.py
@@ -8,9 +8,11 @@ relevant fields from the format string and persisting them for later
examination.
"""
+from __future__ import annotations
+
from collections import defaultdict
from string import Formatter
-from typing import Any, Dict, Optional
+from typing import Any
from ._interfaces import LogEvent
@@ -27,10 +29,10 @@ class KeyFlattener:
"""
Initialize a L{KeyFlattener}.
"""
- self.keys: Dict[str, int] = defaultdict(lambda: 0)
+ self.keys: dict[str, int] = defaultdict(int)
def flatKey(
- self, fieldName: str, formatSpec: Optional[str], conversion: Optional[str]
+ self, fieldName: str, formatSpec: str | None, conversion: str | None
) -> str:
"""
Compute a string key for a given field/format/conversion.
diff --git a/contrib/python/Twisted/py3/twisted/logger/_format.py b/contrib/python/Twisted/py3/twisted/logger/_format.py
index 57d8c3b1e60..a9ef82f2606 100644
--- a/contrib/python/Twisted/py3/twisted/logger/_format.py
+++ b/contrib/python/Twisted/py3/twisted/logger/_format.py
@@ -8,10 +8,9 @@ Tools for formatting logging events.
from __future__ import annotations
+from collections.abc import Iterator, Mapping
from datetime import datetime as DateTime
-from typing import Any, Callable, Iterator, Mapping, Optional, Union, cast
-
-from constantly import NamedConstant
+from typing import Any, Callable, Optional, Union, cast
from twisted.python._tzhelper import FixedOffsetTimeZone
from twisted.python.failure import Failure
@@ -79,8 +78,8 @@ def formatUnformattableEvent(event: LogEvent, error: BaseException) -> str:
def formatTime(
- when: Optional[float],
- timeFormat: Optional[str] = timeFormatRFC3339,
+ when: float | None,
+ timeFormat: str | None = timeFormatRFC3339,
default: str = "-",
) -> str:
"""
@@ -113,8 +112,8 @@ def formatTime(
def formatEventAsClassicLogText(
- event: LogEvent, formatTime: Callable[[Optional[float]], str] = formatTime
-) -> Optional[str]:
+ event: LogEvent, formatTime: Callable[[float | None], str] = formatTime
+) -> str | None:
"""
Format an event as a line of human-readable text for, e.g. traditional log
file output.
@@ -173,7 +172,7 @@ def keycall(key: str, getter: Callable[[str], Any]) -> PotentialCallWrapper:
of C{get} first, before wrapping it up.
@param key: The last dotted segment of a formatting key, as parsed by
- L{Formatter.vformat}, which may end in C{()}.
+ L{string.Formatter.vformat}, which may end in C{()}.
@param getter: A function which takes a string and returns some other
object, to be formatted and stringified for a log.
@@ -189,7 +188,7 @@ def keycall(key: str, getter: Callable[[str], Any]) -> PotentialCallWrapper:
return PotentialCallWrapper(value)
-class PotentialCallWrapper(object):
+class PotentialCallWrapper:
"""
Object wrapper that wraps C{getattr()} so as to process call-parentheses
C{"()"} after a dotted attribute access.
@@ -337,7 +336,7 @@ def _formatSystem(event: LogEvent) -> str:
"""
system = cast(Optional[str], event.get("log_system", None))
if system is None:
- level = cast(Optional[NamedConstant], event.get("log_level", None))
+ level = event.get("log_level", None)
if level is None:
levelName = "-"
else:
diff --git a/contrib/python/Twisted/py3/twisted/logger/_global.py b/contrib/python/Twisted/py3/twisted/logger/_global.py
index 8ae89baf728..6cbcdbe42b2 100644
--- a/contrib/python/Twisted/py3/twisted/logger/_global.py
+++ b/contrib/python/Twisted/py3/twisted/logger/_global.py
@@ -7,9 +7,12 @@ This module includes process-global state associated with the logging system,
and implementation of logic for managing that global state.
"""
+from __future__ import annotations
+
import sys
import warnings
-from typing import IO, Any, Iterable, Optional, Type
+from collections.abc import Iterable
+from typing import IO, Any
from twisted.python.compat import currentframe
from twisted.python.reflect import qual
@@ -73,7 +76,7 @@ class LogBeginner:
errorStream: IO[Any],
stdio: object,
warningsModule: Any,
- initialBufferSize: Optional[int] = None,
+ initialBufferSize: int | None = None,
) -> None:
"""
Initialize this L{LogBeginner}.
@@ -89,7 +92,7 @@ class LogBeginner:
self._log = Logger(observer=publisher)
self._stdio = stdio
self._warningsModule = warningsModule
- self._temporaryObserver: Optional[ILogObserver] = LogPublisher(
+ self._temporaryObserver: ILogObserver | None = LogPublisher(
self._initialBuffer,
FilteringLogObserver(
FileLogObserver(
@@ -185,11 +188,11 @@ class LogBeginner:
def showwarning(
self,
message: str,
- category: Type[Warning],
+ category: type[Warning],
filename: str,
lineno: int,
- file: Optional[IO[Any]] = None,
- line: Optional[str] = None,
+ file: IO[Any] | None = None,
+ line: str | None = None,
) -> None:
"""
Twisted-enabled wrapper around L{warnings.showwarning}.
diff --git a/contrib/python/Twisted/py3/twisted/logger/_interfaces.py b/contrib/python/Twisted/py3/twisted/logger/_interfaces.py
index 496de1de540..c1182448704 100644
--- a/contrib/python/Twisted/py3/twisted/logger/_interfaces.py
+++ b/contrib/python/Twisted/py3/twisted/logger/_interfaces.py
@@ -5,16 +5,18 @@
Logger interfaces.
"""
-from typing import TYPE_CHECKING, Any, Dict, List, Tuple
+from typing import TYPE_CHECKING, Any
from zope.interface import Interface
+from typing_extensions import TypeAlias
+
if TYPE_CHECKING:
from ._logger import Logger
-LogEvent = Dict[str, Any]
-LogTrace = List[Tuple["Logger", "ILogObserver"]]
+LogEvent: TypeAlias = dict[str, Any]
+LogTrace: TypeAlias = list[tuple["Logger", "ILogObserver"]]
class ILogObserver(Interface):
diff --git a/contrib/python/Twisted/py3/twisted/logger/_io.py b/contrib/python/Twisted/py3/twisted/logger/_io.py
index 96634212201..bf98ee562df 100644
--- a/contrib/python/Twisted/py3/twisted/logger/_io.py
+++ b/contrib/python/Twisted/py3/twisted/logger/_io.py
@@ -6,8 +6,11 @@
File-like object that logs.
"""
+from __future__ import annotations
+
import sys
-from typing import AnyStr, Iterable, Optional
+from collections.abc import Iterable
+from typing import AnyStr
from constantly import NamedConstant
from incremental import Version
@@ -29,6 +32,7 @@ class LoggingFile:
"""
_softspace = 0
+ _encoding: str
@deprecatedProperty(Version("Twisted", 21, 2, 0))
def softspace(self):
@@ -42,7 +46,7 @@ class LoggingFile:
self,
logger: Logger,
level: NamedConstant = LogLevel.info,
- encoding: Optional[str] = None,
+ encoding: str | None = None,
) -> None:
"""
@param logger: the logger to log through.
diff --git a/contrib/python/Twisted/py3/twisted/logger/_json.py b/contrib/python/Twisted/py3/twisted/logger/_json.py
index aa837ded75a..0d353d840d1 100644
--- a/contrib/python/Twisted/py3/twisted/logger/_json.py
+++ b/contrib/python/Twisted/py3/twisted/logger/_json.py
@@ -6,8 +6,11 @@
Tools for saving and loading log events in a structured format.
"""
+from __future__ import annotations
+
+from collections.abc import Iterable
from json import dumps, loads
-from typing import IO, Any, AnyStr, Dict, Iterable, Optional, Union, cast
+from typing import IO, Any, AnyStr, cast
from uuid import UUID
from constantly import NamedConstant
@@ -22,7 +25,7 @@ from ._logger import Logger
log = Logger()
-JSONDict = Dict[str, Any]
+JSONDict = dict[str, Any]
def failureAsJSON(failure: Failure) -> JSONDict:
@@ -133,7 +136,7 @@ def eventAsJSON(event: LogEvent) -> str:
file.
"""
- def default(unencodable: object) -> Union[JSONDict, str]:
+ def default(unencodable: object) -> JSONDict | str:
"""
Serialize an object not otherwise serializable by L{dumps}.
@@ -189,7 +192,7 @@ def jsonFileLogObserver(
def eventsFromJSONLogFile(
inFile: IO[Any],
- recordSeparator: Optional[str] = None,
+ recordSeparator: str | None = None,
bufferSize: int = 4096,
) -> Iterable[LogEvent]:
"""
@@ -213,7 +216,7 @@ def eventsFromJSONLogFile(
else:
return s.encode("utf-8")
- def eventFromBytearray(record: bytearray) -> Optional[LogEvent]:
+ def eventFromBytearray(record: bytearray) -> LogEvent | None:
try:
text = bytes(record).decode("utf-8")
except UnicodeDecodeError:
@@ -251,7 +254,7 @@ def eventsFromJSONLogFile(
else:
- def eventFromRecord(record: bytearray) -> Optional[LogEvent]:
+ def eventFromRecord(record: bytearray) -> LogEvent | None:
if record[-1] == ord("\n"):
return eventFromBytearray(record)
else:
diff --git a/contrib/python/Twisted/py3/twisted/logger/_legacy.py b/contrib/python/Twisted/py3/twisted/logger/_legacy.py
index 2847bc7a406..c8ec2e40137 100644
--- a/contrib/python/Twisted/py3/twisted/logger/_legacy.py
+++ b/contrib/python/Twisted/py3/twisted/logger/_legacy.py
@@ -6,7 +6,9 @@
Integration with L{twisted.python.log}.
"""
-from typing import TYPE_CHECKING, Any, Callable, Dict, Optional
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Any, Callable
from zope.interface import implementer
@@ -29,7 +31,7 @@ class LegacyLogObserverWrapper:
expect legacy events.
"""
- def __init__(self, legacyObserver: "ILegacyLogObserver") -> None:
+ def __init__(self, legacyObserver: ILegacyLogObserver) -> None:
"""
@param legacyObserver: a legacy observer to which this observer will
forward events.
@@ -92,8 +94,8 @@ class LegacyLogObserverWrapper:
def publishToNewObserver(
observer: ILogObserver,
- eventDict: Dict[str, Any],
- textFromEventDict: Callable[[Dict[str, Any]], Optional[str]],
+ eventDict: dict[str, Any],
+ textFromEventDict: Callable[[dict[str, Any]], str | None],
) -> None:
"""
Publish an old-style (L{twisted.python.log}) event to a new-style
diff --git a/contrib/python/Twisted/py3/twisted/logger/_logger.py b/contrib/python/Twisted/py3/twisted/logger/_logger.py
index b7f3e2e1213..a84ad79eb6c 100644
--- a/contrib/python/Twisted/py3/twisted/logger/_logger.py
+++ b/contrib/python/Twisted/py3/twisted/logger/_logger.py
@@ -10,7 +10,7 @@ from __future__ import annotations
from time import time
from types import TracebackType
-from typing import Any, Callable, ContextManager, Optional, Protocol, cast
+from typing import Any, Callable, ContextManager, Protocol, cast
from twisted.python.compat import currentframe
from twisted.python.failure import Failure
@@ -124,9 +124,9 @@ class Logger:
def __init__(
self,
- namespace: Optional[str] = None,
- source: Optional[object] = None,
- observer: Optional["ILogObserver"] = None,
+ namespace: str | None = None,
+ source: object | None = None,
+ observer: ILogObserver | None = None,
) -> None:
"""
@param namespace: The namespace for this logger. Uses a dotted
@@ -151,7 +151,7 @@ class Logger:
else:
self.observer = observer
- def __get__(self, instance: object, owner: Optional[type] = None) -> "Logger":
+ def __get__(self, instance: object, owner: type | None = None) -> Logger:
"""
When used as a descriptor, i.e.::
@@ -187,7 +187,7 @@ class Logger:
return f"<{self.__class__.__name__} {self.namespace!r}>"
def emit(
- self, level: LogLevel, format: Optional[str] = None, **kwargs: object
+ self, level: LogLevel, format: str | None = None, **kwargs: object
) -> None:
"""
Emit a log event to all log observers at the given level.
@@ -228,7 +228,7 @@ class Logger:
def failure(
self,
format: str,
- failure: Optional[Failure] = None,
+ failure: Failure | None = None,
level: LogLevel = LogLevel.critical,
**kwargs: object,
) -> None:
@@ -280,7 +280,7 @@ class Logger:
self.emit(level, format, log_failure=failure, **kwargs)
- def debug(self, format: Optional[str] = None, **kwargs: object) -> None:
+ def debug(self, format: str | None = None, **kwargs: object) -> None:
"""
Emit a log event at log level L{LogLevel.debug}.
@@ -295,7 +295,7 @@ class Logger:
"""
self.emit(LogLevel.debug, format, **kwargs)
- def info(self, format: Optional[str] = None, **kwargs: object) -> None:
+ def info(self, format: str | None = None, **kwargs: object) -> None:
"""
Emit a log event at log level L{LogLevel.info}.
@@ -310,7 +310,7 @@ class Logger:
"""
self.emit(LogLevel.info, format, **kwargs)
- def warn(self, format: Optional[str] = None, **kwargs: object) -> None:
+ def warn(self, format: str | None = None, **kwargs: object) -> None:
"""
Emit a log event at log level L{LogLevel.warn}.
@@ -325,7 +325,7 @@ class Logger:
"""
self.emit(LogLevel.warn, format, **kwargs)
- def error(self, format: Optional[str] = None, **kwargs: object) -> None:
+ def error(self, format: str | None = None, **kwargs: object) -> None:
"""
Emit a log event at log level L{LogLevel.error}.
@@ -340,7 +340,7 @@ class Logger:
"""
self.emit(LogLevel.error, format, **kwargs)
- def critical(self, format: Optional[str] = None, **kwargs: object) -> None:
+ def critical(self, format: str | None = None, **kwargs: object) -> None:
"""
Emit a log event at log level L{LogLevel.critical}.
@@ -431,9 +431,9 @@ class Logger:
corrective guidance can be offered to an user/administrator, and the
impact of the condition is unknown.
- @param format: a message format using new-style (PEP 3101) formatting.
- The logging event (which is a L{dict}) is used to render this
- format string.
+ @param staticMessage: a message format using new-style (PEP 3101)
+ formatting. The logging event (which is a L{dict}) is used to
+ render this format string.
@param level: a L{LogLevel} to use.
diff --git a/contrib/python/Twisted/py3/twisted/logger/_observer.py b/contrib/python/Twisted/py3/twisted/logger/_observer.py
index 86f89c37b45..36bfa278e63 100644
--- a/contrib/python/Twisted/py3/twisted/logger/_observer.py
+++ b/contrib/python/Twisted/py3/twisted/logger/_observer.py
@@ -6,7 +6,9 @@
Basic log observers.
"""
-from typing import Callable, Optional
+from __future__ import annotations
+
+from typing import Callable
from zope.interface import implementer
@@ -59,7 +61,7 @@ class LogPublisher:
Forward events to contained observers.
"""
if "log_trace" not in event:
- trace: Optional[Callable[[ILogObserver], None]] = None
+ trace: Callable[[ILogObserver], None] | None = None
else:
diff --git a/contrib/python/Twisted/py3/twisted/logger/_stdlib.py b/contrib/python/Twisted/py3/twisted/logger/_stdlib.py
index abc707e4a82..5216e6739d3 100644
--- a/contrib/python/Twisted/py3/twisted/logger/_stdlib.py
+++ b/contrib/python/Twisted/py3/twisted/logger/_stdlib.py
@@ -6,8 +6,10 @@
Integration with Python standard library logging.
"""
+from __future__ import annotations
+
import logging as stdlibLogging
-from typing import Mapping, Tuple
+from collections.abc import Mapping
from zope.interface import implementer
@@ -28,13 +30,14 @@ toStdlibLogLevelMapping: Mapping[NamedConstant, int] = {
}
-def _reverseLogLevelMapping() -> Mapping[int, NamedConstant]:
+def _reverseLogLevelMapping() -> Mapping[str | int, NamedConstant]:
"""
Reverse the above mapping, adding both the numerical keys used above and
the corresponding string keys also used by python logging.
+
@return: the reversed mapping
"""
- mapping = {}
+ mapping: dict[str | int, NamedConstant] = {}
for logLevel, pyLogLevel in toStdlibLogLevelMapping.items():
mapping[pyLogLevel] = logLevel
mapping[stdlibLogging.getLevelName(pyLogLevel)] = logLevel
@@ -80,7 +83,7 @@ class STDLibLogObserver:
def _findCaller(
self, stackInfo: bool = False, stackLevel: int = 1
- ) -> Tuple[str, int, str, None]:
+ ) -> tuple[str, int, str, None]:
"""
Based on the stack depth passed to this L{STDLibLogObserver}, identify
the calling function.
diff --git a/contrib/python/Twisted/py3/twisted/logger/_util.py b/contrib/python/Twisted/py3/twisted/logger/_util.py
index e8f02ddd225..f8c626e7136 100644
--- a/contrib/python/Twisted/py3/twisted/logger/_util.py
+++ b/contrib/python/Twisted/py3/twisted/logger/_util.py
@@ -6,8 +6,6 @@
Logging utilities.
"""
-from typing import List
-
from ._interfaces import LogTrace
from ._logger import Logger
@@ -31,7 +29,7 @@ def formatTrace(trace: LogTrace) -> str:
return f"{obj}"
result = []
- lineage: List[Logger] = []
+ lineage: list[Logger] = []
for parent, child in trace:
if not lineage or lineage[-1] is not parent:
diff --git a/contrib/python/Twisted/py3/twisted/mail/_pop3client.py b/contrib/python/Twisted/py3/twisted/mail/_pop3client.py
index 08efe1ec545..14bd07836b9 100644
--- a/contrib/python/Twisted/py3/twisted/mail/_pop3client.py
+++ b/contrib/python/Twisted/py3/twisted/mail/_pop3client.py
@@ -13,7 +13,6 @@ Don't use this module directly. Use twisted.mail.pop3 instead.
import re
from hashlib import md5
-from typing import List
from twisted.internet import defer, error, interfaces
from twisted.mail._except import (
@@ -1232,4 +1231,4 @@ class POP3Client(basic.LineOnlyReceiver, policies.TimeoutMixin):
return self.sendShort(b"QUIT", None)
-__all__: List[str] = []
+__all__: list[str] = []
diff --git a/contrib/python/Twisted/py3/twisted/mail/imap4.py b/contrib/python/Twisted/py3/twisted/mail/imap4.py
index f38d094f896..bf8e3afeacd 100644
--- a/contrib/python/Twisted/py3/twisted/mail/imap4.py
+++ b/contrib/python/Twisted/py3/twisted/mail/imap4.py
@@ -14,6 +14,7 @@ To do::
Clarify some API docs (Query, etc)
Make APPEND recognize (again) non-existent mailboxes before accepting the literal
"""
+from __future__ import annotations
import binascii
import codecs
@@ -28,7 +29,7 @@ import uuid
from base64 import decodebytes, encodebytes
from io import BytesIO
from itertools import chain
-from typing import Any, List, Optional, cast
+from typing import Any, cast
from zope.interface import implementer
@@ -184,7 +185,7 @@ class MessageSet:
that it will not be called out-of-order).
"""
- _empty: List[Any] = []
+ _empty: list[Any] = []
_infinity = float("inf")
def __init__(self, start=_empty, end=_empty):
@@ -5691,7 +5692,7 @@ class _FetchParser:
def __str__(self) -> str:
return self.__bytes__().decode("ascii")
- def getBytes(self, length: Optional[int] = None) -> bytes:
+ def getBytes(self, length: int | None = None) -> bytes:
"""
Prepare the initial command response for a Fetch BODY request.
Interpret the Fetch request from the client and return the
diff --git a/contrib/python/Twisted/py3/twisted/mail/interfaces.py b/contrib/python/Twisted/py3/twisted/mail/interfaces.py
index 0bb78e1bfa1..027bc6ffbc0 100644
--- a/contrib/python/Twisted/py3/twisted/mail/interfaces.py
+++ b/contrib/python/Twisted/py3/twisted/mail/interfaces.py
@@ -155,8 +155,7 @@ class IMailboxPOP3(Interface):
Retrieve the size of a message, or, if none is specified, the size of
each message in the mailbox.
- @type index: L{int} or L{None}
- @param index: The 0-based index of the message.
+ @param i: The 0-based index of the message.
@rtype: L{int}, sequence of L{int}, or L{Deferred <defer.Deferred>}
@return: The number of octets in the specified message, or, if an
diff --git a/contrib/python/Twisted/py3/twisted/mail/pop3.py b/contrib/python/Twisted/py3/twisted/mail/pop3.py
index 8b4405cb3b7..a564124e3d4 100644
--- a/contrib/python/Twisted/py3/twisted/mail/pop3.py
+++ b/contrib/python/Twisted/py3/twisted/mail/pop3.py
@@ -16,7 +16,7 @@ import base64
import binascii
import warnings
from hashlib import md5
-from typing import IO, Optional, overload
+from typing import IO, overload
from zope.interface import implementer
@@ -432,7 +432,7 @@ class POP3(basic.LineOnlyReceiver, policies.TimeoutMixin):
@ivar _auth: Authorization credentials.
"""
- magic: Optional[bytes] = None
+ magic: bytes | None = None
_userIs = None
_onLogout = None
diff --git a/contrib/python/Twisted/py3/twisted/mail/pop3client.py b/contrib/python/Twisted/py3/twisted/mail/pop3client.py
index 72676a69a81..1ab47366fa8 100644
--- a/contrib/python/Twisted/py3/twisted/mail/pop3client.py
+++ b/contrib/python/Twisted/py3/twisted/mail/pop3client.py
@@ -4,7 +4,6 @@ Deprecated POP3 client protocol implementation.
Don't use this module directly. Use twisted.mail.pop3 instead.
"""
import warnings
-from typing import List
from twisted.mail._pop3client import ERR, OK, POP3Client
@@ -19,4 +18,4 @@ OK
ERR
POP3Client
-__all__: List[str] = []
+__all__: list[str] = []
diff --git a/contrib/python/Twisted/py3/twisted/mail/protocols.py b/contrib/python/Twisted/py3/twisted/mail/protocols.py
index 035a1b0c12b..b78e380da04 100644
--- a/contrib/python/Twisted/py3/twisted/mail/protocols.py
+++ b/contrib/python/Twisted/py3/twisted/mail/protocols.py
@@ -365,7 +365,7 @@ class POP3Factory(protocol.ServerFactory):
L{VirtualPOP3}.
"""
- protocol = VirtualPOP3
+ protocol = VirtualPOP3 # type:ignore[assignment]
service = None
def __init__(self, service):
diff --git a/contrib/python/Twisted/py3/twisted/mail/relaymanager.py b/contrib/python/Twisted/py3/twisted/mail/relaymanager.py
index 18cc2878331..f7944a519a2 100644
--- a/contrib/python/Twisted/py3/twisted/mail/relaymanager.py
+++ b/contrib/python/Twisted/py3/twisted/mail/relaymanager.py
@@ -10,12 +10,12 @@ configurations. Instead of sending mail directly to the recipient, a sender
sends mail to a smart host. The smart host finds the mail exchange server for
the recipient and sends on the message.
"""
+from __future__ import annotations
import email.utils
import os
import pickle
import time
-from typing import Type
from twisted.application import internet
from twisted.internet import protocol
@@ -69,7 +69,7 @@ class ManagedRelayerMixin:
self.manager.notifyDone(self.factory)
-class SMTPManagedRelayer(ManagedRelayerMixin, relay.SMTPRelayer): # type: ignore[misc]
+class SMTPManagedRelayer(ManagedRelayerMixin, relay.SMTPRelayer):
"""
An SMTP managed relayer.
@@ -100,7 +100,7 @@ class SMTPManagedRelayer(ManagedRelayerMixin, relay.SMTPRelayer): # type: ignor
relay.SMTPRelayer.__init__(self, messages, *args, **kw)
-class ESMTPManagedRelayer(ManagedRelayerMixin, relay.ESMTPRelayer): # type: ignore[misc]
+class ESMTPManagedRelayer(ManagedRelayerMixin, relay.ESMTPRelayer):
"""
An ESMTP managed relayer.
@@ -154,7 +154,7 @@ class SMTPManagedRelayerFactory(protocol.ClientFactory):
@ivar pKwArgs: Keyword arguments for L{SMTPClient.__init__}
"""
- protocol: "Type[protocol.Protocol]" = SMTPManagedRelayer
+ protocol: type[protocol.Protocol] = SMTPManagedRelayer
def __init__(self, messages, manager, *args, **kw):
"""
@@ -674,7 +674,7 @@ class SmartHostSMTPRelayingManager:
filenames of messages the managed relayer is responsible for.
"""
- factory: Type[protocol.ClientFactory] = SMTPManagedRelayerFactory
+ factory: type[protocol.ClientFactory] = SMTPManagedRelayerFactory
PORT = 25
diff --git a/contrib/python/Twisted/py3/twisted/mail/smtp.py b/contrib/python/Twisted/py3/twisted/mail/smtp.py
index 55511647e66..058f4e24783 100644
--- a/contrib/python/Twisted/py3/twisted/mail/smtp.py
+++ b/contrib/python/Twisted/py3/twisted/mail/smtp.py
@@ -19,13 +19,12 @@ import time
import warnings
from email.utils import parseaddr
from io import BytesIO
-from typing import Type
from zope.interface import implementer
from twisted import cred
from twisted.copyright import longversion
-from twisted.internet import defer, error, protocol, reactor
+from twisted.internet import defer, protocol, reactor
from twisted.internet._idna import _idnaText
from twisted.internet.interfaces import ISSLTransport, ITLSTransport
from twisted.mail._cred import (
@@ -950,6 +949,10 @@ class SMTPClient(basic.LineReceiver, policies.TimeoutMixin):
self.code = -1
self.log = util.LineLog(logsize)
+ # A `Failure` giving the reason we were unable to successfully send the message,
+ # if any. Otherwise, `None`.
+ self._failureReason = None
+
def sendLine(self, line):
# Log sendLine only if you are in debug mode for performance
if self.debug:
@@ -968,6 +971,7 @@ class SMTPClient(basic.LineReceiver, policies.TimeoutMixin):
"""
We are no longer connected
"""
+ self._failureReason = reason
self.setTimeout(None)
self.mailFile = None
@@ -1862,7 +1866,7 @@ class SMTPSenderFactory(protocol.ClientFactory):
"""
domain = DNSNAME
- protocol: Type[SMTPClient] = SMTPSender
+ protocol: type[SMTPClient] = SMTPSender
def __init__(self, fromEmail, toEmail, file, deferred, retries=5, timeout=None):
"""
@@ -1922,8 +1926,22 @@ class SMTPSenderFactory(protocol.ClientFactory):
self._processConnectionError(connector, err)
def _processConnectionError(self, connector, err):
+ # If the initial connection succeeded, but there was a later error
+ # (such as TLS validation failure), we're more interested in that than
+ # "Connection was closed cleanly" or whatever.
+ if (
+ self.currentProtocol is not None
+ and self.currentProtocol._failureReason is not None
+ ):
+ err = self.currentProtocol._failureReason
+
self.currentProtocol = None
- if (self.retries < 0) and (not self.sendFinished):
+ if self.sendFinished:
+ # we already completed the send, and the deferred has been called.
+ # There is nothing more we can do.
+ return
+
+ if self.retries < 0:
log.msg("SMTP Client retrying server. Retry: %s" % -self.retries)
# Rewind the file in case part of it was read while attempting to
@@ -1931,12 +1949,14 @@ class SMTPSenderFactory(protocol.ClientFactory):
self.file.seek(0, 0)
connector.connect()
self.retries += 1
- elif not self.sendFinished:
- # If we were unable to communicate with the SMTP server a ConnectionDone will be
- # returned. We want a more clear error message for debugging
- if err.check(error.ConnectionDone):
- err.value = SMTPConnectError(-1, "Unable to connect to server.")
- self.result.errback(err.value)
+ return
+
+ # do our best to wrap the error into something more meaningful.
+ exn = SMTPConnectError(
+ -1, "Unable to connect to SMTP server: " + str(err.value)
+ )
+ exn.__cause__ = err.value
+ self.result.errback(exn)
def buildProtocol(self, addr):
p = self.protocol(self.domain, self.nEmails * 2 + 2)
diff --git a/contrib/python/Twisted/py3/twisted/names/authority.py b/contrib/python/Twisted/py3/twisted/names/authority.py
index 33df6c00686..d6043d92a7f 100644
--- a/contrib/python/Twisted/py3/twisted/names/authority.py
+++ b/contrib/python/Twisted/py3/twisted/names/authority.py
@@ -307,12 +307,11 @@ class BindAuthority(FileAuthority):
Load records from C{filename}.
@param filename: file to read from
- @type filename: L{bytes}
"""
fp = FilePath(filename)
# Not the best way to set an origin. It can be set using $ORIGIN
# though.
- self.origin = nativeString(fp.basename() + b".")
+ self.origin = fp.asTextMode().basename() + "."
lines = fp.getContent().splitlines(True)
lines = self.stripComments(lines)
diff --git a/contrib/python/Twisted/py3/twisted/names/dns.py b/contrib/python/Twisted/py3/twisted/names/dns.py
index c7644ef50d6..257efc93122 100644
--- a/contrib/python/Twisted/py3/twisted/names/dns.py
+++ b/contrib/python/Twisted/py3/twisted/names/dns.py
@@ -11,13 +11,16 @@ Future Plans:
from __future__ import annotations
# System imports
+import contextvars
import inspect
import random
import socket
import struct
+from collections.abc import Generator, Sequence
+from contextlib import contextmanager
from io import BytesIO
from itertools import chain
-from typing import Optional, Sequence, SupportsInt, Union, overload
+from typing import SupportsInt, overload
from zope.interface import Attribute, Interface, implementer
@@ -126,6 +129,7 @@ __all__ = [
"OP_UPDATE",
"PORT",
"AuthoritativeDomainError",
+ "DNSDecodeError",
"DNSQueryTimeoutError",
"DomainError",
]
@@ -402,7 +406,7 @@ def _str2time(s: str) -> int:
@overload
-def str2time(s: Union[str, bytes, int]) -> int:
+def str2time(s: str | bytes | int) -> int:
...
@@ -411,7 +415,7 @@ def str2time(s: None) -> None:
...
-def str2time(s: Union[str, bytes, int, None]) -> Union[int, None]:
+def str2time(s: str | bytes | int | None) -> int | None:
"""
Parse a string description of an interval into an integer number of seconds.
@@ -444,6 +448,87 @@ def readPrecisely(file, l):
return buff
+class DNSDecodeError(ValueError):
+ """
+ Raised when a DNS message cannot be decoded because it violates a
+ protocol-level safety limit.
+ """
+
+
+class _DecodeContext:
+ """
+ Mutable state shared between the L{IEncodable} decoders invoked while
+ reading a single DNS message.
+
+ The primary purpose is to bound the total number of compression-pointer
+ jumps taken across every name in the message, defending against packets
+ that fan out thousands of records pointing to deeply chained pointers.
+
+ This class is private. External callers must not rely on it; the
+ per-message scope is installed and torn down by L{Message.decode}
+ through L{_decodeContextVar}.
+
+ @ivar jumps: The number of compression pointers followed so far.
+ @ivar maxJumps: The inclusive upper bound on L{jumps}. Exceeding it
+ causes L{registerJump} to raise L{DNSDecodeError}.
+ """
+
+ __slots__ = ("jumps", "maxJumps")
+
+ def __init__(self, maxJumps: int = 1000) -> None:
+ self.jumps = 0
+ self.maxJumps = maxJumps
+
+ def registerJump(self) -> None:
+ """
+ Record that a compression pointer has been followed.
+
+ The check is performed before any further bytes are read so the
+ caller fails fast as soon as the aggregate limit is breached, even
+ if additional records remain in the buffer.
+
+ @raise DNSDecodeError: if the cumulative number of jumps exceeds
+ L{maxJumps}.
+ """
+ self.jumps += 1
+ if self.jumps > self.maxJumps:
+ raise DNSDecodeError(
+ "Too many compression pointers while decoding DNS message "
+ f"(limit is {self.maxJumps})"
+ )
+
+
+# Private module-level L{contextvars.ContextVar} used to share a single
+# L{_DecodeContext} across the re-entrant calls performed while decoding one
+# DNS message. L{contextvars} (rather than a plain module attribute) is used
+# on purpose: although Twisted's reactor is single-threaded, message decoding
+# is re-entrant across many records in a single pass and L{ContextVar}
+# guarantees the scope is restored correctly on exit -- and remains isolated
+# per-task should a future caller decode messages from multiple
+# L{asyncio}-style contexts concurrently.
+_decodeContextVar: contextvars.ContextVar[
+ _DecodeContext | None
+] = contextvars.ContextVar("_dnsDecodeContext", default=None)
+
+
+@contextmanager
+def _installDecodeContext(context: _DecodeContext) -> Generator[_DecodeContext]:
+ """
+ Install C{context} on L{_decodeContextVar} for the duration of the
+ C{with} block and restore the previous value on exit.
+
+ This wraps the L{contextvars.ContextVar.set} / L{contextvars.ContextVar.reset}
+ token dance so call sites can use a plain C{with} statement.
+
+ @param context: The L{_DecodeContext} to install as the active context.
+ """
+ token = _decodeContextVar.set(context)
+ try:
+ yield context
+ finally:
+ _decodeContextVar.reset(token)
+
+
class IEncodable(Interface):
"""
Interface for something which can be encoded to and decoded
@@ -549,8 +634,17 @@ class Name:
@ivar name: A byte string giving the name.
@type name: L{bytes}
+
+ @ivar maxCompressionPointers: Per-message cap on the total number of
+ compression-pointer dereferences L{decode} will follow before
+ raising L{DNSDecodeError}. Defaults to C{1000}. Override it on
+ a subclass or individual instance to tune the trade-off between
+ tolerance for legitimately verbose messages and resistance to
+ denial-of-service attacks.
"""
+ maxCompressionPointers: int = 1000
+
def __init__(self, name: bytes | str = b""):
"""
@param name: A name.
@@ -595,16 +689,33 @@ class Name:
"""
Decode a byte string into this Name.
+ When invoked from L{Message.decode}, a shared compression-pointer
+ counter is picked up transparently from the private
+ L{_decodeContextVar}. Standalone callers get a fresh per-call
+ counter seeded from L{maxCompressionPointers}, so existing code
+ keeps working unchanged while still being protected against
+ pathological inputs.
+
@type strio: file
@param strio: Bytes will be read from this file until the full Name
- is decoded.
+ is decoded.
+
+ @type length: L{int} or L{None}
+ @param length: Present for compatibility with the L{IEncodable}
+ interface; ignored by this decoder.
@raise EOFError: Raised when there are not enough bytes available
- from C{strio}.
+ from C{strio}.
+
+ @raise ValueError: Raised when the name cannot be decoded because
+ it contains a compression loop.
- @raise ValueError: Raised when the name cannot be decoded (for example,
- because it contains a loop).
+ @raise DNSDecodeError: Raised when the cumulative number of
+ compression-pointer jumps exceeds the configured limit.
"""
+ context = _decodeContextVar.get()
+ if context is None:
+ context = _DecodeContext(maxJumps=self.maxCompressionPointers)
visited = set()
self.name = b""
off = 0
@@ -616,6 +727,7 @@ class Name:
return
if (l >> 6) == 3:
new_off = (l & 63) << 8 | ord(readPrecisely(strio, 1))
+ context.registerJump()
if new_off in visited:
raise ValueError("Compression loop in encoded name")
visited.add(new_off)
@@ -660,7 +772,7 @@ class Query:
@type cls: L{int}
"""
- def __init__(self, name: Union[bytes, str] = b"", type: int = A, cls: int = IN):
+ def __init__(self, name: bytes | str = b"", type: int = A, cls: int = IN):
"""
@type name: L{bytes} or L{str}
@param name: See L{Query.name}
@@ -989,11 +1101,11 @@ class RRHeader(tputil.FancyEqMixin):
def __init__(
self,
- name: Union[bytes, str] = b"",
+ name: bytes | str = b"",
type: int = A,
cls: int = IN,
ttl: SupportsInt = 0,
- payload: Optional[IEncodableRecord] = None,
+ payload: IEncodableRecord | None = None,
auth: bool = False,
):
"""
@@ -1093,7 +1205,7 @@ class SimpleRecord(tputil.FancyStrMixin, tputil.FancyEqMixin):
showAttributes = (("name", "name", "%s"), "ttl")
compareAttributes = ("name", "ttl")
- TYPE: Optional[int] = None
+ TYPE: int | None = None
name = None
def __init__(self, name=b"", ttl=None):
@@ -1568,7 +1680,7 @@ class Record_A6(tputil.FancyStrMixin, tputil.FancyEqMixin):
prefixLen: int = 0,
suffix: bytes | str = "::",
prefix: bytes | str = b"",
- ttl: Union[str, bytes, int, None] = None,
+ ttl: str | bytes | int | None = None,
):
"""
@param suffix: An IPv6 address suffix in in RFC 2373 format.
@@ -1943,7 +2055,7 @@ class Record_HINFO(tputil.FancyStrMixin, tputil.FancyEqMixin):
self,
cpu: bytes = b"",
os: bytes = b"",
- ttl: Union[str, bytes, int, None] = None,
+ ttl: str | bytes | int | None = None,
):
self.cpu, self.os = cpu, os
self.ttl = str2time(ttl)
@@ -2488,8 +2600,17 @@ class Message(tputil.FancyEqMixin):
header fields.
@ivar _sectionNames: The names of attributes representing the record
sections of this message.
+
+ @ivar maxCompressionPointers: Per-message cap on the total number of
+ compression-pointer dereferences L{decode} will follow across every
+ name in the message before raising L{DNSDecodeError}. Defaults to
+ C{1000}. Override it on a subclass or individual instance to tune
+ the trade-off between tolerance for legitimately verbose messages
+ and resistance to denial-of-service attacks.
"""
+ maxCompressionPointers: int = 1000
+
compareAttributes = (
"id",
"answer",
@@ -2704,19 +2825,29 @@ class Message(tputil.FancyEqMixin):
self.checkingDisabled = (byte4 >> 4) & 1
self.rCode = byte4 & 0xF
- self.queries = []
- for i in range(nqueries):
- q = Query()
- try:
- q.decode(strio)
- except EOFError:
- return
- self.queries.append(q)
+ # A single shared counter bounds the total compression-pointer work
+ # performed across every name in this message. It is installed on
+ # the private context variable so nested record decoders pick it up
+ # without needing to thread it through each signature.
+ decodeContext = _DecodeContext(maxJumps=self.maxCompressionPointers)
+ with _installDecodeContext(decodeContext):
+ self.queries = []
+ for i in range(nqueries):
+ q = Query()
+ try:
+ q.decode(strio)
+ except EOFError:
+ return
+ self.queries.append(q)
- items = ((self.answers, nans), (self.authority, nns), (self.additional, nadd))
+ items = (
+ (self.answers, nans),
+ (self.authority, nns),
+ (self.additional, nadd),
+ )
- for l, n in items:
- self.parseRecords(l, n, strio)
+ for l, n in items:
+ self.parseRecords(l, n, strio)
def parseRecords(self, list, num, strio):
for i in range(num):
diff --git a/contrib/python/Twisted/py3/twisted/names/server.py b/contrib/python/Twisted/py3/twisted/names/server.py
index 63fff7a2778..6ff64fcb6e6 100644
--- a/contrib/python/Twisted/py3/twisted/names/server.py
+++ b/contrib/python/Twisted/py3/twisted/names/server.py
@@ -62,7 +62,7 @@ class DNSServerFactory(protocol.ServerFactory):
"""
# Type is wrong. See: https://twistedmatrix.com/trac/ticket/10004#ticket
- protocol = dns.DNSProtocol # type: ignore[assignment]
+ protocol = dns.DNSProtocol
cache = None
_messageFactory = dns.Message
diff --git a/contrib/python/Twisted/py3/twisted/pair/ip.py b/contrib/python/Twisted/py3/twisted/pair/ip.py
index 3606abf2721..da1bb8af67c 100644
--- a/contrib/python/Twisted/py3/twisted/pair/ip.py
+++ b/contrib/python/Twisted/py3/twisted/pair/ip.py
@@ -57,7 +57,15 @@ class IPProtocol(protocol.AbstractDatagramProtocol):
self.ipProtos[num] = []
self.ipProtos[num].append(proto)
- def datagramReceived(self, data, partial, dest, source, protocol):
+ # this never should have subclassed AbstractDatagramProtocol
+ def datagramReceived( # type:ignore[override]
+ self,
+ data,
+ partial,
+ dest,
+ source,
+ protocol,
+ ):
header = IPHeader(data)
for proto in self.ipProtos.get(header.protocol, ()):
proto.datagramReceived(
diff --git a/contrib/python/Twisted/py3/twisted/pair/rawudp.py b/contrib/python/Twisted/py3/twisted/pair/rawudp.py
index c4f2d97ef4e..ff3ddfdb0eb 100644
--- a/contrib/python/Twisted/py3/twisted/pair/rawudp.py
+++ b/contrib/python/Twisted/py3/twisted/pair/rawudp.py
@@ -37,7 +37,8 @@ class RawUDPProtocol(protocol.AbstractDatagramProtocol):
self.udpProtos[num] = []
self.udpProtos[num].append(proto)
- def datagramReceived(
+ # This never really should have subclassed AbstractDatagramProtocol
+ def datagramReceived( # type:ignore[override]
self,
data,
partial,
diff --git a/contrib/python/Twisted/py3/twisted/pair/tuntap.py b/contrib/python/Twisted/py3/twisted/pair/tuntap.py
index 2564257966b..85caa47e15f 100644
--- a/contrib/python/Twisted/py3/twisted/pair/tuntap.py
+++ b/contrib/python/Twisted/py3/twisted/pair/tuntap.py
@@ -15,7 +15,6 @@ import platform
import struct
import warnings
from collections import namedtuple
-from typing import Tuple
from zope.interface import Attribute, Interface, implementer
@@ -257,7 +256,7 @@ class TuntapPort(abstract.FileDescriptor):
self.logstr = f"{logPrefix} ({self._mode.name})"
def __repr__(self) -> str:
- args: Tuple[str, ...] = (fullyQualifiedName(self.protocol.__class__),)
+ args: tuple[str, ...] = (fullyQualifiedName(self.protocol.__class__),)
if self.connected:
args = args + ("",)
else:
diff --git a/contrib/python/Twisted/py3/twisted/persisted/dirdbm.py b/contrib/python/Twisted/py3/twisted/persisted/dirdbm.py
index e9bdc560f55..69894d136f6 100644
--- a/contrib/python/Twisted/py3/twisted/persisted/dirdbm.py
+++ b/contrib/python/Twisted/py3/twisted/persisted/dirdbm.py
@@ -22,7 +22,8 @@ import base64
import glob
import os
import pickle
-from typing import AnyStr, Iterable, Mapping, TypeVar, overload
+from collections.abc import Iterable, Mapping
+from typing import AnyStr, TypeVar, overload
from twisted.python.filepath import FilePath
@@ -258,7 +259,7 @@ class DirDBM:
Add all the key/value pairs in L{dict} to this dirdbm. Any conflicting
keys will be overwritten with the values from L{dict}.
- @param dict: A mapping of key/value pairs to add to this dirdbm.
+ @param other: A mapping of key/value pairs to add to this dirdbm.
"""
for key, val in other.items():
self[key] = val
diff --git a/contrib/python/Twisted/py3/twisted/persisted/styles.py b/contrib/python/Twisted/py3/twisted/persisted/styles.py
index 2d014dc8477..1e1f8406b93 100644
--- a/contrib/python/Twisted/py3/twisted/persisted/styles.py
+++ b/contrib/python/Twisted/py3/twisted/persisted/styles.py
@@ -12,12 +12,11 @@ import inspect
import pickle
import types
from io import StringIO as _cStringIO
-from typing import Dict
from twisted.python import log, reflect
from twisted.python.compat import _PYPY
-oldModules: Dict[str, types.ModuleType] = {}
+oldModules: dict[str, types.ModuleType] = {}
_UniversalPicklingError = pickle.PicklingError
@@ -241,7 +240,7 @@ class Ephemeral:
self.__class__ = Ephemeral
-versionedsToUpgrade: Dict[int, "Versioned"] = {}
+versionedsToUpgrade: dict[int, "Versioned"] = {}
upgraded = {}
diff --git a/contrib/python/Twisted/py3/twisted/plugin.py b/contrib/python/Twisted/py3/twisted/plugin.py
index 45180014cdc..ecda72ae02d 100644
--- a/contrib/python/Twisted/py3/twisted/plugin.py
+++ b/contrib/python/Twisted/py3/twisted/plugin.py
@@ -10,12 +10,14 @@ Plugin system for Twisted.
@author: Glyph Lefkowitz
"""
+from __future__ import annotations
import os
import pickle
import sys
import types
-from typing import Iterable, Optional, Type, TypeVar
+from collections.abc import Iterable
+from typing import TypeVar
from zope.interface import Interface, providedBy
@@ -196,7 +198,7 @@ _TInterface = TypeVar("_TInterface", bound=Interface)
def getPlugins(
- interface: Type[_TInterface], package: Optional[types.ModuleType] = None
+ interface: type[_TInterface], package: types.ModuleType | None = None
) -> Iterable[_TInterface]:
"""
Retrieve all plugins implementing the given interface beneath the given module.
diff --git a/contrib/python/Twisted/py3/twisted/plugins/__init__.py b/contrib/python/Twisted/py3/twisted/plugins/__init__.py
index d4b94bbca0d..e0346962538 100644
--- a/contrib/python/Twisted/py3/twisted/plugins/__init__.py
+++ b/contrib/python/Twisted/py3/twisted/plugins/__init__.py
@@ -14,9 +14,7 @@ the __path__ variable.
@author: Glyph Lefkowitz
"""
-from typing import List
-
from twisted.plugin import pluginPackagePaths
__path__.extend(pluginPackagePaths(__name__))
-__all__: List[str] = [] # nothing to see here, move along, move along
+__all__: list[str] = [] # nothing to see here, move along, move along
diff --git a/contrib/python/Twisted/py3/twisted/plugins/cred_unix.py b/contrib/python/Twisted/py3/twisted/plugins/cred_unix.py
index 3d929f75cce..4bb156a7249 100644
--- a/contrib/python/Twisted/py3/twisted/plugins/cred_unix.py
+++ b/contrib/python/Twisted/py3/twisted/plugins/cred_unix.py
@@ -93,16 +93,19 @@ class UNIXChecker:
def checkSpwd(self, spwd, username, password):
"""
- Obtain the encrypted password for C{username} from the
- Unix shadow password database using L{spwd.getspnam},
- and see if it it matches it matches C{password}.
+ Obtain the encrypted password for C{username} from the Unix shadow
+ password database using C{spwd.getspnam}, and see if it it matches it
+ matches C{password}.
- @param spwd: Module which provides functions which
- access to the Unix shadow password database.
+ @param spwd: Module which provides functions which access to the Unix
+ shadow password database.
@type spwd: C{module}
+
@param username: The user to look up in the Unix password database.
@type username: L{unicode}/L{str} or L{bytes}
+
@param password: The password to compare.
+
@type username: L{unicode}/L{str} or L{bytes}
"""
try:
diff --git a/contrib/python/Twisted/py3/twisted/plugins/twisted_core.py b/contrib/python/Twisted/py3/twisted/plugins/twisted_core.py
index 140ac918bdc..c8b9509f4c0 100644
--- a/contrib/python/Twisted/py3/twisted/plugins/twisted_core.py
+++ b/contrib/python/Twisted/py3/twisted/plugins/twisted_core.py
@@ -7,6 +7,7 @@ from twisted.internet.endpoints import (
_SystemdParser,
_TCP6ServerParser,
_TLSClientEndpointParser,
+ _TLSServerEndpointParser,
)
from twisted.protocols.haproxy._parser import (
HAProxyServerParser as _HAProxyServerParser,
@@ -16,4 +17,5 @@ systemdEndpointParser = _SystemdParser()
tcp6ServerEndpointParser = _TCP6ServerParser()
stdioEndpointParser = _StandardIOParser()
tlsClientEndpointParser = _TLSClientEndpointParser()
+tlsServerEndpointParser = _TLSServerEndpointParser()
_haProxyServerEndpointParser = _HAProxyServerParser()
diff --git a/contrib/python/Twisted/py3/twisted/positioning/_sentence.py b/contrib/python/Twisted/py3/twisted/positioning/_sentence.py
index b40dd060ed8..ee69bbe7706 100644
--- a/contrib/python/Twisted/py3/twisted/positioning/_sentence.py
+++ b/contrib/python/Twisted/py3/twisted/positioning/_sentence.py
@@ -3,7 +3,6 @@
"""
Generic sentence handling tools: hopefully reusable.
"""
-from typing import Set
class _BaseSentence:
@@ -35,7 +34,7 @@ class _BaseSentence:
@type ALLOWED_ATTRIBUTES: C{set} of C{str}
"""
- ALLOWED_ATTRIBUTES: Set[str] = set()
+ ALLOWED_ATTRIBUTES: set[str] = set()
def __init__(self, sentenceData):
"""
diff --git a/contrib/python/Twisted/py3/twisted/positioning/base.py b/contrib/python/Twisted/py3/twisted/positioning/base.py
index 460f5b4ff20..220b07eaacd 100644
--- a/contrib/python/Twisted/py3/twisted/positioning/base.py
+++ b/contrib/python/Twisted/py3/twisted/positioning/base.py
@@ -8,9 +8,10 @@ Generic positioning base classes.
"""
+from collections.abc import Sequence
from functools import partial
from operator import attrgetter
-from typing import ClassVar, Sequence
+from typing import ClassVar
from zope.interface import implementer
diff --git a/contrib/python/Twisted/py3/twisted/protocols/_sni.py b/contrib/python/Twisted/py3/twisted/protocols/_sni.py
new file mode 100644
index 00000000000..ce8b5bf9ffb
--- /dev/null
+++ b/contrib/python/Twisted/py3/twisted/protocols/_sni.py
@@ -0,0 +1,347 @@
+# -*- test-case-name: twisted.internet.test.test_endpoints -*-
+
+from __future__ import annotations
+
+from dataclasses import dataclass
+from functools import cached_property, partial
+from typing import Callable
+
+from zope.interface import implementer
+
+from OpenSSL.crypto import FILETYPE_PEM
+from OpenSSL.SSL import Connection, Context
+
+from cryptography.x509 import DNSName, ExtensionOID, load_pem_x509_certificate
+
+from twisted.internet.defer import Deferred
+from twisted.internet.interfaces import (
+ IListeningPort,
+ IOpenSSLServerConnectionCreator,
+ IProtocolFactory,
+ IReactorTime,
+ IStreamServerEndpoint,
+)
+from twisted.internet.ssl import (
+ DN,
+ Certificate,
+ CertificateOptions,
+ KeyPair,
+ PrivateCertificate,
+)
+from twisted.logger import Logger
+from twisted.protocols._tls_legacy import SomeConnectionCreator
+from twisted.protocols.tls import TLSMemoryBIOFactory, TLSMemoryBIOProtocol
+from twisted.python.filepath import FilePath
+
+log = Logger()
+
+
+def lookupWithWildcard(
+ flatLookup: Callable[[bytes | None], Context | None], name: bytes | None
+) -> Context | None:
+ """
+ Look up an OpenSSL context for the given domain name, or construct a
+ default one suitable for bootstrapping the connection.
+ """
+ candidate = flatLookup(name)
+ if candidate is None:
+ if name is not None:
+ segments = name.split(b".")
+ segments[0] = b"*"
+ wildcardName = b".".join(segments)
+ candidate = flatLookup(wildcardName)
+ if candidate is None:
+ log.warn("no server certificate for name {name!r}", name=name)
+ return candidate
+
+
+@implementer(IOpenSSLServerConnectionCreator)
+@dataclass
+class SNIConnectionCreator:
+ """
+ (Private) L{IOpenSSLServerConnectionCreator} implementation that creates an
+ OpenSSL connection with a context that will switch to the appropriate one.
+ """
+
+ _contextLookup: Callable[[bytes | None], Context | None]
+ """
+ This method should look up an OpenSSL Context object for the given DNS
+ name, or one that is suitable for unidentified clients. The lookup may
+ fail and return None.
+ """
+
+ @cached_property
+ def defaultContext(self) -> Context:
+ """
+ Create and cache the OpenSSL context that connections will initially be
+ using. This constructs a default context which doesn't know its domain
+ name by delegating to C{self._contextLookup} with None, then sets the
+ TLS extension servername callback to get invoked to I{switch} contexts
+ by doing another lookup when the client sends its servername.
+
+ @note: The client I{might} never send a servername at all, in which
+ case it will be stuck. This edge case is not handled particularly
+ well right now. Handling it better would involve some changes in
+ this code (to hook the handshake completion callback rather than
+ just the servername callback) as well as better ability to
+ customize which certificate produces the default context in the
+ implementation of C{_contextLookup}, which is to say, mostly
+ L{PEMObjects}.
+ """
+ lookedUp = lookupWithWildcard(self._contextLookup, None)
+ if lookedUp is None:
+ blankOptions = CertificateOptions(
+ contextForServerName=partial(
+ lookupWithWildcard,
+ self._contextLookup,
+ )
+ )
+ return blankOptions.getContext()
+
+ return lookedUp
+
+ def serverConnectionForTLS(self, protocol: TLSMemoryBIOProtocol) -> Connection:
+ """
+ Construct an OpenSSL server connection that can react to the TLS
+ servername callback to select an appropriate certificate based on a
+ mapping.
+
+ @param protocol: The protocol initiating a TLS connection.
+
+ @return: a newly-created connection.
+ """
+ return Connection(self.defaultContext)
+
+
+@implementer(IStreamServerEndpoint)
+class TLSServerEndpoint:
+ """
+ A wrapper L{IStreamServerEndpoint} that can run TLS over an arbitrary other
+ L{IStreamServerEndpoint} (most commonly, TCP).
+ """
+
+ def __init__(
+ self,
+ endpoint: IStreamServerEndpoint,
+ connectionCreator: SomeConnectionCreator,
+ clock: IReactorTime | None = None,
+ ) -> None:
+ """
+ @param endpoint: the endpoint to run over.
+
+ @param connectionCreator: The object that will construct OpenSSL
+ connections (or Contexts).
+
+ @param clock: The clock which will be used to schedule buffer flushes.
+ """
+ self.endpoint = endpoint
+ self.connectionCreator = connectionCreator
+ self.clock = clock
+
+ def listen(self, factory: IProtocolFactory) -> Deferred[IListeningPort]:
+ """
+ Begin listening with the given factory.
+ """
+ return self.endpoint.listen(
+ TLSMemoryBIOFactory(
+ self.connectionCreator, False, factory, clock=self.clock
+ )
+ )
+
+
+def _getSubjectAltNames(c: Certificate) -> list[str]:
+ """
+ Get all the DNSName SANs for a given certificate.
+ """
+ return [
+ value
+ for extension in load_pem_x509_certificate(c.dumpPEM()).extensions
+ if extension.oid == ExtensionOID.SUBJECT_ALTERNATIVE_NAME
+ for value in extension.value.get_values_for_type(DNSName)
+ ]
+
+
+def autoReloadingDirectoryOfPEMs(
+ path: FilePath[str],
+) -> Callable[[bytes | None], Context | None]:
+ """
+ Construct a callable that can look up a HTTPS certificate based on their
+ DNS names, by inspecting a directory full of PEM objects. When
+ encountering a lookup failure, the directory will be reloaded, so that if
+ new certificates are added they will be picked up.
+ """
+ # TODO: some flaws with this approach
+
+ """
+ 1. too much re-scanning; re-reading full file contents for every single
+ certificate even if only one has changed. a mtime/length cache
+ would be a good place to start with this
+
+ 2. too trusting; we get a network request for a billion certificate
+ names per second, we go ahead and do a bunch of work every single
+ time (and, see point 1, re-scan and re-parse every single file)
+
+ 3. not *enough* re-scanning on the happy path; if certificates go
+ stale, we just let them sit there until we get an unknown hostname
+
+ 4. we don't look at notAfter/notBefore, so if we find multiple certs,
+ we may end up using the wrong one
+
+ """
+
+ certMap: dict[str, CertificateOptions]
+
+ def doReload() -> None:
+ nonlocal certMap
+ certMap = PEMObjects.fromDirectory(path).inferDomainMapping()
+
+ def lookup(name: bytes | None, shouldReload: bool = True) -> Context | None:
+ name = next(iter(certMap.keys()), "").encode() if name in (None, b"") else name
+ assert name is not None
+ if (options := certMap.get(name.decode())) is not None:
+ return options.getContext()
+ if not shouldReload:
+ return None
+ msg = "could not find domain {name}, re-loading {path}"
+ log.warn(msg, name=name, path=path)
+ doReload()
+ return lookup(name, False)
+
+ doReload()
+ return lookup
+
+
+@dataclass
+class PEMObjects:
+ """
+ A collection of objects loaded from a collection of PEM-encoded files.
+ """
+
+ _certificates: list[tuple[FilePath[str], Certificate]]
+ """
+ A list of pairs of (FilePath, Certificate) that indicates what files
+ contain what certificates.
+ """
+ _keyPairs: list[tuple[FilePath[str], KeyPair]]
+ """
+ A list of pairs of (FilePath, KeyPair) that indicates what pairs contain
+ what certificates.
+ """
+
+ @classmethod
+ def fromDirectory(cls, directory: FilePath[str]) -> PEMObjects:
+ """
+ Walk through the given directory looking for files with a `.pem`
+ extension, and instantiate a L{PEMObjects} containing all certificates
+ and key pairs from those files.
+
+ @param directory: a L{FilePath} pointing at a directory in the
+ filesystem which may contain some PEM files.
+ """
+ self = PEMObjects([], [])
+ for fp in directory.walk():
+ if fp.basename().endswith(".pem") and fp.isfile():
+ subself = cls.fromFile(fp)
+ self._certificates.extend(subself._certificates)
+ self._keyPairs.extend(subself._keyPairs)
+ return self
+
+ @classmethod
+ def fromFile(cls, fp: FilePath[str]) -> PEMObjects:
+ """
+ Load some objects from the lines of a single PEM file.
+
+ @param fp: A L{FilePath} pointing at a file on the filesystem whose
+ contents should be PEM data.
+ """
+ certBlobs: list[bytes] = []
+ keyBlobs: list[bytes] = []
+ blobs = [b""]
+ with fp.open() as pemlines:
+ for line in pemlines:
+ if line.startswith(b"-----BEGIN"):
+ blobs = certBlobs if b"CERTIFICATE" in line else keyBlobs
+ blobs.append(b"")
+ blobs[-1] += line
+ return cls(
+ _keyPairs=[
+ (fp, KeyPair.load(keyBlob, FILETYPE_PEM)) for keyBlob in keyBlobs
+ ],
+ _certificates=[
+ (fp, Certificate.loadPEM(certBlob)) for certBlob in certBlobs
+ ],
+ )
+
+ def inferDomainMapping(self) -> dict[str, CertificateOptions]:
+ """
+ Return a mapping of DNS name to L{CertificateOptions}.
+ """
+
+ privateCerts = []
+
+ certificatesByFingerprint = {
+ certificate.getPublicKey().keyHash(): certificate
+ for (_, certificate) in self._certificates
+ }
+
+ for pairPath, keyPair in self._keyPairs:
+ keyHash = keyPair.keyHash()
+ matchingCertificate = certificatesByFingerprint.pop(keyHash, None)
+ if matchingCertificate is None:
+ # log something?
+ log.warn(
+ "unused private key at {path} with hash {hash}",
+ path=pairPath.path,
+ hash=keyHash,
+ )
+ continue
+ privateCerts.append(
+ (
+ _getSubjectAltNames(matchingCertificate),
+ PrivateCertificate.fromCertificateAndKeyPair(
+ matchingCertificate, keyPair
+ ),
+ )
+ )
+
+ noPrivateKeys = [
+ Certificate.load(dumped)
+ for dumped in {each.dump() for each in certificatesByFingerprint.values()}
+ ]
+
+ def hashDN(dn: DN) -> tuple[tuple[str, bytes], ...]:
+ return tuple(sorted(dn.items()))
+
+ bySubject = {
+ hashDN(eachIntermediate.getSubject()): eachIntermediate
+ for eachIntermediate in noPrivateKeys
+ }
+
+ result: dict[str, CertificateOptions] = {}
+
+ def flatLookup(servername: bytes | None) -> Context | None:
+ if servername is None:
+ return None
+ options = result.get(servername.decode("ascii"))
+ if options is not None:
+ return options.getContext()
+ return None
+
+ def nameToContext(servername: bytes | None) -> Context | None:
+ return lookupWithWildcard(flatLookup, servername)
+
+ for names, privateCert in privateCerts:
+ chain = []
+ chained = privateCert
+ while hashDN(chained.getIssuer()) in bySubject:
+ chained = bySubject[hashDN(chained.getIssuer())]
+ chain.append(chained.original)
+ options = CertificateOptions(
+ certificate=privateCert.original,
+ privateKey=privateCert.privateKey.original,
+ extraCertChain=chain,
+ contextForServerName=nameToContext,
+ )
+ for dnsName in names:
+ result[dnsName] = options
+ return result
diff --git a/contrib/python/Twisted/py3/twisted/protocols/_tls_legacy.py b/contrib/python/Twisted/py3/twisted/protocols/_tls_legacy.py
new file mode 100644
index 00000000000..072d8b8c722
--- /dev/null
+++ b/contrib/python/Twisted/py3/twisted/protocols/_tls_legacy.py
@@ -0,0 +1,110 @@
+# -*- test-case-name: twisted.internet.test.test_endpoints.TLSEndpointsTests -*-
+"""
+Handler for the various legacy things that a C{contextFactory} can be.
+"""
+
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Callable, Union
+from warnings import warn
+
+from OpenSSL.SSL import Connection, Context
+
+from twisted.internet.interfaces import (
+ IOpenSSLClientConnectionCreator,
+ IOpenSSLContextFactory,
+ IOpenSSLServerConnectionCreator,
+)
+
+if TYPE_CHECKING:
+ # Circular import.
+ from twisted.protocols.tls import TLSMemoryBIOProtocol
+
+SomeConnectionCreator = Union[
+ IOpenSSLContextFactory,
+ IOpenSSLClientConnectionCreator,
+ IOpenSSLServerConnectionCreator,
+]
+
+
+SingleArgFactory = Callable[["TLSMemoryBIOProtocol"], Connection]
+
+
+class LegacyContextFactoryWarning(Warning):
+ """
+ You should be using a newer TLS context configuration interface.
+ """
+
+
+def older(olderMethod: Callable[[], Context]) -> SingleArgFactory:
+ """
+ Compatibility shim for L{IOpenSSLContextFactory.getContext}-style method to
+ create(Client/Server)Creator.
+ """
+
+ def convert(p: TLSMemoryBIOProtocol) -> Connection:
+ context = olderMethod()
+ connection = Connection(context, None)
+ return connection
+
+ return convert
+
+
+def oldest(isClient: bool, creator: object) -> SingleArgFactory:
+ """
+ Comptibility shim that does largely the same thing as L{older} but for
+ things that don't even properly implement the old-style interface; check
+ explicitly for the method and try to provide a useful assert if the object
+ is just the wrong type rather than simply using an older API.
+ """
+ itype = "Client" if isClient else "Server"
+ warn(
+ f"{creator} does not explicitly provide any OpenSSL connection-"
+ f"creator {itype} interface; neither IOpenSSL{itype}ConnectionCreator,"
+ f" nor IOpenSSLContextFactory.",
+ LegacyContextFactoryWarning,
+ stacklevel=4,
+ )
+ getContext = getattr(creator, "getContext", None)
+ if getContext is None:
+ raise TypeError(f"{creator} does not even have a `getContext` method")
+ if not isinstance(getContext(), Context):
+ raise TypeError(f"{creator}'s `getContext` method doesn't return a `Context`")
+ return older(getContext)
+
+
+def _convertToAppropriateFactory(
+ isClient: bool, creator: SomeConnectionCreator
+) -> SingleArgFactory:
+ """
+ Upgrade a connection creator / context-factory-ish object into something
+ with a signature like the most recent interface for building OpenSSL
+ connection objects (i.e. like the methods on
+ L{IOpenSSLClientConnectionCreator} and L{IOpenSSLServerConnectionCreator}),
+ accounting for all the various interfaces older versions of Twisted used
+ for context configuration.
+ """
+ baseCallable = (
+ creator.clientConnectionForTLS
+ if (isClient and IOpenSSLClientConnectionCreator.providedBy(creator))
+ else (
+ creator.serverConnectionForTLS
+ if ((not isClient) and IOpenSSLServerConnectionCreator.providedBy(creator))
+ else (
+ older(creator.getContext)
+ if IOpenSSLContextFactory.providedBy(creator)
+ else oldest(isClient, creator)
+ )
+ )
+ )
+
+ def connectionFactory(protocol: TLSMemoryBIOProtocol) -> Connection:
+ connection = baseCallable(protocol)
+ if isClient:
+ connection.set_connect_state()
+ else:
+ connection.set_accept_state()
+ connection.set_app_data(protocol)
+ return connection
+
+ return connectionFactory
diff --git a/contrib/python/Twisted/py3/twisted/protocols/amp.py b/contrib/python/Twisted/py3/twisted/protocols/amp.py
index 8b80982d2ac..70a4ef6f4b0 100644
--- a/contrib/python/Twisted/py3/twisted/protocols/amp.py
+++ b/contrib/python/Twisted/py3/twisted/protocols/amp.py
@@ -24,17 +24,11 @@ implementation of Deferreds. AMP provides the following base-level features:
- Command dispatching (like HTTP Verbs): the protocol is extensible, and
multiple AMP sub-protocols can be grouped together easily.
-The protocol implementation also provides a few additional features which are
-not part of the core wire protocol, but are nevertheless very useful:
-
- - Tight TLS integration, with an included StartTLS command.
-
- - Handshaking to other protocols: because AMP has well-defined message
- boundaries and maintains all incoming and outgoing requests for you, you
- can start a connection over AMP and then switch to another protocol.
- This makes it ideal for firewall-traversal applications where you may
- have only one forwarded port but multiple applications that want to use
- it.
+You can also use AMP to tunnel other protocols: because AMP has well-defined
+message boundaries and maintains all incoming and outgoing requests for you,
+you can start a connection over AMP and then switch to another protocol. This
+makes it ideal for firewall-traversal applications where you may have only one
+forwarded port but multiple applications that want to use it.
Using AMP with Twisted is simple. Each message is a command, with a response.
You begin by defining a command type. Commands specify their input and output
@@ -65,8 +59,8 @@ a L{Deferred} which will fire with the result::
lambda p: p.callRemote(Sum, a=13, b=81)).addCallback(
lambda result: result['total'])
-Command responders may also return Deferreds, causing the response to be
-sent only once the Deferred fires::
+Command responders may also return Deferreds, causing the response to be sent
+only once the Deferred fires::
class DelayedSum(amp.AMP):
def slowSum(self, a, b):
@@ -202,18 +196,7 @@ from io import BytesIO
from itertools import count
from struct import pack
from types import MethodType
-from typing import (
- Any,
- Callable,
- ClassVar,
- Dict,
- List,
- Optional,
- Tuple,
- Type,
- TypeVar,
- Union,
-)
+from typing import Any, Callable, ClassVar, TypeVar
from zope.interface import Interface, implementer
@@ -622,7 +605,7 @@ class IncompatibleVersions(AmpError):
PROTOCOL_ERRORS = {UNHANDLED_ERROR_CODE: UnhandledCommand}
-class AmpBox(Dict[bytes, bytes]):
+class AmpBox(dict[bytes, bytes]):
"""
I am a packet in the AMP protocol, much like a
regular bytes:bytes dictionary.
@@ -630,7 +613,7 @@ class AmpBox(Dict[bytes, bytes]):
# be like a regular dictionary don't magically
# acquire a __dict__...
- __slots__: List[str] = []
+ __slots__: list[str] = []
def __init__(self, *args, **kw):
"""
@@ -726,7 +709,7 @@ class QuitBox(AmpBox):
I am an AmpBox that, upon being sent, terminates the connection.
"""
- __slots__: List[str] = []
+ __slots__: list[str] = []
def __repr__(self) -> str:
return f"QuitBox(**{super().__repr__()})"
@@ -1097,7 +1080,7 @@ class _CommandLocatorMeta(type):
metaclass.
"""
- _currentClassCommands: "list[tuple[type[Command], Callable[..., Any]]]" = []
+ _currentClassCommands: list[tuple[type[Command], Callable[..., Any]]] = []
def __new__(cls, name, bases, attrs):
commands = cls._currentClassCommands[:]
@@ -1710,12 +1693,12 @@ class _CommandMeta(type):
def __new__(
cls: type[_Self], name: str, bases: tuple[type], attrs: dict[str, object]
- ) -> Type[Command]:
+ ) -> type[Command]:
reverseErrors = attrs["reverseErrors"] = {}
er = attrs["allErrors"] = {}
if "commandName" not in attrs:
attrs["commandName"] = name.encode("ascii")
- newtype: Type[Command] = type.__new__(cls, name, bases, attrs) # type:ignore
+ newtype: type[Command] = type.__new__(cls, name, bases, attrs) # type:ignore
if not isinstance(newtype.commandName, bytes):
raise TypeError(
@@ -1730,8 +1713,8 @@ class _CommandMeta(type):
if not isinstance(bname, bytes):
raise TypeError(f"Response names must be byte strings, got: {bname!r}")
- errors: Dict[Type[Exception], bytes] = {}
- fatalErrors: Dict[Type[Exception], bytes] = {}
+ errors: dict[type[Exception], bytes] = {}
+ fatalErrors: dict[type[Exception], bytes] = {}
accumulateClassDict(newtype, "errors", errors)
accumulateClassDict(newtype, "fatalErrors", fatalErrors)
@@ -1803,14 +1786,14 @@ class Command(metaclass=_CommandMeta):
"""
commandName: ClassVar[bytes]
- arguments: ClassVar[List[Tuple[bytes, Argument]]] = []
- response: ClassVar[List[Tuple[bytes, Argument]]] = []
- extra: ClassVar[List[Any]] = []
- errors: ClassVar[Dict[Type[Exception], bytes]] = {}
- fatalErrors: ClassVar[Dict[Type[Exception], bytes]] = {}
+ arguments: ClassVar[list[tuple[bytes, Argument]]] = []
+ response: ClassVar[list[tuple[bytes, Argument]]] = []
+ extra: ClassVar[list[Any]] = []
+ errors: ClassVar[dict[type[Exception], bytes]] = {}
+ fatalErrors: ClassVar[dict[type[Exception], bytes]] = {}
- commandType: "ClassVar[Union[Type[Command], Type[Box]]]" = Box
- responseType: ClassVar[Type[AmpBox]] = Box
+ commandType: ClassVar[type[Command] | type[Box]] = Box
+ responseType: ClassVar[type[AmpBox]] = Box
requiresAnswer = True
@@ -2044,7 +2027,7 @@ class _TLSBox(AmpBox):
I am an AmpBox that, upon being sent, initiates a TLS connection.
"""
- __slots__: List[str] = []
+ __slots__: list[str] = []
def __init__(self):
if ssl is None:
@@ -2083,18 +2066,28 @@ class _LocalArgument(String):
class StartTLS(Command):
"""
- Use, or subclass, me to implement a command that starts TLS.
+ If your protocol requires a complex plaintext preamble to begin a secure
+ connection, and you are I{ABSOLUTELY SURE} that you understand the
+ consequences of sending that data insecurely, you can use, or subclass,
+ L{StartTLS} to define a command that switches from an unencrypted
+ connection to a TLS connection.
+
+ In general, you should prefer using TLS endpoints as defined by
+ L{twisted.internet.endpoints.wrapClientTLS} and
+ L{twisted.internet.endpoints.wrapServerTLS}, and using server hostname
+ indication and/or application layer protocol negotiation to negotiate the
+ parameters of the TLS connection.
Callers of StartTLS may pass several special arguments, which affect the
TLS negotiation:
- tls_localCertificate: This is a
- twisted.internet.ssl.PrivateCertificate which will be used to secure
- the side of the connection it is returned on.
+ twisted.internet.ssl.PrivateCertificate which will be used to secure
+ the side of the connection it is returned on.
- tls_verifyAuthorities: This is a list of
- twisted.internet.ssl.Certificate objects that will be used as the
- certificate authorities to verify our peer's certificate.
+ twisted.internet.ssl.Certificate objects that will be used as the
+ certificate authorities to verify our peer's certificate.
Each of those special parameters may also be present as a key in the
response dictionary.
@@ -2298,7 +2291,7 @@ class BinaryBoxProtocol(
hostCertificate = None
noPeerCertificate = False # for tests
- innerProtocol: Optional[Protocol] = None
+ innerProtocol: Protocol | None = None
innerProtocolClientFactory = None
def __init__(self, boxReceiver):
diff --git a/contrib/python/Twisted/py3/twisted/protocols/ftp.py b/contrib/python/Twisted/py3/twisted/protocols/ftp.py
index 8ac115c7fab..6fdde084b2e 100644
--- a/contrib/python/Twisted/py3/twisted/protocols/ftp.py
+++ b/contrib/python/Twisted/py3/twisted/protocols/ftp.py
@@ -794,7 +794,7 @@ class FTP(basic.LineReceiver, policies.TimeoutMixin):
passivePortRange = range(0, 1)
- listenFactory = reactor.listenTCP # type: ignore[attr-defined]
+ listenFactory = reactor.listenTCP
_encoding = "latin-1"
def reply(self, key, *args):
@@ -2885,7 +2885,7 @@ class FTPClient(FTPClientBasic):
@ivar passive: See description in __init__.
"""
- connectFactory = reactor.connectTCP # type: ignore[attr-defined]
+ connectFactory = reactor.connectTCP
def __init__(
self, username="anonymous", password="[email protected]", passive=1
diff --git a/contrib/python/Twisted/py3/twisted/protocols/haproxy/_exceptions.py b/contrib/python/Twisted/py3/twisted/protocols/haproxy/_exceptions.py
index 9a521ea2495..7d5e5508690 100644
--- a/contrib/python/Twisted/py3/twisted/protocols/haproxy/_exceptions.py
+++ b/contrib/python/Twisted/py3/twisted/protocols/haproxy/_exceptions.py
@@ -7,7 +7,8 @@ HAProxy specific exceptions.
"""
import contextlib
-from typing import Callable, Generator, Type
+from collections.abc import Generator
+from typing import Callable
class InvalidProxyHeader(Exception):
@@ -30,7 +31,7 @@ class MissingAddressData(InvalidProxyHeader):
@contextlib.contextmanager
def convertError(
- sourceType: Type[BaseException], targetType: Callable[[], BaseException]
+ sourceType: type[BaseException], targetType: Callable[[], BaseException]
) -> Generator[None, None, None]:
"""
Convert an error into a different error type.
diff --git a/contrib/python/Twisted/py3/twisted/protocols/haproxy/_interfaces.py b/contrib/python/Twisted/py3/twisted/protocols/haproxy/_interfaces.py
index 8fe90ea37ab..ab7dad9afc7 100644
--- a/contrib/python/Twisted/py3/twisted/protocols/haproxy/_interfaces.py
+++ b/contrib/python/Twisted/py3/twisted/protocols/haproxy/_interfaces.py
@@ -5,7 +5,7 @@
"""
Interfaces used by the PROXY protocol modules.
"""
-from typing import Tuple, Union
+from __future__ import annotations
import zope.interface
@@ -33,7 +33,7 @@ class IProxyParser(zope.interface.Interface):
Streaming parser that handles PROXY protocol headers.
"""
- def feed(data: bytes) -> Union[Tuple[IProxyInfo, bytes], Tuple[None, None]]:
+ def feed(data: bytes) -> tuple[IProxyInfo, bytes] | tuple[None, None]:
"""
Consume a chunk of data and attempt to parse it.
diff --git a/contrib/python/Twisted/py3/twisted/protocols/haproxy/_parser.py b/contrib/python/Twisted/py3/twisted/protocols/haproxy/_parser.py
index 834ccb73547..0cb32186e53 100644
--- a/contrib/python/Twisted/py3/twisted/protocols/haproxy/_parser.py
+++ b/contrib/python/Twisted/py3/twisted/protocols/haproxy/_parser.py
@@ -5,7 +5,7 @@
"""
Parser for 'haproxy:' string endpoint.
"""
-from typing import Mapping, Tuple
+from collections.abc import Mapping
from zope.interface import implementer
@@ -20,7 +20,7 @@ from twisted.plugin import IPlugin
from . import proxyEndpoint
-def unparseEndpoint(args: Tuple[object, ...], kwargs: Mapping[str, object]) -> str:
+def unparseEndpoint(args: tuple[object, ...], kwargs: Mapping[str, object]) -> str:
"""
Un-parse the already-parsed args and kwargs back into endpoint syntax.
diff --git a/contrib/python/Twisted/py3/twisted/protocols/haproxy/_v1parser.py b/contrib/python/Twisted/py3/twisted/protocols/haproxy/_v1parser.py
index fed987c33af..7401664fe09 100644
--- a/contrib/python/Twisted/py3/twisted/protocols/haproxy/_v1parser.py
+++ b/contrib/python/Twisted/py3/twisted/protocols/haproxy/_v1parser.py
@@ -6,7 +6,7 @@
"""
IProxyParser implementation for version one of the PROXY protocol.
"""
-from typing import Tuple, Union
+from __future__ import annotations
from zope.interface import implementer
@@ -44,9 +44,7 @@ class V1Parser:
def __init__(self) -> None:
self.buffer = b""
- def feed(
- self, data: bytes
- ) -> Union[Tuple[_info.ProxyInfo, bytes], Tuple[None, None]]:
+ def feed(self, data: bytes) -> tuple[_info.ProxyInfo, bytes] | tuple[None, None]:
"""
Consume a chunk of data and attempt to parse it.
diff --git a/contrib/python/Twisted/py3/twisted/protocols/haproxy/_v2parser.py b/contrib/python/Twisted/py3/twisted/protocols/haproxy/_v2parser.py
index cfcf7c99bcc..ac22a8a0003 100644
--- a/contrib/python/Twisted/py3/twisted/protocols/haproxy/_v2parser.py
+++ b/contrib/python/Twisted/py3/twisted/protocols/haproxy/_v2parser.py
@@ -6,15 +6,15 @@
"""
IProxyParser implementation for version two of the PROXY protocol.
"""
+from __future__ import annotations
import binascii
import struct
-from typing import Callable, Tuple, Type, Union
+from typing import Callable, Literal
from zope.interface import implementer
from constantly import ValueConstant, Values
-from typing_extensions import Literal
from twisted.internet import address
from twisted.python import compat
@@ -80,9 +80,7 @@ class V2Parser:
def __init__(self) -> None:
self.buffer = b""
- def feed(
- self, data: bytes
- ) -> Union[Tuple[_info.ProxyInfo, bytes], Tuple[None, None]]:
+ def feed(self, data: bytes) -> tuple[_info.ProxyInfo, bytes] | tuple[None, None]:
"""
Consume a chunk of data and attempt to parse it.
@@ -195,12 +193,12 @@ class V2Parser:
address.UNIXAddress(dest.rstrip(b"\x00")),
)
- addrType: Union[Literal["TCP"], Literal["UDP"]] = "TCP"
+ addrType: Literal["TCP"] | Literal["UDP"] = "TCP"
if netproto is NetProtocol.DGRAM:
addrType = "UDP"
- addrCls: Union[
- Type[address.IPv4Address], Type[address.IPv6Address]
- ] = address.IPv4Address
+ addrCls: (
+ type[address.IPv4Address] | type[address.IPv6Address]
+ ) = address.IPv4Address
addrParser: Callable[[bytes], bytes] = cls._bytesToIPv4
if family is NetFamily.INET6:
addrCls = address.IPv6Address
diff --git a/contrib/python/Twisted/py3/twisted/protocols/haproxy/_wrapper.py b/contrib/python/Twisted/py3/twisted/protocols/haproxy/_wrapper.py
index 935dbfa9e20..3f39a35ac3d 100644
--- a/contrib/python/Twisted/py3/twisted/protocols/haproxy/_wrapper.py
+++ b/contrib/python/Twisted/py3/twisted/protocols/haproxy/_wrapper.py
@@ -8,6 +8,8 @@ Protocol wrapper that provides HAProxy PROXY protocol support.
"""
from typing import Optional, Union
+from typing_extensions import Self
+
from twisted.internet import interfaces
from twisted.internet.endpoints import _WrapperServerEndpoint
from twisted.protocols import policies
@@ -27,7 +29,9 @@ class HAProxyProtocolWrapper(policies.ProtocolWrapper):
"""
def __init__(
- self, factory: policies.WrappingFactory, wrappedProtocol: interfaces.IProtocol
+ self,
+ factory: policies.WrappingFactory[Self],
+ wrappedProtocol: interfaces.IProtocol,
):
super().__init__(factory, wrappedProtocol)
self._proxyInfo: Optional[_info.ProxyInfo] = None
diff --git a/contrib/python/Twisted/py3/twisted/protocols/policies.py b/contrib/python/Twisted/py3/twisted/protocols/policies.py
index a89d4f8a8d1..a6abe372db8 100644
--- a/contrib/python/Twisted/py3/twisted/protocols/policies.py
+++ b/contrib/python/Twisted/py3/twisted/protocols/policies.py
@@ -7,18 +7,18 @@ Resource limiting policies.
@seealso: See also L{twisted.protocols.htb} for rate limiting.
"""
-
+from __future__ import annotations
# system imports
import sys
-from typing import Optional, Type
+from typing import Callable
from zope.interface import directlyProvides, providedBy
-from twisted.internet import error, interfaces
-from twisted.internet.interfaces import ILoggingContext
+from typing_extensions import Self, TypeVar
-# twisted imports
+from twisted.internet import error, interfaces
+from twisted.internet.interfaces import IAddress, ILoggingContext
from twisted.internet.protocol import ClientFactory, Protocol, ServerFactory
from twisted.python import log
@@ -51,7 +51,7 @@ class ProtocolWrapper(Protocol):
disconnecting = 0
def __init__(
- self, factory: "WrappingFactory", wrappedProtocol: interfaces.IProtocol
+ self, factory: WrappingFactory[Self], wrappedProtocol: interfaces.IProtocol
):
self.wrappedProtocol = wrappedProtocol
self.factory = factory
@@ -117,12 +117,15 @@ class ProtocolWrapper(Protocol):
self.wrappedProtocol = None
-class WrappingFactory(ClientFactory):
+WP = TypeVar("WP", bound=ProtocolWrapper, default=ProtocolWrapper)
+
+
+class WrappingFactory(ClientFactory[WP]):
"""
Wraps a factory and its protocols, and keeps track of them.
"""
- protocol: Type[Protocol] = ProtocolWrapper
+ protocol: Callable[..., WP] = ProtocolWrapper # type:ignore[assignment]
def __init__(self, wrappedFactory):
self.wrappedFactory = wrappedFactory
@@ -151,7 +154,7 @@ class WrappingFactory(ClientFactory):
def clientConnectionLost(self, connector, reason):
self.wrappedFactory.clientConnectionLost(connector, reason)
- def buildProtocol(self, addr):
+ def buildProtocol(self, addr: IAddress | None) -> WP:
return self.protocol(self, self.wrappedFactory.buildProtocol(addr))
def registerProtocol(self, p):
@@ -395,7 +398,7 @@ class LimitTotalConnectionsFactory(ServerFactory):
connectionCount = 0
connectionLimit = None
- overflowProtocol: Optional[Type[Protocol]] = None
+ overflowProtocol: type[Protocol] | None = None
def buildProtocol(self, addr):
if self.connectionLimit is None or self.connectionCount < self.connectionLimit:
@@ -628,7 +631,7 @@ class TimeoutMixin:
@cvar timeOut: The number of seconds after which to timeout the connection.
"""
- timeOut: Optional[int] = None
+ timeOut: int | None = None
__timeoutCall = None
diff --git a/contrib/python/Twisted/py3/twisted/protocols/portforward.py b/contrib/python/Twisted/py3/twisted/protocols/portforward.py
index bf3a894dfaa..bd420c2a952 100644
--- a/contrib/python/Twisted/py3/twisted/protocols/portforward.py
+++ b/contrib/python/Twisted/py3/twisted/protocols/portforward.py
@@ -1,57 +1,66 @@
+# -*- test-case-name: twisted.test.test_protocols.PortforwardingTests -*-
# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.
"""
A simple port forwarder.
"""
+from __future__ import annotations
-# Twisted imports
from twisted.internet import protocol
-from twisted.python import log
+from twisted.internet.interfaces import (
+ IAddress,
+ IConsumer,
+ IPushProducer,
+ IStreamServerEndpoint,
+ ITransport,
+)
class Proxy(protocol.Protocol):
noisy = True
+ peer: Proxy | None = None
+ factory: protocol.Factory[Proxy]
- peer = None
-
- def setPeer(self, peer):
+ def setPeer(self, peer: Proxy | None) -> None:
self.peer = peer
def connectionLost(self, reason):
if self.peer is not None:
self.peer.transport.loseConnection()
self.peer = None
- elif self.noisy:
- log.msg(f"Unable to connect to peer: {reason}")
def dataReceived(self, data):
self.peer.transport.write(data)
class ProxyClient(Proxy):
- def connectionMade(self):
+ factory: protocol.Factory[ProxyClient] # type:ignore
+
+ def connectionMade(self) -> None:
+ assert self.peer is not None
self.peer.setPeer(self)
# Wire this and the peer transport together to enable
# flow control (this stops connections from filling
# this proxy memory when one side produces data at a
# higher rate than the other can consume).
- self.transport.registerProducer(self.peer.transport, True)
- self.peer.transport.registerProducer(self.transport, True)
+ self.transport.registerProducer(self.peer.transport, True) # type:ignore
+ self.peer.transport.registerProducer(self.transport, True) # type:ignore
# We're connected, everybody can read to their hearts content.
- self.peer.transport.resumeProducing()
+ self.peer.transport.resumeProducing() # type:ignore
-class ProxyClientFactory(protocol.ClientFactory):
+class ProxyClientFactory(protocol.ClientFactory[ProxyClient]):
protocol = ProxyClient
def setServer(self, server):
self.server = server
- def buildProtocol(self, *args, **kw):
- prot = protocol.ClientFactory.buildProtocol(self, *args, **kw)
+ def buildProtocol(self, addr: IAddress | None) -> ProxyClient:
+ prot = super().buildProtocol(addr)
+ assert prot is not None, "peer must build protocol"
prot.setPeer(self.server)
return prot
@@ -59,11 +68,21 @@ class ProxyClientFactory(protocol.ClientFactory):
self.server.transport.loseConnection()
+class _MakeTypesHappy(IPushProducer, IConsumer, ITransport):
+ """
+ L{ProxyServer}'s transport is implicitly assumed to provide several
+ interfaces so include them all here.
+ """
+
+
class ProxyServer(Proxy):
clientProtocolFactory = ProxyClientFactory
reactor = None
+ transport: _MakeTypesHappy
+ factory: ProxyFactory # type:ignore[assignment]
+ endpoint: IStreamServerEndpoint | None
- def connectionMade(self):
+ def connectionMade(self) -> None:
# Don't read anything from the connecting client until we have
# somewhere to send it to.
self.transport.pauseProducing()
@@ -78,13 +97,13 @@ class ProxyServer(Proxy):
self.reactor.connectTCP(self.factory.host, self.factory.port, client)
-class ProxyFactory(protocol.Factory):
+class ProxyFactory(protocol.Factory[ProxyServer]):
"""
Factory for port forwarder.
"""
protocol = ProxyServer
- def __init__(self, host, port):
+ def __init__(self, host: str, port: int) -> None:
self.host = host
self.port = port
diff --git a/contrib/python/Twisted/py3/twisted/protocols/postfix.py b/contrib/python/Twisted/py3/twisted/protocols/postfix.py
index 5860887cb00..cddc2069545 100644
--- a/contrib/python/Twisted/py3/twisted/protocols/postfix.py
+++ b/contrib/python/Twisted/py3/twisted/protocols/postfix.py
@@ -9,7 +9,7 @@ from __future__ import annotations
import sys
from collections import UserDict
-from typing import TYPE_CHECKING, Union
+from typing import Union
from urllib.parse import quote as _quote, unquote as _unquote
from twisted.internet import defer, protocol
@@ -107,10 +107,7 @@ class PostfixTCPMapServer(basic.LineReceiver, policies.TimeoutMixin):
self.sendCode(500, b"put is not implemented yet.")
-if TYPE_CHECKING or sys.version_info >= (3, 9):
- _PostfixTCPMapDict = UserDict[bytes, Union[str, bytes]]
-else:
- _PostfixTCPMapDict = UserDict
+_PostfixTCPMapDict = UserDict[bytes, Union[str, bytes]]
class PostfixTCPMapDictServerFactory(_PostfixTCPMapDict, protocol.ServerFactory):
diff --git a/contrib/python/Twisted/py3/twisted/protocols/sip.py b/contrib/python/Twisted/py3/twisted/protocols/sip.py
index 51cac143d86..c41d32654ca 100644
--- a/contrib/python/Twisted/py3/twisted/protocols/sip.py
+++ b/contrib/python/Twisted/py3/twisted/protocols/sip.py
@@ -13,7 +13,6 @@ import socket
import time
import warnings
from collections import OrderedDict
-from typing import Dict, List
from zope.interface import Interface, implementer
@@ -340,7 +339,7 @@ class URL:
self.headers = headers
def toString(self) -> str:
- l: List[str] = []
+ l: list[str] = []
w = l.append
w("sip:")
if self.username != None:
@@ -1057,7 +1056,7 @@ class RegisterProxy(Proxy):
registry = None # Should implement IRegistry
- authorizers: Dict[str, IAuthorizer] = {}
+ authorizers: dict[str, IAuthorizer] = {}
def __init__(self, *args, **kw):
Proxy.__init__(self, *args, **kw)
diff --git a/contrib/python/Twisted/py3/twisted/protocols/tls.py b/contrib/python/Twisted/py3/twisted/protocols/tls.py
index be7a58ce4d3..154d1c13d49 100644
--- a/contrib/python/Twisted/py3/twisted/protocols/tls.py
+++ b/contrib/python/Twisted/py3/twisted/protocols/tls.py
@@ -38,23 +38,21 @@ transports, such as UNIX sockets and stdio.
from __future__ import annotations
-from typing import Callable, Iterable, Optional, cast
+from collections.abc import Iterable
+from typing import Callable, cast
from zope.interface import directlyProvides, implementer, providedBy
-from OpenSSL.SSL import Connection, Error, SysCallError, WantReadError, ZeroReturnError
+from OpenSSL.SSL import Error, SysCallError, WantReadError, ZeroReturnError
from twisted.internet._producer_helpers import _PullToPush
-from twisted.internet._sslverify import _setAcceptableProtocols
from twisted.internet.interfaces import (
IDelayedCall,
IHandshakeListener,
ILoggingContext,
INegotiated,
- IOpenSSLClientConnectionCreator,
- IOpenSSLServerConnectionCreator,
IProtocol,
- IProtocolNegotiationFactory,
+ IProtocolFactory,
IPushProducer,
IReactorTime,
ISystemHandle,
@@ -64,6 +62,7 @@ from twisted.internet.main import CONNECTION_LOST
from twisted.internet.protocol import Protocol
from twisted.protocols.policies import ProtocolWrapper, WrappingFactory
from twisted.python.failure import Failure
+from ._tls_legacy import SomeConnectionCreator, _convertToAppropriateFactory
@implementer(IPushProducer)
@@ -119,6 +118,9 @@ def _representsEOF(exceptionObject: Error) -> bool:
_, reasonString = exceptionObject.args
else:
errorQueue = exceptionObject.args[0]
+ if not errorQueue:
+ # This appears to be the behavior in OpenSSL 4.
+ return True
_, _, reasonString = errorQueue[-1]
return reasonString.casefold().startswith("unexpected eof")
@@ -188,6 +190,15 @@ class TLSMemoryBIOProtocol(ProtocolWrapper):
_producer = None
_aborted = False
+ # we are a ProtocolWrapper and thus ->Protocol. in order LSP substitute to
+ # Protocol, self.factory must be (invariant: the attribute is read/write!)
+ # Factory[Self] | None. Thus, constraining it to TLSMemoryBIOFactory like
+ # this is invalid; a client of Protocol looking at an instance of
+ # TLSMemoryBIOProtocol might want to assign some arbitrary .factory and
+ # that would be wrong.
+
+ factory: TLSMemoryBIOFactory
+
def __init__(self, factory, wrappedProtocol, _connectWrapped=True):
ProtocolWrapper.__init__(self, factory, wrappedProtocol)
self._connectWrapped = _connectWrapped
@@ -209,7 +220,7 @@ class TLSMemoryBIOProtocol(ProtocolWrapper):
Connect this wrapper to the given transport and initialize the
necessary L{OpenSSL.SSL.Connection} with a memory BIO.
"""
- self._tlsConnection = self.factory._createConnection(self)
+ self._tlsConnection = self.factory._creatorCallable(self)
self._appSendBuffer = []
# Add interfaces provided by the transport we are wrapping:
@@ -554,31 +565,19 @@ class TLSMemoryBIOProtocol(ProtocolWrapper):
return self._tlsConnection.get_peer_certificate()
@property
- def negotiatedProtocol(self):
+ def negotiatedProtocol(self) -> bytes | None:
"""
@see: L{INegotiated.negotiatedProtocol}
"""
- protocolName = None
-
- try:
- # If ALPN is not implemented that's ok, NPN might be.
- protocolName = self._tlsConnection.get_alpn_proto_negotiated()
- except (NotImplementedError, AttributeError):
- pass
-
- if protocolName not in (b"", None):
- # A protocol was selected using ALPN.
- return protocolName
-
- try:
- protocolName = self._tlsConnection.get_next_proto_negotiated()
- except (NotImplementedError, AttributeError):
- pass
-
- if protocolName != b"":
- return protocolName
-
- return None
+ tc = self._tlsConnection
+ if tc is None:
+ # We have not yet established a TLS connection, so no application
+ # layer protocol has been negotiated.
+ return None
+ protocolName: bytes | None = tc.get_alpn_proto_negotiated()
+ if protocolName == b"":
+ return None
+ return protocolName
def registerProducer(self, producer, streaming):
# If we've already disconnected, nothing to do here:
@@ -615,81 +614,6 @@ class TLSMemoryBIOProtocol(ProtocolWrapper):
self._shutdownTLS()
-@implementer(IOpenSSLClientConnectionCreator, IOpenSSLServerConnectionCreator)
-class _ContextFactoryToConnectionFactory:
- """
- Adapter wrapping a L{twisted.internet.interfaces.IOpenSSLContextFactory}
- into a L{IOpenSSLClientConnectionCreator} or
- L{IOpenSSLServerConnectionCreator}.
-
- See U{https://twistedmatrix.com/trac/ticket/7215} for work that should make
- this unnecessary.
- """
-
- def __init__(self, oldStyleContextFactory):
- """
- Construct a L{_ContextFactoryToConnectionFactory} with a
- L{twisted.internet.interfaces.IOpenSSLContextFactory}.
-
- Immediately call C{getContext} on C{oldStyleContextFactory} in order to
- force advance parameter checking, since old-style context factories
- don't actually check that their arguments to L{OpenSSL} are correct.
-
- @param oldStyleContextFactory: A factory that can produce contexts.
- @type oldStyleContextFactory:
- L{twisted.internet.interfaces.IOpenSSLContextFactory}
- """
- oldStyleContextFactory.getContext()
- self._oldStyleContextFactory = oldStyleContextFactory
-
- def _connectionForTLS(self, protocol):
- """
- Create an L{OpenSSL.SSL.Connection} object.
-
- @param protocol: The protocol initiating a TLS connection.
- @type protocol: L{TLSMemoryBIOProtocol}
-
- @return: a connection
- @rtype: L{OpenSSL.SSL.Connection}
- """
- context = self._oldStyleContextFactory.getContext()
- return Connection(context, None)
-
- def serverConnectionForTLS(self, protocol):
- """
- Construct an OpenSSL server connection from the wrapped old-style
- context factory.
-
- @note: Since old-style context factories don't distinguish between
- clients and servers, this is exactly the same as
- L{_ContextFactoryToConnectionFactory.clientConnectionForTLS}.
-
- @param protocol: The protocol initiating a TLS connection.
- @type protocol: L{TLSMemoryBIOProtocol}
-
- @return: a connection
- @rtype: L{OpenSSL.SSL.Connection}
- """
- return self._connectionForTLS(protocol)
-
- def clientConnectionForTLS(self, protocol):
- """
- Construct an OpenSSL server connection from the wrapped old-style
- context factory.
-
- @note: Since old-style context factories don't distinguish between
- clients and servers, this is exactly the same as
- L{_ContextFactoryToConnectionFactory.serverConnectionForTLS}.
-
- @param protocol: The protocol initiating a TLS connection.
- @type protocol: L{TLSMemoryBIOProtocol}
-
- @return: a connection
- @rtype: L{OpenSSL.SSL.Connection}
- """
- return self._connectionForTLS(protocol)
-
-
class _AggregateSmallWrites:
"""
Aggregate small writes so they get written in large batches.
@@ -709,7 +633,7 @@ class _AggregateSmallWrites:
self._clock = clock
self._buffer: list[bytes] = []
self._bufferLeft = self.MAX_BUFFER_SIZE
- self._scheduled: Optional[IDelayedCall] = None
+ self._scheduled: IDelayedCall | None = None
def write(self, data: bytes) -> None:
"""
@@ -801,31 +725,25 @@ class BufferingTLSTransport(TLSMemoryBIOProtocol):
super().loseConnection()
-class TLSMemoryBIOFactory(WrappingFactory):
+class TLSMemoryBIOFactory(WrappingFactory[TLSMemoryBIOProtocol]):
"""
L{TLSMemoryBIOFactory} adds TLS to connections.
- @ivar _creatorInterface: the interface which L{_connectionCreator} is
- expected to implement.
- @type _creatorInterface: L{zope.interface.interfaces.IInterface}
-
- @ivar _connectionCreator: a callable which creates an OpenSSL Connection
- object.
- @type _connectionCreator: 1-argument callable taking
- L{TLSMemoryBIOProtocol} and returning L{OpenSSL.SSL.Connection}.
+ @ivar _creatorCallable: A callable for creating an
+ L{OpenSSL.SSL.Connection} from a L{TLSMemoryBIOProtocol}.
"""
- protocol = BufferingTLSTransport
+ protocol: type[TLSMemoryBIOProtocol] = BufferingTLSTransport
noisy = False # disable unnecessary logging.
def __init__(
self,
- contextFactory,
- isClient,
- wrappedFactory,
- clock=None,
- ):
+ contextFactory: SomeConnectionCreator,
+ isClient: bool,
+ wrappedFactory: IProtocolFactory,
+ clock: IReactorTime | None = None,
+ ) -> None:
"""
Create a L{TLSMemoryBIOFactory}.
@@ -868,15 +786,9 @@ class TLSMemoryBIOFactory(WrappingFactory):
application-level protocol.
@type wrappedFactory: L{twisted.internet.interfaces.IProtocolFactory}
"""
- WrappingFactory.__init__(self, wrappedFactory)
- if isClient:
- creatorInterface = IOpenSSLClientConnectionCreator
- else:
- creatorInterface = IOpenSSLServerConnectionCreator
- self._creatorInterface = creatorInterface
- if not creatorInterface.providedBy(contextFactory):
- contextFactory = _ContextFactoryToConnectionFactory(contextFactory)
- self._connectionCreator = contextFactory
+ super().__init__(wrappedFactory)
+
+ self._creatorCallable = _convertToAppropriateFactory(isClient, contextFactory)
if clock is None:
clock = _get_default_clock()
@@ -894,43 +806,3 @@ class TLSMemoryBIOFactory(WrappingFactory):
else:
logPrefix = self.wrappedFactory.__class__.__name__
return f"{logPrefix} (TLS)"
-
- def _applyProtocolNegotiation(self, connection):
- """
- Applies ALPN/NPN protocol neogitation to the connection, if the factory
- supports it.
-
- @param connection: The OpenSSL connection object to have ALPN/NPN added
- to it.
- @type connection: L{OpenSSL.SSL.Connection}
-
- @return: Nothing
- @rtype: L{None}
- """
- if IProtocolNegotiationFactory.providedBy(self.wrappedFactory):
- protocols = self.wrappedFactory.acceptableProtocols()
- context = connection.get_context()
- _setAcceptableProtocols(context, protocols)
-
- return
-
- def _createConnection(self, tlsProtocol):
- """
- Create an OpenSSL connection and set it up good.
-
- @param tlsProtocol: The protocol which is establishing the connection.
- @type tlsProtocol: L{TLSMemoryBIOProtocol}
-
- @return: an OpenSSL connection object for C{tlsProtocol} to use
- @rtype: L{OpenSSL.SSL.Connection}
- """
- connectionCreator = self._connectionCreator
- if self._creatorInterface is IOpenSSLClientConnectionCreator:
- connection = connectionCreator.clientConnectionForTLS(tlsProtocol)
- self._applyProtocolNegotiation(connection)
- connection.set_connect_state()
- else:
- connection = connectionCreator.serverConnectionForTLS(tlsProtocol)
- self._applyProtocolNegotiation(connection)
- connection.set_accept_state()
- return connection
diff --git a/contrib/python/Twisted/py3/twisted/python/_release.py b/contrib/python/Twisted/py3/twisted/python/_release.py
index 35220c31953..6c72f8ae41b 100644
--- a/contrib/python/Twisted/py3/twisted/python/_release.py
+++ b/contrib/python/Twisted/py3/twisted/python/_release.py
@@ -14,7 +14,6 @@ which must run on multiple platforms (eg the setup.py script).
import os
from subprocess import STDOUT, CalledProcessError, check_output
-from typing import Dict
from zope.interface import Interface, implementer
@@ -198,7 +197,7 @@ class Project:
@return: A L{incremental.Version} specifying the version number of the
project based on live python modules.
"""
- namespace: Dict[str, object] = {}
+ namespace: dict[str, object] = {}
directory = self.directory
while not namespace:
if directory.path == "/":
diff --git a/contrib/python/Twisted/py3/twisted/python/_shellcomp.py b/contrib/python/Twisted/py3/twisted/python/_shellcomp.py
index 9c9a46a8d42..bc72951e506 100644
--- a/contrib/python/Twisted/py3/twisted/python/_shellcomp.py
+++ b/contrib/python/Twisted/py3/twisted/python/_shellcomp.py
@@ -30,7 +30,6 @@ import getopt
import inspect
import itertools
from types import MethodType
-from typing import Dict, List, Set
from twisted.python import reflect, usage, util
from twisted.python.compat import ioType
@@ -211,7 +210,7 @@ class ZshSubcommandBuilder(ZshBuilder):
self.subOptions = subOptions
ZshBuilder.__init__(self, *args)
- def write(self):
+ def write(self, genSubs=True):
"""
Generate the completion function and write it to the output file
@return: L{None}
@@ -305,8 +304,8 @@ class ZshArgumentsGenerator:
aCL = reflect.accumulateClassList
- optFlags: List[List[object]] = []
- optParams: List[List[object]] = []
+ optFlags: list[list[object]] = []
+ optParams: list[list[object]] = []
aCL(options.__class__, "optFlags", optFlags)
aCL(options.__class__, "optParameters", optParams)
@@ -468,7 +467,7 @@ class ZshArgumentsGenerator:
strings.sort() # need deterministic order for reliable unit-tests
return "(%s)" % " ".join(strings)
- def makeExcludesDict(self) -> Dict[str, Set[str]]:
+ def makeExcludesDict(self) -> dict[str, set[str]]:
"""
@return: A C{dict} that maps each option name appearing in
self.mutuallyExclusive to a set of those option names that is it
@@ -481,7 +480,7 @@ class ZshArgumentsGenerator:
if optList[1] != None:
longToShort[optList[0]] = optList[1]
- excludes: Dict[str, Set[str]] = {}
+ excludes: dict[str, set[str]] = {}
for lst in self.mutuallyExclusive:
for i, longname in enumerate(lst):
tmp = set(lst[:i] + lst[i + 1 :])
@@ -624,7 +623,7 @@ class ZshArgumentsGenerator:
These will be defined by 'opt_foo' methods of the Options subclass
@return: L{None}
"""
- methodsDict: Dict[str, MethodType] = {}
+ methodsDict: dict[str, MethodType] = {}
reflect.accumulateMethods(self.options, methodsDict, "opt_")
methodToShort = {}
for name in methodsDict.copy():
diff --git a/contrib/python/Twisted/py3/twisted/python/_textattributes.py b/contrib/python/Twisted/py3/twisted/python/_textattributes.py
index 36403094ed4..58ecaabb4b9 100644
--- a/contrib/python/Twisted/py3/twisted/python/_textattributes.py
+++ b/contrib/python/Twisted/py3/twisted/python/_textattributes.py
@@ -21,7 +21,8 @@ Serializing a formatting structure is done with L{flatten}.
"""
-from typing import ClassVar, List, Sequence
+from collections.abc import Sequence
+from typing import ClassVar
from twisted.python.util import FancyEqMixin
@@ -85,7 +86,7 @@ class _NormalAttr(_Attribute):
A text attribute for normal text.
"""
- def serialize(self, write, attrs, attributeRenderer):
+ def serialize(self, write, attrs=None, attributeRenderer="toVT102"):
attrs.__init__()
_Attribute.serialize(self, write, attrs, attributeRenderer)
@@ -115,7 +116,7 @@ class _OtherAttr(_Attribute):
result.children.extend(self.children)
return result
- def serialize(self, write, attrs, attributeRenderer):
+ def serialize(self, write, attrs=None, attributeRenderer="toVT102"):
attrs = attrs._withAttribute(self.attrname, self.attrvalue)
_Attribute.serialize(self, write, attrs, attributeRenderer)
@@ -136,7 +137,7 @@ class _ColorAttr(_Attribute):
self.color = color
self.ground = ground
- def serialize(self, write, attrs, attributeRenderer):
+ def serialize(self, write, attrs=None, attributeRenderer="toVT102"):
attrs = attrs._withAttribute(self.ground, self.color)
_Attribute.serialize(self, write, attrs, attributeRenderer)
@@ -296,7 +297,7 @@ def flatten(output, attrs, attributeRenderer="toVT102"):
@return: A string expressing the text and display attributes specified by
L{output}.
"""
- flattened: List[str] = []
+ flattened: list[str] = []
output.serialize(flattened.append, attrs, attributeRenderer)
return "".join(flattened)
diff --git a/contrib/python/Twisted/py3/twisted/python/_tzhelper.py b/contrib/python/Twisted/py3/twisted/python/_tzhelper.py
index 2b841889d75..1b83b53bd9e 100644
--- a/contrib/python/Twisted/py3/twisted/python/_tzhelper.py
+++ b/contrib/python/Twisted/py3/twisted/python/_tzhelper.py
@@ -6,13 +6,14 @@
Time zone utilities.
"""
+from __future__ import annotations
+
from datetime import (
datetime as DateTime,
timedelta as TimeDelta,
timezone,
tzinfo as TZInfo,
)
-from typing import Optional
__all__ = [
"FixedOffsetTimeZone",
@@ -31,7 +32,7 @@ class FixedOffsetTimeZone(TZInfo):
offset.
"""
- def __init__(self, offset: TimeDelta, name: Optional[str] = None) -> None:
+ def __init__(self, offset: TimeDelta, name: str | None = None) -> None:
"""
Construct a L{FixedOffsetTimeZone} with a fixed offset.
@@ -44,7 +45,7 @@ class FixedOffsetTimeZone(TZInfo):
@classmethod
def fromSignHoursMinutes(
cls, sign: str, hours: int, minutes: int
- ) -> "FixedOffsetTimeZone":
+ ) -> FixedOffsetTimeZone:
"""
Construct a L{FixedOffsetTimeZone} from an offset described by sign
('+' or '-'), hours, and minutes.
@@ -68,7 +69,7 @@ class FixedOffsetTimeZone(TZInfo):
return cls(TimeDelta(hours=hours, minutes=minutes), name)
@classmethod
- def fromLocalTimeStamp(cls, timeStamp: float) -> "FixedOffsetTimeZone":
+ def fromLocalTimeStamp(cls, timeStamp: float) -> FixedOffsetTimeZone:
"""
Create a time zone with a fixed offset corresponding to a time stamp in
the system's locally configured time zone.
@@ -78,20 +79,20 @@ class FixedOffsetTimeZone(TZInfo):
).replace(tzinfo=None)
return cls(offset)
- def utcoffset(self, dt: Optional[DateTime]) -> TimeDelta:
+ def utcoffset(self, dt: DateTime | None) -> TimeDelta:
"""
Return the given timezone's offset from UTC.
"""
return self.offset
- def dst(self, dt: Optional[DateTime]) -> TimeDelta:
+ def dst(self, dt: DateTime | None) -> TimeDelta:
"""
Return a zero L{TimeDelta} for the daylight saving time
offset, since there is never one.
"""
return TimeDelta(0)
- def tzname(self, dt: Optional[DateTime]) -> str:
+ def tzname(self, dt: DateTime | None) -> str:
"""
Return a string describing this timezone.
"""
diff --git a/contrib/python/Twisted/py3/twisted/python/components.py b/contrib/python/Twisted/py3/twisted/python/components.py
index b6a6015dcc6..7ec71309de8 100644
--- a/contrib/python/Twisted/py3/twisted/python/components.py
+++ b/contrib/python/Twisted/py3/twisted/python/components.py
@@ -31,7 +31,6 @@ interface.
from io import StringIO
-from typing import Dict
# zope3 imports
from zope.interface import declarations, interface
@@ -332,7 +331,7 @@ def proxyForInterface(iface, originalAttribute="original"):
def __init__(self, original):
setattr(self, originalAttribute, original)
- contents: Dict[str, object] = {"__init__": __init__}
+ contents: dict[str, object] = {"__init__": __init__}
for name in iface:
contents[name] = _ProxyDescriptor(name, originalAttribute)
proxy = type(f"(Proxy for {reflect.qual(iface)})", (object,), contents)
diff --git a/contrib/python/Twisted/py3/twisted/python/context.py b/contrib/python/Twisted/py3/twisted/python/context.py
index f2d3bd82e9a..3afdf4c95a2 100644
--- a/contrib/python/Twisted/py3/twisted/python/context.py
+++ b/contrib/python/Twisted/py3/twisted/python/context.py
@@ -14,9 +14,8 @@ This is thread-safe.
from threading import local
-from typing import Dict, Type
-defaultContextDict: Dict[Type[object], Dict[str, str]] = {}
+defaultContextDict: dict[type[object], dict[str, str]] = {}
setDefault = defaultContextDict.__setitem__
diff --git a/contrib/python/Twisted/py3/twisted/python/deprecate.py b/contrib/python/Twisted/py3/twisted/python/deprecate.py
index 22b0d162aa9..83e119e27f6 100644
--- a/contrib/python/Twisted/py3/twisted/python/deprecate.py
+++ b/contrib/python/Twisted/py3/twisted/python/deprecate.py
@@ -94,10 +94,11 @@ __all__ = [
import inspect
import sys
+from collections.abc import Sequence
from dis import findlinestarts
from functools import wraps
from types import ModuleType
-from typing import Any, Callable, Dict, Optional, TypeVar, cast
+from typing import Any, Callable, TypeVar, cast
from warnings import warn, warn_explicit
from incremental import Version, getVersionString
@@ -628,75 +629,32 @@ def warnAboutFunction(offender, warningString):
)
-def _passedArgSpec(argspec, positional, keyword):
- """
- Take an I{inspect.ArgSpec}, a tuple of positional arguments, and a dict of
- keyword arguments, and return a mapping of arguments that were actually
- passed to their passed values.
-
- @param argspec: The argument specification for the function to inspect.
- @type argspec: I{inspect.ArgSpec}
-
- @param positional: The positional arguments that were passed.
- @type positional: L{tuple}
-
- @param keyword: The keyword arguments that were passed.
- @type keyword: L{dict}
-
- @return: A dictionary mapping argument names (those declared in C{argspec})
- to values that were passed explicitly by the user.
- @rtype: L{dict} mapping L{str} to L{object}
- """
- result: Dict[str, object] = {}
- unpassed = len(argspec.args) - len(positional)
- if argspec.keywords is not None:
- kwargs = result[argspec.keywords] = {}
- if unpassed < 0:
- if argspec.varargs is None:
- raise TypeError("Too many arguments.")
- else:
- result[argspec.varargs] = positional[len(argspec.args) :]
- for name, value in zip(argspec.args, positional):
- result[name] = value
- for name, value in keyword.items():
- if name in argspec.args:
- if name in result:
- raise TypeError("Already passed.")
- result[name] = value
- elif argspec.keywords is not None:
- kwargs[name] = value
- else:
- raise TypeError("no such param")
- return result
-
-
-def _passedSignature(signature, positional, keyword):
+def _passedSignature(
+ signature: inspect.Signature,
+ positional: tuple[object, ...],
+ keyword: dict[str, object],
+) -> dict[str, object]:
"""
Take an L{inspect.Signature}, a tuple of positional arguments, and a dict of
keyword arguments, and return a mapping of arguments that were actually
passed to their passed values.
@param signature: The signature of the function to inspect.
- @type signature: L{inspect.Signature}
-
@param positional: The positional arguments that were passed.
- @type positional: L{tuple}
-
@param keyword: The keyword arguments that were passed.
- @type keyword: L{dict}
@return: A dictionary mapping argument names (those declared in
C{signature}) to values that were passed explicitly by the user.
- @rtype: L{dict} mapping L{str} to L{object}
"""
- result = {}
- kwargs = None
- numPositional = 0
+ result: dict[str, object] = {}
+ kwargs: dict[str, object] | None = None
+ numPositional: int = 0
for n, (name, param) in enumerate(signature.parameters.items()):
if param.kind == inspect.Parameter.VAR_POSITIONAL:
# Varargs, for example: *args
- result[name] = positional[n:]
- numPositional = len(result[name]) + 1
+ varargs: tuple[object, ...] = positional[n:]
+ result[name] = varargs
+ numPositional = len(varargs) + 1
elif param.kind == inspect.Parameter.VAR_KEYWORD:
# Variable keyword args, for example: **my_kwargs
kwargs = result[name] = {}
@@ -730,30 +688,29 @@ def _passedSignature(signature, positional, keyword):
return result
-def _mutuallyExclusiveArguments(argumentPairs):
+def _mutuallyExclusiveArguments(
+ argumentPairs: Sequence[tuple[str, str]]
+) -> Callable[[Callable[_P, _R]], Callable[_P, _R]]:
"""
Decorator which causes its decoratee to raise a L{TypeError} if two of the
given arguments are passed at the same time.
@param argumentPairs: pairs of argument identifiers, each pair indicating
an argument that may not be passed in conjunction with another.
- @type argumentPairs: sequence of 2-sequences of L{str}
@return: A decorator, used like so::
@_mutuallyExclusiveArguments([["tweedledum", "tweedledee"]])
def function(tweedledum=1, tweedledee=2):
"Don't pass tweedledum and tweedledee at the same time."
-
- @rtype: 1-argument callable taking a callable and returning a callable.
"""
- def wrapper(wrappee):
+ def wrapper(wrappee: Callable[_P, _R]) -> Callable[_P, _R]:
spec = inspect.signature(wrappee)
_passed = _passedSignature
@wraps(wrappee)
- def wrapped(*args, **kwargs):
+ def wrapped(*args: _P.args, **kwargs: _P.kwargs) -> _R:
arguments = _passed(spec, args, kwargs)
for this, that in argumentPairs:
if this in arguments and that in arguments:
@@ -772,7 +729,7 @@ _Tc = TypeVar("_Tc", bound=Callable[..., Any])
def deprecatedKeywordParameter(
- version: Version, name: str, replacement: Optional[str] = None
+ version: Version, name: str, replacement: str | None = None
) -> Callable[[_Tc], _Tc]:
"""
Return a decorator that marks a keyword parameter of a callable
diff --git a/contrib/python/Twisted/py3/twisted/python/fakepwd.py b/contrib/python/Twisted/py3/twisted/python/fakepwd.py
index 39c11008214..c040b37f98e 100644
--- a/contrib/python/Twisted/py3/twisted/python/fakepwd.py
+++ b/contrib/python/Twisted/py3/twisted/python/fakepwd.py
@@ -6,7 +6,7 @@
L{twisted.python.fakepwd} provides a fake implementation of the L{pwd} API.
"""
-from typing import List, Optional
+from __future__ import annotations
__all__ = ["UserDatabase", "ShadowDatabase"]
@@ -60,7 +60,7 @@ class UserDatabase:
added to this database.
"""
- _users: List[_UserRecord]
+ _users: list[_UserRecord]
_lastUID: int = 10101
_lastGID: int = 20202
@@ -71,8 +71,8 @@ class UserDatabase:
self,
username: str,
password: str = "password",
- uid: Optional[int] = None,
- gid: Optional[int] = None,
+ uid: int | None = None,
+ gid: int | None = None,
gecos: str = "",
home: str = "",
shell: str = "/bin/sh",
@@ -130,7 +130,7 @@ class UserDatabase:
return entry
raise KeyError()
- def getpwall(self) -> List[_UserRecord]:
+ def getpwall(self) -> list[_UserRecord]:
"""
Return a list of all user records.
"""
@@ -194,7 +194,7 @@ class ShadowDatabase:
@since: 12.0
"""
- _users: List[_ShadowRecord]
+ _users: list[_ShadowRecord]
def __init__(self) -> None:
self._users = []
diff --git a/contrib/python/Twisted/py3/twisted/python/filepath.py b/contrib/python/Twisted/py3/twisted/python/filepath.py
index c5feb2f3f42..1a0cca5c0bb 100644
--- a/contrib/python/Twisted/py3/twisted/python/filepath.py
+++ b/contrib/python/Twisted/py3/twisted/python/filepath.py
@@ -12,6 +12,7 @@ import base64
import errno
import os
import sys
+from collections.abc import Iterable, Sequence
from os import listdir, stat, utime
from os.path import (
abspath,
@@ -46,22 +47,17 @@ from typing import (
Any,
AnyStr,
Callable,
- Dict,
Generic,
- Iterable,
- List,
- Optional,
- Sequence,
- Tuple,
+ Literal,
TypeVar,
- Union,
cast,
overload,
)
-from zope.interface import Attribute, Interface, implementer
+if TYPE_CHECKING:
+ from types import NotImplementedType
-from typing_extensions import Literal
+from zope.interface import Attribute, Interface, implementer
from twisted.python.compat import cmp, comparable
from twisted.python.runtime import platform
@@ -208,7 +204,7 @@ class IFilePath(Interface):
@raise Exception: if the file at this file path is not a directory.
"""
- def basename() -> Union[str, bytes]:
+ def basename() -> str | bytes:
"""
Retrieve the final component of the file path's path (everything after
the final path separator).
@@ -352,7 +348,7 @@ class AbstractFilePath(Generic[AnyStr]):
"""
raise NotImplementedError()
- def listdir(self) -> List[AnyStr]:
+ def listdir(self) -> list[AnyStr]:
"""
Subclasses must implement this.
"""
@@ -405,7 +401,7 @@ class AbstractFilePath(Generic[AnyStr]):
@return: an iterable of all currently-existing children of this object.
"""
try:
- subnames: List[AnyStr] = self.listdir()
+ subnames: list[AnyStr] = self.listdir()
except OSError as ose:
# Under Python 3.3 and higher on Windows, WindowsError is an
# alias for OSError. OSError has a winerror attribute and an
@@ -441,7 +437,7 @@ class AbstractFilePath(Generic[AnyStr]):
def walk(
self: _Self,
- descend: Optional[Callable[[_Self], bool]] = None,
+ descend: Callable[[_Self], bool] | None = None,
) -> Iterable[_Self]:
"""
Yield myself, then each of my children, and each of those children's
@@ -513,7 +509,7 @@ class AbstractFilePath(Generic[AnyStr]):
path = path.child(name)
return path
- def segmentsFrom(self: _Self, ancestor: _Self) -> List[AnyStr]:
+ def segmentsFrom(self: _Self, ancestor: _Self) -> list[AnyStr]:
"""
Return a list of segments between a child and its ancestor.
@@ -534,7 +530,7 @@ class AbstractFilePath(Generic[AnyStr]):
# obvious fast implemenation does the right thing too
f = self
p: _Self = f.parent() # type:ignore[assignment]
- segments: List[AnyStr] = []
+ segments: list[AnyStr] = []
while f != ancestor and p != f:
segments[0:0] = [f.basename()]
f = p
@@ -664,7 +660,7 @@ class Permissions(FancyEqMixin):
return "".join([x.shorthand() for x in (self.user, self.group, self.other)])
-def _asFilesystemBytes(path: Union[bytes, str], encoding: Optional[str] = "") -> bytes:
+def _asFilesystemBytes(path: bytes | str, encoding: str | None = "") -> bytes:
"""
Return C{path} as a string of L{bytes} suitable for use on this system's
filesystem.
@@ -684,7 +680,7 @@ def _asFilesystemBytes(path: Union[bytes, str], encoding: Optional[str] = "") ->
return path.encode(encoding, errors="surrogateescape")
-def _asFilesystemText(path: Union[bytes, str], encoding: Optional[str] = None) -> str:
+def _asFilesystemText(path: bytes | str, encoding: str | None = None) -> str:
"""
Return C{path} as a string of L{unicode} suitable for use on this system's
filesystem.
@@ -706,7 +702,7 @@ def _asFilesystemText(path: Union[bytes, str], encoding: Optional[str] = None) -
def _coerceToFilesystemEncoding(
- path: AnyStr, newpath: Union[bytes, str], encoding: Optional[str] = None
+ path: AnyStr, newpath: bytes | str, encoding: str | None = None
) -> AnyStr:
"""
Return a C{newpath} that is suitable for joining to C{path}.
@@ -807,7 +803,7 @@ class FilePath(AbstractFilePath[AnyStr]):
"""
return FilePath(path)
- def __getstate__(self) -> Dict[str, object]:
+ def __getstate__(self) -> dict[str, object]:
"""
Support serialization by discarding cached L{os.stat} results and
returning everything else.
@@ -827,7 +823,7 @@ class FilePath(AbstractFilePath[AnyStr]):
"""
return _coerceToFilesystemEncoding(self.path, os.sep)
- def _asBytesPath(self, encoding: Optional[str] = None) -> bytes:
+ def _asBytesPath(self, encoding: str | None = None) -> bytes:
"""
Return the path of this L{FilePath} as bytes.
@@ -838,7 +834,7 @@ class FilePath(AbstractFilePath[AnyStr]):
"""
return _asFilesystemBytes(self.path, encoding=encoding)
- def _asTextPath(self, encoding: Optional[str] = None) -> str:
+ def _asTextPath(self, encoding: str | None = None) -> str:
"""
Return the path of this L{FilePath} as text.
@@ -849,7 +845,7 @@ class FilePath(AbstractFilePath[AnyStr]):
"""
return _asFilesystemText(self.path, encoding=encoding)
- def asBytesMode(self, encoding: Optional[str] = None) -> FilePath[bytes]:
+ def asBytesMode(self, encoding: str | None = None) -> FilePath[bytes]:
"""
Return this L{FilePath} in L{bytes}-mode.
@@ -862,7 +858,7 @@ class FilePath(AbstractFilePath[AnyStr]):
return self.clonePath(self._asBytesPath(encoding=encoding))
return self
- def asTextMode(self, encoding: Optional[str] = None) -> FilePath[str]:
+ def asTextMode(self, encoding: str | None = None) -> FilePath[str]:
"""
Return this L{FilePath} in L{unicode}-mode.
@@ -938,9 +934,7 @@ class FilePath(AbstractFilePath[AnyStr]):
raise InsecurePath(f"{newpath!r} is not a child of {ourPath!r}")
return self.clonePath(newpath)
- def childSearchPreauth(
- self, *paths: OtherAnyStr
- ) -> Optional[FilePath[OtherAnyStr]]:
+ def childSearchPreauth(self, *paths: OtherAnyStr) -> FilePath[OtherAnyStr] | None:
"""
Return my first existing child with a name in C{paths}.
@@ -962,7 +956,7 @@ class FilePath(AbstractFilePath[AnyStr]):
def siblingExtensionSearch(
self, *exts: OtherAnyStr
- ) -> Optional[FilePath[OtherAnyStr]]:
+ ) -> FilePath[OtherAnyStr] | None:
"""
Attempt to return a path with my name, given multiple possible
extensions.
@@ -1397,7 +1391,7 @@ class FilePath(AbstractFilePath[AnyStr]):
"""
return isabs(self.path)
- def listdir(self) -> List[AnyStr]:
+ def listdir(self) -> list[AnyStr]:
"""
List the base names of the direct children of this L{FilePath}.
@@ -1411,7 +1405,7 @@ class FilePath(AbstractFilePath[AnyStr]):
"""
return listdir(self.path)
- def splitext(self) -> Tuple[AnyStr, AnyStr]:
+ def splitext(self) -> tuple[AnyStr, AnyStr]:
"""
Split the file path into a pair C{(root, ext)} such that
C{root + ext == path}.
@@ -1473,7 +1467,7 @@ class FilePath(AbstractFilePath[AnyStr]):
):
raise
- def globChildren(self, pattern: OtherAnyStr) -> List[FilePath[OtherAnyStr]]:
+ def globChildren(self, pattern: OtherAnyStr) -> list[FilePath[OtherAnyStr]]:
"""
Assuming I am representing a directory, return a list of FilePaths
representing my children that match the given pattern.
@@ -1524,7 +1518,7 @@ class FilePath(AbstractFilePath[AnyStr]):
"""
return self.clonePath(self.dirname())
- def setContent(self, content: bytes, ext: Union[str, bytes] = ".new") -> None:
+ def setContent(self, content: bytes, ext: str | bytes = ".new") -> None:
"""
Replace the file at this path with a new file that contains the given
bytes, trying to avoid data-loss in the meanwhile.
@@ -1576,9 +1570,10 @@ class FilePath(AbstractFilePath[AnyStr]):
os.unlink(self.path)
os.rename(sib.path, self.asBytesMode().path)
- def __cmp__(self, other: object) -> int:
+ def __cmp__(self, other: object) -> int | NotImplementedType:
if not isinstance(other, FilePath):
- return NotImplemented
+ # https://github.com/python/mypy/issues/18914
+ return NotImplemented # type:ignore[no-any-return]
return cmp(self.path, other.path)
def createDirectory(self) -> None:
@@ -1622,13 +1617,11 @@ class FilePath(AbstractFilePath[AnyStr]):
...
@overload
- def temporarySibling(
- self, extension: Optional[OtherAnyStr]
- ) -> FilePath[OtherAnyStr]:
+ def temporarySibling(self, extension: OtherAnyStr | None) -> FilePath[OtherAnyStr]:
...
def temporarySibling(
- self, extension: Optional[OtherAnyStr] = None
+ self, extension: OtherAnyStr | None = None
) -> FilePath[OtherAnyStr]:
"""
Construct a path referring to a sibling of this path.
diff --git a/contrib/python/Twisted/py3/twisted/python/formmethod.py b/contrib/python/Twisted/py3/twisted/python/formmethod.py
index aa5fb97a2d3..4394f51eec7 100644
--- a/contrib/python/Twisted/py3/twisted/python/formmethod.py
+++ b/contrib/python/Twisted/py3/twisted/python/formmethod.py
@@ -10,8 +10,10 @@ This module contains support for descriptive method signatures that can be used
to format methods.
"""
+from __future__ import annotations
+
import calendar
-from typing import Any, Optional, Tuple
+from typing import Any
class FormException(Exception):
@@ -136,7 +138,7 @@ class Hidden(String):
class Integer(Argument):
"""A single integer."""
- defaultDefault: Optional[int] = None
+ defaultDefault: int | None = None
def __init__(
self, name, allowNone=1, default=None, shortDesc=None, longDesc=None, hints=None
@@ -203,7 +205,7 @@ class IntegerRange(Integer):
class Float(Argument):
- defaultDefault: Optional[float] = None
+ defaultDefault: float | None = None
def __init__(
self, name, allowNone=1, default=None, shortDesc=None, longDesc=None, hints=None
@@ -343,7 +345,7 @@ def positiveInt(x):
class Date(Argument):
"""A date -- (year, month, day) tuple."""
- defaultDefault: Optional[Tuple[int, int, int]] = None
+ defaultDefault: tuple[int, int, int] | None = None
def __init__(
self, name, allowNone=1, default=None, shortDesc=None, longDesc=None, hints=None
diff --git a/contrib/python/Twisted/py3/twisted/python/htmlizer.py b/contrib/python/Twisted/py3/twisted/python/htmlizer.py
index c1d4e43a378..2e959f5d103 100644
--- a/contrib/python/Twisted/py3/twisted/python/htmlizer.py
+++ b/contrib/python/Twisted/py3/twisted/python/htmlizer.py
@@ -9,7 +9,6 @@ HTML rendering of Python source.
import keyword
import tokenize
from html import escape
-from typing import List
from . import reflect
@@ -74,11 +73,11 @@ class HTMLWriter:
tokens as HTML spans.
"""
- noSpan: List[str] = []
+ noSpan: list[str] = []
def __init__(self, writer):
self.writer = writer
- noSpan: List[str] = []
+ noSpan: list[str] = []
reflect.accumulateClassList(self.__class__, "noSpan", noSpan)
self.noSpan = noSpan
diff --git a/contrib/python/Twisted/py3/twisted/python/log.py b/contrib/python/Twisted/py3/twisted/python/log.py
index 4392486bbee..82db7fc9f87 100644
--- a/contrib/python/Twisted/py3/twisted/python/log.py
+++ b/contrib/python/Twisted/py3/twisted/python/log.py
@@ -6,13 +6,14 @@
Logging and metrics infrastructure.
"""
+from __future__ import annotations
import sys
import time
import warnings
from abc import ABC, abstractmethod
from datetime import datetime, timezone
-from typing import Any, BinaryIO, Dict, Optional, cast
+from typing import Any, BinaryIO, cast
from zope.interface import Interface
@@ -31,7 +32,7 @@ from twisted.logger._legacy import publishToNewObserver as _publishNew
from twisted.python import context, failure, reflect, util
from twisted.python.threadable import synchronize
-EventDict = Dict[str, Any]
+EventDict = dict[str, Any]
class ILogContext:
@@ -353,7 +354,7 @@ if "theLogPublisher" not in globals():
"""
-def _safeFormat(fmtString: str, fmtDict: Dict[str, Any]) -> str:
+def _safeFormat(fmtString: str, fmtDict: dict[str, Any]) -> str:
"""
Try to format a string, swallowing all errors to always return a string.
@@ -397,7 +398,7 @@ def _safeFormat(fmtString: str, fmtDict: Dict[str, Any]) -> str:
return text
-def textFromEventDict(eventDict: EventDict) -> Optional[str]:
+def textFromEventDict(eventDict: EventDict) -> str | None:
"""
Extract text from an event dict passed to a log observer. If it cannot
handle the dict, it returns None.
@@ -472,7 +473,7 @@ class FileLogObserver(_GlobalStartStopObserver):
@ivar timeFormat: If not L{None}, the format string passed to strftime().
"""
- timeFormat: Optional[str] = None
+ timeFormat: str | None = None
def __init__(self, f):
# Compatibility
diff --git a/contrib/python/Twisted/py3/twisted/python/logfile.py b/contrib/python/Twisted/py3/twisted/python/logfile.py
index 33971bab08e..8f079a715b2 100644
--- a/contrib/python/Twisted/py3/twisted/python/logfile.py
+++ b/contrib/python/Twisted/py3/twisted/python/logfile.py
@@ -7,13 +7,14 @@
A rotating, browsable log file.
"""
+from __future__ import annotations
# System Imports
import glob
import os
import stat
import time
-from typing import BinaryIO, Optional, cast
+from typing import BinaryIO, cast
from twisted.python import threadable
@@ -26,7 +27,7 @@ class BaseLogFile:
synchronized = ["write", "rotate"]
def __init__(
- self, name: str, directory: str, defaultMode: Optional[int] = None
+ self, name: str, directory: str, defaultMode: int | None = None
) -> None:
"""
Create a log file.
@@ -40,7 +41,7 @@ class BaseLogFile:
self.name = name
self.path = os.path.join(directory, name)
if defaultMode is None and os.path.exists(self.path):
- self.defaultMode: Optional[int] = stat.S_IMODE(
+ self.defaultMode: int | None = stat.S_IMODE(
os.stat(self.path)[stat.ST_MODE]
)
else:
diff --git a/contrib/python/Twisted/py3/twisted/python/rebuild.py b/contrib/python/Twisted/py3/twisted/python/rebuild.py
index e82beec1b6a..9df41c94e30 100644
--- a/contrib/python/Twisted/py3/twisted/python/rebuild.py
+++ b/contrib/python/Twisted/py3/twisted/python/rebuild.py
@@ -15,7 +15,6 @@ import time
import types
from importlib import reload
from types import ModuleType
-from typing import Dict
# Sibling Imports
from twisted.python import log, reflect
@@ -60,7 +59,7 @@ class Sensitive:
return anObject
-_modDictIDMap: Dict[int, ModuleType] = {}
+_modDictIDMap: dict[int, ModuleType] = {}
def latestFunction(oldFunc):
@@ -210,7 +209,10 @@ def rebuild(module, doLog=1):
log.msg("")
log.msg(f" (fixing {str(module.__name__)}): ")
modcount = 0
- for mk, mod in sys.modules.items():
+ # note: sys.modules can change throughout iteration
+ # https://github.com/twisted/twisted/issues/12458
+ for mk in list(sys.modules):
+ mod = sys.modules.get(mk)
modcount = modcount + 1
if mod == module or mod is None:
continue
diff --git a/contrib/python/Twisted/py3/twisted/python/reflect.py b/contrib/python/Twisted/py3/twisted/python/reflect.py
index 9991b4eb63a..70096fbabf8 100644
--- a/contrib/python/Twisted/py3/twisted/python/reflect.py
+++ b/contrib/python/Twisted/py3/twisted/python/reflect.py
@@ -7,6 +7,7 @@ Standardized versions of various cool and/or strange things that you can do
with Python's reflection capabilities.
"""
+from __future__ import annotations
import os
import pickle
@@ -17,7 +18,6 @@ import types
import weakref
from collections import deque
from io import IOBase, StringIO
-from typing import Type, Union
from twisted.python.compat import nativeString
from twisted.python.deprecate import _fullyQualifiedName as fullyQualifiedName
@@ -348,7 +348,7 @@ def filenameToModuleName(fn):
return modName
-def qual(clazz: Type[object]) -> str:
+def qual(clazz: type[object]) -> str:
"""
Return full import path of a class.
"""
@@ -373,7 +373,7 @@ def _determineClassName(x):
return "<BROKEN CLASS AT 0x%x>" % id(c)
-def _safeFormat(formatter: Union[types.FunctionType, Type[str]], o: object) -> str:
+def _safeFormat(formatter: types.FunctionType | type[str], o: object) -> str:
"""
Helper function for L{safe_repr} and L{safe_str}.
diff --git a/contrib/python/Twisted/py3/twisted/python/runtime.py b/contrib/python/Twisted/py3/twisted/python/runtime.py
index 5e9c71357e5..59fd0a67888 100644
--- a/contrib/python/Twisted/py3/twisted/python/runtime.py
+++ b/contrib/python/Twisted/py3/twisted/python/runtime.py
@@ -2,6 +2,8 @@
# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.
+from __future__ import annotations
+
__all__ = [
"seconds",
"shortPythonVersion",
@@ -13,7 +15,6 @@ import os
import sys
import warnings
from time import time as seconds
-from typing import Optional
def shortPythonVersion() -> str:
@@ -37,13 +38,11 @@ class Platform:
Gives us information about the platform we're running on.
"""
- type: Optional[str] = knownPlatforms.get(os.name)
+ type: str | None = knownPlatforms.get(os.name)
seconds = staticmethod(seconds)
_platform = sys.platform
- def __init__(
- self, name: Optional[str] = None, platform: Optional[str] = None
- ) -> None:
+ def __init__(self, name: str | None = None, platform: str | None = None) -> None:
if name is not None:
self.type = knownPlatforms.get(name)
if platform is not None:
@@ -57,7 +56,7 @@ class Platform:
"""
return self.type != None
- def getType(self) -> Optional[str]:
+ def getType(self) -> str | None:
"""
Get platform type.
diff --git a/contrib/python/Twisted/py3/twisted/python/sendmsg.py b/contrib/python/Twisted/py3/twisted/python/sendmsg.py
index ace9a5231c5..4f987204230 100644
--- a/contrib/python/Twisted/py3/twisted/python/sendmsg.py
+++ b/contrib/python/Twisted/py3/twisted/python/sendmsg.py
@@ -9,7 +9,6 @@ sendmsg(2) and recvmsg(2) support for Python.
from collections import namedtuple
from socket import CMSG_SPACE, SCM_RIGHTS, socket as Socket
-from typing import List, Tuple
__all__ = ["sendmsg", "recvmsg", "getSocketFamily", "SCM_RIGHTS"]
@@ -20,7 +19,7 @@ ReceivedMessage = namedtuple("ReceivedMessage", ["data", "ancillary", "flags"])
def sendmsg(
socket: Socket,
data: bytes,
- ancillary: List[Tuple[int, int, bytes]] = [],
+ ancillary: list[tuple[int, int, bytes]] = [],
flags: int = 0,
) -> int:
"""
diff --git a/contrib/python/Twisted/py3/twisted/python/systemd.py b/contrib/python/Twisted/py3/twisted/python/systemd.py
index 51911160eb9..627f0177ad9 100644
--- a/contrib/python/Twisted/py3/twisted/python/systemd.py
+++ b/contrib/python/Twisted/py3/twisted/python/systemd.py
@@ -9,11 +9,12 @@ Currently only the minimum APIs necessary for using systemd's socket activation
feature are supported.
"""
+from __future__ import annotations
__all__ = ["ListenFDs"]
+from collections.abc import Mapping, Sequence
from os import getpid
-from typing import Dict, List, Mapping, Optional, Sequence
from attrs import Factory, define
@@ -46,9 +47,9 @@ class ListenFDs:
@classmethod
def fromEnvironment(
cls,
- environ: Optional[Mapping[str, str]] = None,
- start: Optional[int] = None,
- ) -> "ListenFDs":
+ environ: Mapping[str, str] | None = None,
+ start: int | None = None,
+ ) -> ListenFDs:
"""
@param environ: A dictionary-like object to inspect to discover
inherited descriptors. By default, L{None}, indicating that the
@@ -71,7 +72,7 @@ class ListenFDs:
start = cls._START
if str(getpid()) == environ.get("LISTEN_PID"):
- descriptors: List[int] = _parseDescriptors(start, environ)
+ descriptors: list[int] = _parseDescriptors(start, environ)
names: Sequence[str] = _parseNames(environ)
else:
descriptors = []
@@ -89,13 +90,13 @@ class ListenFDs:
return cls(descriptors, names)
- def inheritedDescriptors(self) -> List[int]:
+ def inheritedDescriptors(self) -> list[int]:
"""
@return: The configured descriptors.
"""
return list(self._descriptors)
- def inheritedNamedDescriptors(self) -> Dict[str, int]:
+ def inheritedNamedDescriptors(self) -> dict[str, int]:
"""
@return: A mapping from the names of configured descriptors to
their integer values.
@@ -103,7 +104,7 @@ class ListenFDs:
return dict(zip(self._names, self._descriptors))
-def _parseDescriptors(start: int, environ: Mapping[str, str]) -> List[int]:
+def _parseDescriptors(start: int, environ: Mapping[str, str]) -> list[int]:
"""
Parse the I{LISTEN_FDS} environment variable supplied by systemd.
diff --git a/contrib/python/Twisted/py3/twisted/python/threadpool.py b/contrib/python/Twisted/py3/twisted/python/threadpool.py
index ab5c0f1e67f..58f008085f2 100644
--- a/contrib/python/Twisted/py3/twisted/python/threadpool.py
+++ b/contrib/python/Twisted/py3/twisted/python/threadpool.py
@@ -12,9 +12,9 @@ instead of creating a thread pool directly.
from __future__ import annotations
from threading import Thread, current_thread
-from typing import Any, Callable, List, Optional, TypeVar
+from typing import Any, Callable, Protocol, TypeVar
-from typing_extensions import ParamSpec, Protocol, TypedDict
+from typing_extensions import ParamSpec, TypedDict
from twisted._threads import pool as _pool
from twisted.python import context, log
@@ -72,7 +72,7 @@ class ThreadPool:
_pool = staticmethod(_pool)
def __init__(
- self, minthreads: int = 5, maxthreads: int = 20, name: Optional[str] = None
+ self, minthreads: int = 5, maxthreads: int = 20, name: str | None = None
):
"""
Create a new threadpool.
@@ -91,7 +91,7 @@ class ThreadPool:
self.min = minthreads
self.max = maxthreads
self.name = name
- self.threads: List[Thread] = []
+ self.threads: list[Thread] = []
def trackingThreadFactory(*a: Any, **kw: Any) -> Thread:
thread = self.threadFactory( # type: ignore[misc]
@@ -226,7 +226,7 @@ class ThreadPool:
def callInThreadWithCallback(
self,
- onResult: Optional[Callable[[bool, _R], object]],
+ onResult: Callable[[bool, _R], object] | None,
func: Callable[_P, _R],
*args: _P.args,
**kw: _P.kwargs,
@@ -300,7 +300,7 @@ class ThreadPool:
thread.join()
def adjustPoolsize(
- self, minthreads: Optional[int] = None, maxthreads: Optional[int] = None
+ self, minthreads: int | None = None, maxthreads: int | None = None
) -> None:
"""
Adjust the number of available threads by setting C{min} and C{max} to
diff --git a/contrib/python/Twisted/py3/twisted/python/usage.py b/contrib/python/Twisted/py3/twisted/python/usage.py
index 32f074c7318..750197b4c20 100644
--- a/contrib/python/Twisted/py3/twisted/python/usage.py
+++ b/contrib/python/Twisted/py3/twisted/python/usage.py
@@ -21,7 +21,7 @@ import os
import sys
import textwrap
from os import path
-from typing import Any, Dict, Optional, cast
+from typing import Any, cast
# Sibling Imports
from twisted.python import reflect, util
@@ -63,7 +63,7 @@ class CoerceParameter:
self.options.opts[parameterName] = value
-class Options(Dict[str, Any]):
+class Options(dict[str, Any]):
"""
An option list parser class
@@ -150,9 +150,9 @@ class Options(Dict[str, Any]):
or doc/core/howto/options.xhtml in your Twisted directory.
"""
- subCommand: Optional[str] = None
- defaultSubCommand: Optional[str] = None
- parent: "Optional[Options]" = None
+ subCommand: str | None = None
+ defaultSubCommand: str | None = None
+ parent: Options | None = None
completionData = None
_shellCompFile = sys.stdout # file to use if shell completion is requested
@@ -477,7 +477,7 @@ class Options(Dict[str, Any]):
)
return synopsis
- def getUsage(self, width: Optional[int] = None) -> str:
+ def getUsage(self, width: int | None = None) -> str:
# If subOptions exists by now, then there was probably an error while
# parsing its options.
if hasattr(self, "subOptions"):
@@ -569,7 +569,7 @@ class Completer:
subclasses for specific completion functionality.
"""
- _descr: Optional[str] = None
+ _descr: str | None = None
def __init__(self, descr=None, repeat=False):
"""
diff --git a/contrib/python/Twisted/py3/twisted/python/util.py b/contrib/python/Twisted/py3/twisted/python/util.py
index 6047f7eda53..4a196012a92 100644
--- a/contrib/python/Twisted/py3/twisted/python/util.py
+++ b/contrib/python/Twisted/py3/twisted/python/util.py
@@ -30,17 +30,8 @@ else:
# For backwards compatibility, some things import this, so just link it
from collections import OrderedDict
-from typing import (
- Any,
- Callable,
- ClassVar,
- Mapping,
- MutableMapping,
- Sequence,
- Tuple,
- TypeVar,
- Union,
-)
+from collections.abc import Mapping, MutableMapping, Sequence
+from typing import Any, Callable, ClassVar, TypeVar
from incremental import Version
@@ -615,7 +606,7 @@ class FancyStrMixin:
# Override in subclasses:
showAttributes: Sequence[
- Union[str, Tuple[str, str, str], Tuple[str, Callable[[Any], str]]]
+ str | tuple[str, str, str] | tuple[str, Callable[[Any], str]]
] = ()
def __str__(self) -> str:
diff --git a/contrib/python/Twisted/py3/twisted/python/zippath.py b/contrib/python/Twisted/py3/twisted/python/zippath.py
index 9aa9c7c6b88..472194ddad6 100644
--- a/contrib/python/Twisted/py3/twisted/python/zippath.py
+++ b/contrib/python/Twisted/py3/twisted/python/zippath.py
@@ -12,24 +12,13 @@ from __future__ import annotations
import errno
import os
import time
-from typing import (
- IO,
- TYPE_CHECKING,
- Any,
- AnyStr,
- Dict,
- Generic,
- Iterable,
- List,
- Tuple,
- TypeVar,
- Union,
-)
+from collections.abc import Iterable
+from typing import IO, TYPE_CHECKING, Any, AnyStr, Generic, Literal, TypeVar
from zipfile import ZipFile
from zope.interface import implementer
-from typing_extensions import Literal, Self
+from typing_extensions import Self
from twisted.python.compat import cmp, comparable
from twisted.python.filepath import (
@@ -80,19 +69,20 @@ class ZipPath(Generic[_ZipStr, _ArchiveStr], AbstractFilePath[_ZipStr]):
archiveFilename: _ZipStr = _coerceToFilesystemEncoding(
pathInArchive, archive._zipfileFilename
)
- segments: List[_ZipStr] = self.pathInArchive.split(sep)
+ segments: list[_ZipStr] = self.pathInArchive.split(sep)
fakePath: _ZipStr = os.path.join(archiveFilename, *segments)
self.path: _ZipStr = fakePath
def __cmp__(self, other: object) -> int:
if not isinstance(other, ZipPath):
- return NotImplemented
+ # https://github.com/python/mypy/issues/18914
+ return NotImplemented # type:ignore[no-any-return]
return cmp(
(self.archive, self.pathInArchive), (other.archive, other.pathInArchive)
)
def __repr__(self) -> str:
- parts: List[_ZipStr]
+ parts: list[_ZipStr]
parts = [
_coerceToFilesystemEncoding(self.sep, os.path.abspath(self.archive.path))
]
@@ -112,7 +102,7 @@ class ZipPath(Generic[_ZipStr, _ArchiveStr], AbstractFilePath[_ZipStr]):
def _nativeParent(
self,
- ) -> Union[ZipPath[_ZipStr, _ArchiveStr], ZipArchive[_ArchiveStr]]:
+ ) -> ZipPath[_ZipStr, _ArchiveStr] | ZipArchive[_ArchiveStr]:
"""
Return parent, discarding our own encoding in favor of whatever the
archive's is.
@@ -122,7 +112,7 @@ class ZipPath(Generic[_ZipStr, _ArchiveStr], AbstractFilePath[_ZipStr]):
return self.archive
return ZipPath(self.archive, self.sep.join(splitup[:-1]))
- def parent(self) -> Union[ZipPath[_ZipStr, _ArchiveStr], ZipArchive[_ZipStr]]:
+ def parent(self) -> ZipPath[_ZipStr, _ArchiveStr] | ZipArchive[_ZipStr]:
parent = self._nativeParent()
if isinstance(parent, ZipArchive):
return ZipArchive(
@@ -134,7 +124,7 @@ class ZipPath(Generic[_ZipStr, _ArchiveStr], AbstractFilePath[_ZipStr]):
def parents(
self,
- ) -> Iterable[Union[ZipPath[_ZipStr, _ArchiveStr], ZipArchive[_ZipStr]]]:
+ ) -> Iterable[ZipPath[_ZipStr, _ArchiveStr] | ZipArchive[_ZipStr]]:
...
def child(self, path: OtherAnyStr) -> ZipPath[OtherAnyStr, _ArchiveStr]:
@@ -154,8 +144,8 @@ class ZipPath(Generic[_ZipStr, _ArchiveStr], AbstractFilePath[_ZipStr]):
return ZipPath(self.archive, joiner.join([pathInArchive, path]))
def sibling(self, path: OtherAnyStr) -> ZipPath[OtherAnyStr, _ArchiveStr]:
- parent: Union[ZipPath[_ZipStr, _ArchiveStr], ZipArchive[_ZipStr]]
- rightTypedParent: Union[ZipPath[_ZipStr, _ArchiveStr], ZipArchive[_ArchiveStr]]
+ parent: ZipPath[_ZipStr, _ArchiveStr] | ZipArchive[_ZipStr]
+ rightTypedParent: ZipPath[_ZipStr, _ArchiveStr] | ZipArchive[_ArchiveStr]
parent = self.parent()
rightTypedParent = self.archive if isinstance(parent, ZipArchive) else parent
@@ -174,7 +164,7 @@ class ZipPath(Generic[_ZipStr, _ArchiveStr], AbstractFilePath[_ZipStr]):
def islink(self) -> bool:
return False
- def listdir(self) -> List[_ZipStr]:
+ def listdir(self) -> list[_ZipStr]:
if self.exists():
if self.isdir():
parentArchivePath: _ArchiveStr = _coerceToFilesystemEncoding(
@@ -191,7 +181,7 @@ class ZipPath(Generic[_ZipStr, _ArchiveStr], AbstractFilePath[_ZipStr]):
OSError(errno.ENOENT, "Non-existent zip entry listed")
)
- def splitext(self) -> Tuple[_ZipStr, _ZipStr]:
+ def splitext(self) -> tuple[_ZipStr, _ZipStr]:
"""
Return a value similar to that returned by C{os.path.splitext}.
"""
@@ -295,7 +285,7 @@ class ZipArchive(ZipPath[AnyStr, AnyStr]):
self.pathInArchive = _coerceToFilesystemEncoding(archivePathname, "")
# zipfile is already wasting O(N) memory on cached ZipInfo instances,
# so there's no sense in trying to do this lazily or intelligently
- self.childmap: Dict[AnyStr, Dict[AnyStr, int]] = {}
+ self.childmap: dict[AnyStr, dict[AnyStr, int]] = {}
for name in self.zipfile.namelist():
splitName = _coerceToFilesystemEncoding(self.path, name).split(self.sep)
@@ -309,7 +299,8 @@ class ZipArchive(ZipPath[AnyStr, AnyStr]):
def __cmp__(self, other: object) -> int:
if not isinstance(other, ZipArchive):
- return NotImplemented
+ # https://github.com/python/mypy/issues/18914
+ return NotImplemented # type:ignore[no-any-return]
return cmp(self.path, other.path)
def child(self, path: OtherAnyStr) -> ZipPath[OtherAnyStr, AnyStr]:
diff --git a/contrib/python/Twisted/py3/twisted/runner/procmon.py b/contrib/python/Twisted/py3/twisted/runner/procmon.py
index 865ce00624b..c4278a0168c 100644
--- a/contrib/python/Twisted/py3/twisted/runner/procmon.py
+++ b/contrib/python/Twisted/py3/twisted/runner/procmon.py
@@ -7,7 +7,7 @@ Support for starting, monitoring, and restarting child process.
"""
from __future__ import annotations
-from typing import Any, Dict, List, Optional
+from typing import Any
import attr
import incremental
@@ -32,27 +32,22 @@ class _Process:
The parameters of a process to be restarted.
@ivar args: command-line arguments (including name of command as first one)
- @type args: C{list}
@ivar uid: user-id to run process as, or None (which means inherit uid)
- @type uid: C{int}
@ivar gid: group-id to run process as, or None (which means inherit gid)
- @type gid: C{int}
@ivar env: environment for process
- @type env: C{dict}
@ivar cwd: initial working directory for process or None
(which means inherit cwd)
- @type cwd: C{str}
"""
- args: List[str]
- uid: Optional[int] = None
- gid: Optional[int] = None
- env: Dict[str, str] = attr.ib(default=attr.Factory(dict))
- cwd: Optional[str] = None
+ args: list[str]
+ uid: int | None = None
+ gid: int | None = None
+ env: dict[str, str] = attr.ib(default=attr.Factory(dict))
+ cwd: str | None = None
@deprecate.deprecated(incremental.Version("Twisted", 18, 7, 0))
def toTuple(self) -> tuple[list[str], int | None, int | None, dict[str, str]]:
@@ -206,7 +201,7 @@ class ProcessMonitor(service.Service):
def __init__(
self,
- reactor: IReactorProcess = _reactor, # type:ignore
+ reactor: IReactorProcess = _reactor,
) -> None:
self._reactor = reactor
self._clock = IReactorTime(reactor)
@@ -318,7 +313,7 @@ class ProcessMonitor(service.Service):
for name in list(self._processes):
self.stopProcess(name)
- @deprecate.deprecatedProperty(incremental.Version("Twisted", 25, 5, 0))
+ @deprecate.deprecated(incremental.Version("Twisted", 25, 5, 0))
def connectionLost(self, name: str) -> None:
"""
Called when a monitored processes exits. If
diff --git a/contrib/python/Twisted/py3/twisted/runner/procmontap.py b/contrib/python/Twisted/py3/twisted/runner/procmontap.py
index 03205783b31..071471ce682 100644
--- a/contrib/python/Twisted/py3/twisted/runner/procmontap.py
+++ b/contrib/python/Twisted/py3/twisted/runner/procmontap.py
@@ -6,7 +6,7 @@
Support for creating a service which runs a process monitor.
"""
-from typing import List, Sequence
+from collections.abc import Sequence
from twisted.python import usage
from twisted.runner.procmon import ProcessMonitor
@@ -57,7 +57,7 @@ class Options(usage.Options):
],
]
- optFlags: List[Sequence[str]] = []
+ optFlags: list[Sequence[str]] = []
longdesc = """\
procmon runs processes, monitors their progress, and restarts them when they
diff --git a/contrib/python/Twisted/py3/twisted/scripts/_twistd_unix.py b/contrib/python/Twisted/py3/twisted/scripts/_twistd_unix.py
index 295a32fa01e..f33c5959c4c 100644
--- a/contrib/python/Twisted/py3/twisted/scripts/_twistd_unix.py
+++ b/contrib/python/Twisted/py3/twisted/scripts/_twistd_unix.py
@@ -390,7 +390,9 @@ class UnixApplicationRunner(app.ApplicationRunner):
"An error has occurred: {}\nPlease look at log "
"file for more information.\n".format(dataRepr)
)
- untilConcludes(sys.__stderr__.write, msg)
+ origstderr = sys.__stderr__
+ assert origstderr is not None
+ untilConcludes(origstderr.write, msg)
return 1
return 0
diff --git a/contrib/python/Twisted/py3/twisted/scripts/htmlizer.py b/contrib/python/Twisted/py3/twisted/scripts/htmlizer.py
index 9c588ae11fc..fa820b33bde 100644
--- a/contrib/python/Twisted/py3/twisted/scripts/htmlizer.py
+++ b/contrib/python/Twisted/py3/twisted/scripts/htmlizer.py
@@ -44,7 +44,7 @@ class Options(usage.Options):
extraActions=[usage.CompleteFiles("*.py", descr="source python file")]
)
- def parseArgs(self, filename):
+ def parseArgs(self, filename): # type:ignore[override]
self["filename"] = filename
diff --git a/contrib/python/Twisted/py3/twisted/scripts/trial.py b/contrib/python/Twisted/py3/twisted/scripts/trial.py
index f3259ed21b7..f48bbdd39cf 100644
--- a/contrib/python/Twisted/py3/twisted/scripts/trial.py
+++ b/contrib/python/Twisted/py3/twisted/scripts/trial.py
@@ -2,6 +2,7 @@
# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.
+from __future__ import annotations
import gc
import inspect
@@ -12,7 +13,7 @@ import sys
import time
import trace
import warnings
-from typing import NoReturn, Optional, Type
+from typing import Callable, NoReturn
from twisted import plugin
from twisted.application import app
@@ -57,11 +58,13 @@ def _autoJobs() -> int:
@returns: A strictly positive integer.
"""
- number: Optional[int]
- if getattr(os, "process_cpu_count", None) is not None:
- number = os.process_cpu_count() # type: ignore[attr-defined]
- elif getattr(os, "sched_getaffinity", None) is not None:
- number = len(os.sched_getaffinity(0))
+ number: int | None
+ process_cpu_count: Callable[[], int | None] | None
+ sched_getaffinity: Callable[[int], set[int]] | None
+ if process_cpu_count := getattr(os, "process_cpu_count", None):
+ number = process_cpu_count()
+ elif sched_getaffinity := getattr(os, "sched_getaffinity", None):
+ number = len(sched_getaffinity(0))
else:
number = os.cpu_count()
if number is None or number < 1:
@@ -268,7 +271,7 @@ class _BasicOptions:
],
)
- tracer: Optional[trace.Trace] = None
+ tracer: trace.Trace | None = None
def __init__(self):
self["tests"] = []
@@ -625,7 +628,7 @@ def _makeRunner(config: Options) -> runner._Runner:
@return: A trial runner instance.
"""
- cls: Type[runner._Runner] = runner.TrialRunner
+ cls: type[runner._Runner] = runner.TrialRunner
args = {
"reporterFactory": config["reporter"],
"tracebackFormat": config["tbformat"],
diff --git a/contrib/python/Twisted/py3/twisted/spread/jelly.py b/contrib/python/Twisted/py3/twisted/spread/jelly.py
index 46cda178448..dccf8f3aace 100644
--- a/contrib/python/Twisted/py3/twisted/spread/jelly.py
+++ b/contrib/python/Twisted/py3/twisted/spread/jelly.py
@@ -203,14 +203,14 @@ def setUnjellyableFactoryForClass(classname, copyFactory):
"""
Set the factory to construct a remote instance of a type::
- jellier.setUnjellyableFactoryForClass('module.package.Class', MyFactory)
+ jellier.setUnjellyableFactoryForClass('module.package.Class', MyFactory)
Call this at the module level immediately after its class definition.
C{copyFactory} should return an instance or subclass of
L{RemoteCopy<pb.RemoteCopy>}.
- Similar to L{setUnjellyableForClass} except it uses a factory instead
- of creating an instance.
+ Similar to L{twisted.spread.jelly.setUnjellyableForClass} except it uses a
+ factory instead of creating an instance.
"""
global unjellyableFactoryRegistry
diff --git a/contrib/python/Twisted/py3/twisted/spread/pb.py b/contrib/python/Twisted/py3/twisted/spread/pb.py
index 031292e318c..bb85a63bcbc 100644
--- a/contrib/python/Twisted/py3/twisted/spread/pb.py
+++ b/contrib/python/Twisted/py3/twisted/spread/pb.py
@@ -1277,7 +1277,7 @@ def challenge():
return crap
-class PBClientFactory(protocol.ClientFactory):
+class PBClientFactory(protocol.ClientFactory[Broker]):
"""
Client factory for PB brokers.
@@ -1420,7 +1420,7 @@ class PBClientFactory(protocol.ClientFactory):
return d
-class PBServerFactory(protocol.ServerFactory):
+class PBServerFactory(protocol.ServerFactory[Broker]):
"""
Server factory for perspective broker.
diff --git a/contrib/python/Twisted/py3/twisted/tap/portforward.py b/contrib/python/Twisted/py3/twisted/tap/portforward.py
index 3fc01460941..70d5c6ad347 100644
--- a/contrib/python/Twisted/py3/twisted/tap/portforward.py
+++ b/contrib/python/Twisted/py3/twisted/tap/portforward.py
@@ -11,16 +11,19 @@ from twisted.python import usage
class Options(usage.Options):
synopsis = "[options]"
- longdesc = "Port Forwarder."
+ longdesc = "(Deprecated) Port Forwarder. See 'twist forward' instead."
optParameters = [
- ["port", "p", "6666", "Set the port number."],
- ["host", "h", "localhost", "Set the host."],
- ["dest_port", "d", 6665, "Set the destination port."],
+ ["port", "p", "tcp:6666", "The string endpoint to listen on."],
+ ["host", "h", "localhost", "The destination host to connect to."],
+ ["dest_port", "d", 6665, "Set the destination port to connect to."],
]
compData = usage.Completions(optActions={"host": usage.CompleteHostnames()})
def makeService(config):
+ """
+ Create a port-forwarding service.
+ """
f = portforward.ProxyFactory(config["host"], int(config["dest_port"]))
return strports.service(config["port"], f)
diff --git a/contrib/python/Twisted/py3/twisted/trial/_asyncrunner.py b/contrib/python/Twisted/py3/twisted/trial/_asyncrunner.py
index 12a8342ffae..12058f26ebc 100644
--- a/contrib/python/Twisted/py3/twisted/trial/_asyncrunner.py
+++ b/contrib/python/Twisted/py3/twisted/trial/_asyncrunner.py
@@ -5,12 +5,12 @@
"""
Infrastructure for test running and suites.
"""
-
+from __future__ import annotations
import doctest
import gc
import unittest as pyunit
-from typing import Iterator, Union
+from collections.abc import Iterator
from zope.interface import implementer
@@ -25,7 +25,7 @@ class TestSuite(pyunit.TestSuite):
C{run} method.
"""
- def run(self, result):
+ def run(self, result: pyunit.TestResult, debug: bool = False) -> pyunit.TestResult:
"""
Call C{run} on every member of the suite.
"""
@@ -162,7 +162,7 @@ if _docTestCase:
def _iterateTests(
- testSuiteOrCase: Union[pyunit.TestCase, pyunit.TestSuite]
+ testSuiteOrCase: pyunit.TestCase | pyunit.TestSuite,
) -> Iterator[itrial.ITestCase]:
"""
Iterate through all of the test cases in C{testSuiteOrCase}.
diff --git a/contrib/python/Twisted/py3/twisted/trial/_asynctest.py b/contrib/python/Twisted/py3/twisted/trial/_asynctest.py
index 6765c703f66..b9edfdfd8d5 100644
--- a/contrib/python/Twisted/py3/twisted/trial/_asynctest.py
+++ b/contrib/python/Twisted/py3/twisted/trial/_asynctest.py
@@ -10,7 +10,7 @@ from __future__ import annotations
import inspect
import warnings
-from typing import Callable, List
+from typing import Callable
from zope.interface import implementer
@@ -27,7 +27,7 @@ from twisted.trial._synctest import FailTest, SkipTest, SynchronousTestCase
_P = ParamSpec("_P")
-_wait_is_running: List[None] = []
+_wait_is_running: list[None] = []
@implementer(itrial.ITestCase)
@@ -127,7 +127,7 @@ class TestCase(SynchronousTestCase):
)
if inspect.isgeneratorfunction(func):
exc = TypeError(
- "{!r} is a generator function and therefore will never run".format(func)
+ f"{func!r} is a generator function and therefore will never run"
)
return defer.fail(exc)
d = defer.maybeDeferred(
diff --git a/contrib/python/Twisted/py3/twisted/trial/_dist/distreporter.py b/contrib/python/Twisted/py3/twisted/trial/_dist/distreporter.py
index 3a45cc48067..798d2181320 100644
--- a/contrib/python/Twisted/py3/twisted/trial/_dist/distreporter.py
+++ b/contrib/python/Twisted/py3/twisted/trial/_dist/distreporter.py
@@ -10,21 +10,24 @@ test is over.
@since: 12.3
"""
+from __future__ import annotations
from types import TracebackType
-from typing import Optional, Tuple, Union
+from typing import Union
from zope.interface import implementer
+from typing_extensions import TypeAlias
+
from twisted.python.components import proxyForInterface
from twisted.python.failure import Failure
-from ..itrial import IReporter, ITestCase
+from ..itrial import IReporterWithDurations, ITestCase
-ReporterFailure = Union[Failure, Tuple[type, Exception, TracebackType]]
+ReporterFailure: TypeAlias = Union[Failure, tuple[type, Exception, TracebackType]]
-@implementer(IReporter)
-class DistReporter(proxyForInterface(IReporter)): # type: ignore[misc]
+@implementer(IReporterWithDurations)
+class DistReporter(proxyForInterface(IReporterWithDurations)): # type: ignore[misc]
"""
See module docstring.
"""
@@ -65,7 +68,7 @@ class DistReporter(proxyForInterface(IReporter)): # type: ignore[misc]
self.running[test.id()].append((self.original.addUnexpectedSuccess, test, todo))
def addExpectedFailure(
- self, test: ITestCase, error: ReporterFailure, todo: Optional[str] = None
+ self, test: ITestCase, error: ReporterFailure, todo: str | None = None
) -> None:
"""
Queue adding an expected failure.
diff --git a/contrib/python/Twisted/py3/twisted/trial/_dist/disttrial.py b/contrib/python/Twisted/py3/twisted/trial/_dist/disttrial.py
index bad82a88148..bb7355a28a9 100644
--- a/contrib/python/Twisted/py3/twisted/trial/_dist/disttrial.py
+++ b/contrib/python/Twisted/py3/twisted/trial/_dist/disttrial.py
@@ -8,23 +8,14 @@ responsible for coordinating all of trial's behavior at the highest level.
@since: 12.3
"""
+from __future__ import annotations
import os
import sys
+from collections.abc import Awaitable, Iterable, Sequence
from functools import partial
from os.path import isabs
-from typing import (
- Any,
- Awaitable,
- Callable,
- Iterable,
- List,
- Optional,
- Sequence,
- TextIO,
- Union,
- cast,
-)
+from typing import Any, Callable, TextIO, cast
from unittest import TestCase, TestSuite
from attrs import define, field, frozen
@@ -119,8 +110,8 @@ class StartedWorkerPool:
workingDirectory: FilePath[Any]
testDirLock: FilesystemLock
testLog: TextIO
- workers: List[LocalWorker]
- ampWorkers: List[LocalWorkerAMP]
+ workers: list[LocalWorker]
+ ampWorkers: list[LocalWorkerAMP]
_logger = Logger()
@@ -168,7 +159,7 @@ class WorkerPool:
protocols: Iterable[LocalWorkerAMP],
workingDirectory: FilePath[Any],
logFile: TextIO,
- ) -> List[LocalWorker]:
+ ) -> list[LocalWorker]:
"""
Create local worker protocol instances and return them.
@@ -273,7 +264,7 @@ async def runTests(
testCases: Iterable[ITestCase],
result: DistReporter,
driveWorker: Callable[
- [DistReporter, Sequence[ITestCase], LocalWorkerAMP], Awaitable[None]
+ [DistReporter, Iterable[ITestCase], LocalWorkerAMP], Awaitable[None]
],
) -> None:
try:
@@ -310,7 +301,7 @@ class DistTrialRunner:
# on the argument annotation
_reporterFactory: Callable[..., IReporter]
_maxWorkers: int
- _workerArguments: List[str]
+ _workerArguments: list[str]
_exitFirst: bool = False
_reactor: IDistTrialReactor = field(
# mypy doesn't understand the converter
@@ -349,7 +340,7 @@ class DistTrialRunner:
async def _driveWorker(
self,
result: DistReporter,
- testCases: Sequence[ITestCase],
+ testCases: Iterable[ITestCase],
worker: LocalWorkerAMP,
) -> None:
"""
@@ -377,7 +368,7 @@ class DistTrialRunner:
async def runAsync(
self,
- suite: Union[TestCase, TestSuite],
+ suite: TestCase | TestSuite,
untilFailure: bool = False,
) -> DistReporter:
"""
@@ -451,16 +442,16 @@ class DistTrialRunner:
# Shut down the worker pool.
await startedPool.join()
- def _run(self, test: Union[TestCase, TestSuite], untilFailure: bool) -> IReporter:
- result: Union[Failure, DistReporter, None] = None
+ def _run(self, test: TestCase | TestSuite, untilFailure: bool) -> IReporter:
+ result: Failure | DistReporter | None = None
reactorStopping: bool = False
testsInProgress: Deferred[object]
- def capture(r: Union[Failure, DistReporter]) -> None:
+ def capture(r: Failure | DistReporter) -> None:
nonlocal result
result = r
- def maybeStopTests() -> Optional[Deferred[object]]:
+ def maybeStopTests() -> Deferred[object] | None:
nonlocal reactorStopping
reactorStopping = True
if result is None:
@@ -495,7 +486,7 @@ class DistTrialRunner:
# object. DistReporter isn't type annotated correctly so fix it here.
return cast(IReporter, result.original)
- def run(self, test: Union[TestCase, TestSuite]) -> IReporter:
+ def run(self, test: TestCase | TestSuite) -> IReporter:
"""
Run a reactor and a test suite.
@@ -503,7 +494,7 @@ class DistTrialRunner:
"""
return self._run(test, untilFailure=False)
- def runUntilFailure(self, test: Union[TestCase, TestSuite]) -> IReporter:
+ def runUntilFailure(self, test: TestCase | TestSuite) -> IReporter:
"""
Run the tests with local worker processes until they fail.
diff --git a/contrib/python/Twisted/py3/twisted/trial/_dist/functional.py b/contrib/python/Twisted/py3/twisted/trial/_dist/functional.py
index f0258bf44c2..f799032b63f 100644
--- a/contrib/python/Twisted/py3/twisted/trial/_dist/functional.py
+++ b/contrib/python/Twisted/py3/twisted/trial/_dist/functional.py
@@ -4,9 +4,11 @@
"""
General functional-style helpers for disttrial.
"""
+from __future__ import annotations
+from collections.abc import Awaitable, Iterable
from functools import partial, wraps
-from typing import Awaitable, Callable, Iterable, Optional, TypeVar
+from typing import Callable, TypeVar
from twisted.internet.defer import Deferred, succeed
@@ -15,7 +17,7 @@ _B = TypeVar("_B")
_C = TypeVar("_C")
-def fromOptional(default: _A, optional: Optional[_A]) -> _A:
+def fromOptional(default: _A, optional: _A | None) -> _A:
"""
Get a definite value from an optional value.
diff --git a/contrib/python/Twisted/py3/twisted/trial/_dist/stream.py b/contrib/python/Twisted/py3/twisted/trial/_dist/stream.py
index a53fd4ab214..5a23cc867b5 100644
--- a/contrib/python/Twisted/py3/twisted/trial/_dist/stream.py
+++ b/contrib/python/Twisted/py3/twisted/trial/_dist/stream.py
@@ -2,8 +2,9 @@
Buffer byte streams.
"""
+from collections.abc import Iterator
from itertools import count
-from typing import Dict, Iterator, List, TypeVar
+from typing import TypeVar
from attrs import Factory, define
@@ -38,7 +39,7 @@ class StreamReceiver:
"""
_counter: Iterator[int] = count()
- _streams: Dict[int, List[bytes]] = Factory(dict)
+ _streams: dict[int, list[bytes]] = Factory(dict)
def open(self) -> int:
"""
@@ -56,7 +57,7 @@ class StreamReceiver:
"""
self._streams[streamId].append(chunk)
- def finish(self, streamId: int) -> List[bytes]:
+ def finish(self, streamId: int) -> list[bytes]:
"""
Indicate an open stream may receive no further data and return all of
its current contents.
diff --git a/contrib/python/Twisted/py3/twisted/trial/_dist/worker.py b/contrib/python/Twisted/py3/twisted/trial/_dist/worker.py
index 77e502173ad..89a810af18a 100644
--- a/contrib/python/Twisted/py3/twisted/trial/_dist/worker.py
+++ b/contrib/python/Twisted/py3/twisted/trial/_dist/worker.py
@@ -8,15 +8,17 @@ This module implements the worker classes.
@since: 12.3
"""
+from __future__ import annotations
import os
-from typing import Any, Awaitable, Callable, Dict, List, Optional, TextIO, TypeVar
+from collections.abc import Awaitable
+from typing import Any, Callable, Protocol, TextIO, TypeVar
from unittest import TestCase
from zope.interface import implementer
from attrs import frozen
-from typing_extensions import Protocol, TypedDict
+from typing_extensions import TypedDict
from twisted.internet.defer import Deferred, DeferredList
from twisted.internet.error import ProcessDone
@@ -171,7 +173,7 @@ class LocalWorkerAMP(AMP):
self,
error: WorkerException,
errorClass: str,
- frames: List[str],
+ frames: list[str],
) -> Failure:
"""
Helper to build a C{Failure} with some traceback.
@@ -201,18 +203,16 @@ class LocalWorkerAMP(AMP):
errorClass: str,
errorStreamId: int,
framesStreamId: int,
- ) -> Dict[str, bool]:
+ ) -> dict[str, bool]:
"""
Add an error to the reporter.
- @param errorStreamId: The identifier of a stream over which the text
- of this error was previously completely sent to the peer.
+ @param errorStreamId: The identifier of a stream over which the text of
+ this error was previously completely sent to the peer.
@param framesStreamId: The identifier of a stream over which the lines
of the traceback for this error were previously completely sent to
the peer.
-
- @param error: A message describing the error.
"""
error = b"".join(self._streams.finish(errorStreamId)).decode("utf-8")
frames = [
@@ -233,7 +233,7 @@ class LocalWorkerAMP(AMP):
failStreamId: int,
failClass: str,
framesStreamId: int,
- ) -> Dict[str, bool]:
+ ) -> dict[str, bool]:
"""
Add a failure to the reporter.
@@ -263,8 +263,8 @@ class LocalWorkerAMP(AMP):
@managercommands.AddExpectedFailure.responder
def addExpectedFailure(
- self, testName: str, errorStreamId: int, todo: Optional[str]
- ) -> Dict[str, bool]:
+ self, testName: str, errorStreamId: int, todo: str | None
+ ) -> dict[str, bool]:
"""
Add an expected failure to the reporter.
diff --git a/contrib/python/Twisted/py3/twisted/trial/_dist/workerreporter.py b/contrib/python/Twisted/py3/twisted/trial/_dist/workerreporter.py
index f266d727e36..b5e459ae95e 100644
--- a/contrib/python/Twisted/py3/twisted/trial/_dist/workerreporter.py
+++ b/contrib/python/Twisted/py3/twisted/trial/_dist/workerreporter.py
@@ -8,13 +8,14 @@ Test reporter forwarding test results over trial distributed AMP commands.
@since: 12.3
"""
+from __future__ import annotations
+from collections.abc import Sequence
from types import TracebackType
-from typing import Callable, List, Optional, Sequence, Type, TypeVar
+from typing import Callable, Literal, TypeVar
from unittest import TestCase as PyUnitTestCase
from attrs import Factory, define
-from typing_extensions import Literal
from twisted.internet.defer import Deferred, maybeDeferred
from twisted.protocols.amp import AMP, MAX_VALUE_LENGTH
@@ -29,7 +30,7 @@ T = TypeVar("T")
async def addError(
- amp: AMP, testName: str, errorClass: str, error: str, frames: List[str]
+ amp: AMP, testName: str, errorClass: str, error: str, frames: list[str]
) -> None:
"""
Send an error to the worker manager over an AMP connection.
@@ -58,7 +59,7 @@ async def addError(
async def addFailure(
- amp: AMP, testName: str, fail: str, failClass: str, frames: List[str]
+ amp: AMP, testName: str, fail: str, failClass: str, frames: list[str]
) -> None:
"""
Like L{addError} but for failures.
@@ -123,8 +124,8 @@ class ReportingResults:
interface.
"""
- _reporter: "WorkerReporter"
- _results: List[Deferred[object]] = Factory(list)
+ _reporter: WorkerReporter
+ _results: list[Deferred[object]] = Factory(list)
def __enter__(self) -> Sequence[Deferred[object]]:
"""
@@ -140,7 +141,7 @@ class ReportingResults:
def __exit__(
self,
- excType: Type[BaseException],
+ excType: type[BaseException],
excValue: BaseException,
excTraceback: TracebackType,
) -> Literal[False]:
@@ -173,7 +174,7 @@ class WorkerReporter(TestResult):
_DEFAULT_TODO = "Test expected to fail"
ampProtocol: AMP
- _reporting: Optional[ReportingResults] = None
+ _reporting: ReportingResults | None = None
def __init__(self, ampProtocol):
"""
@@ -202,11 +203,11 @@ class WorkerReporter(TestResult):
return Failure(error[1], error[0], error[2])
return error
- def _getFrames(self, failure: Failure) -> List[str]:
+ def _getFrames(self, failure: Failure) -> list[str]:
"""
Extract frames from a C{Failure} instance.
"""
- frames: List[str] = []
+ frames: list[str] = []
for frame in failure.frames:
# The code object's name, the code object's filename, and the line
# number.
@@ -215,7 +216,7 @@ class WorkerReporter(TestResult):
def _call(self, f: Callable[[], T]) -> None:
"""
- Call L{f} if and only if a "result reporting" context is active.
+ Call C{f} if and only if a "result reporting" context is active.
@param f: A function to call. Its result is accumulated into the
result reporting context. It may return a L{Deferred} or a
diff --git a/contrib/python/Twisted/py3/twisted/trial/_synctest.py b/contrib/python/Twisted/py3/twisted/trial/_synctest.py
index 6b1f43dc89d..569100d025d 100644
--- a/contrib/python/Twisted/py3/twisted/trial/_synctest.py
+++ b/contrib/python/Twisted/py3/twisted/trial/_synctest.py
@@ -7,7 +7,7 @@ Things likely to be used by writers of unit tests.
Maintainer: Jonathan Lange
"""
-
+from __future__ import annotations
import inspect
import os
@@ -16,21 +16,9 @@ import tempfile
import types
import unittest as pyunit
import warnings
+from collections.abc import Coroutine, Generator, Iterable
from dis import findlinestarts as _findlinestarts
-from typing import (
- Any,
- Callable,
- Coroutine,
- Generator,
- Iterable,
- List,
- NoReturn,
- Optional,
- Tuple,
- Type,
- TypeVar,
- Union,
-)
+from typing import Any, Callable, NoReturn, TypeVar
# Python 2.7 and higher has skip support built-in
from unittest import SkipTest
@@ -78,7 +66,7 @@ class Todo:
"""
reason: str
- errors: Optional[Iterable[Type[BaseException]]] = None
+ errors: Iterable[type[BaseException]] | None = None
def __repr__(self) -> str:
return f"<Todo reason={self.reason!r} errors={self.errors!r}>"
@@ -98,9 +86,7 @@ class Todo:
def makeTodo(
- value: Union[
- str, Tuple[Union[Type[BaseException], Iterable[Type[BaseException]]], str]
- ]
+ value: (str | tuple[type[BaseException] | Iterable[type[BaseException]], str])
) -> Todo:
"""
Return a L{Todo} object built from C{value}.
@@ -119,7 +105,7 @@ def makeTodo(
if isinstance(value, tuple):
errors, reason = value
if isinstance(errors, type):
- iterableErrors: Iterable[Type[BaseException]] = [errors]
+ iterableErrors: Iterable[type[BaseException]] = [errors]
else:
iterableErrors = errors
return Todo(reason=reason, errors=iterableErrors)
@@ -371,7 +357,7 @@ class _Assertions(pyunit.TestCase):
callbacks.
"""
- def fail(self, msg: Optional[object] = None) -> NoReturn:
+ def fail(self, msg: object | None = None) -> NoReturn:
"""
Absolutely fail the test. Do not pass go, do not collect $200.
@@ -429,10 +415,13 @@ class _Assertions(pyunit.TestCase):
return context._handle(lambda: f(*args, **kwargs))
- # unittest.TestCase.assertRaises() is defined with 4 arguments
- # but we define it with 5 arguments. So we need to tell mypy
- # to ignore the following assignment to failUnlessRaises
- failUnlessRaises = assertRaises # type: ignore[assignment]
+ # The type-ignore below is present to address the evolving incompatible
+ # signature between assertRaises and failUnlessRaises in the stdlib.
+ # Depending on which version of Python you are developing with you might
+ # get a spurious error here *or not* which is why there's also the
+ # unused-ignore ignore here; in upstream unittest this method is now
+ # entirely removed, since Python 3.12, so there's nothing to conflict with.
+ failUnlessRaises = assertRaises # type:ignore[assignment,unused-ignore]
def assertEqual(self, first, second, msg=None):
"""
@@ -444,9 +433,10 @@ class _Assertions(pyunit.TestCase):
super().assertEqual(first, second, msg)
return first
+ # We keep all these aliases for backward compatibility.
+ assertEquals = assertEqual
failUnlessEqual = assertEqual
failUnlessEquals = assertEqual
- assertEquals = assertEqual
def assertIs(self, first, second, msg=None):
"""
@@ -491,6 +481,7 @@ class _Assertions(pyunit.TestCase):
raise self.failureException(msg or f"{first!r} == {second!r}")
return first
+ # We keep all these aliases for backward compatibility.
assertNotEquals = assertNotEqual
failIfEquals = assertNotEqual
failIfEqual = assertNotEqual
@@ -545,8 +536,8 @@ class _Assertions(pyunit.TestCase):
)
return first
- assertNotAlmostEquals = assertNotAlmostEqual # type:ignore[assignment]
- failIfAlmostEqual = assertNotAlmostEqual # type:ignore[assignment]
+ assertNotAlmostEquals = assertNotAlmostEqual
+ failIfAlmostEqual = assertNotAlmostEqual
failIfAlmostEquals = assertNotAlmostEqual
def assertAlmostEqual(self, first, second, places=7, msg=None, delta=None):
@@ -567,8 +558,8 @@ class _Assertions(pyunit.TestCase):
)
return first
- assertAlmostEquals = assertAlmostEqual # type:ignore[assignment]
- failUnlessAlmostEqual = assertAlmostEqual # type:ignore[assignment]
+ assertAlmostEquals = assertAlmostEqual
+ failUnlessAlmostEqual = assertAlmostEqual
def assertApproximates(self, first, second, tolerance, msg=None):
"""
@@ -686,11 +677,11 @@ class _Assertions(pyunit.TestCase):
def successResultOf(
self,
- deferred: Union[
- Coroutine[Deferred[T], Any, T],
- Generator[Deferred[T], Any, T],
- Deferred[T],
- ],
+ deferred: (
+ Coroutine[Deferred[T], Any, T]
+ | Generator[Deferred[T], Any, T]
+ | Deferred[T]
+ ),
) -> T:
"""
Return the current success result of C{deferred} or raise
@@ -716,7 +707,7 @@ class _Assertions(pyunit.TestCase):
@return: The result of C{deferred}.
"""
deferred = ensureDeferred(deferred)
- results: List[Union[T, failure.Failure]] = []
+ results: list[T | failure.Failure] = []
deferred.addBoth(results.append)
if not results:
@@ -992,7 +983,7 @@ class SynchronousTestCase(_Assertions):
return self._testMethodName
return desc
- def getSkip(self) -> Tuple[bool, Optional[str]]:
+ def getSkip(self) -> tuple[bool, str | None]:
"""
Return the skip reason set on this test, if any is set. Checks on the
instance first, then the class, then the module, then packages. As
@@ -1260,7 +1251,7 @@ class SynchronousTestCase(_Assertions):
}
if message is not None:
expectedWarning = expectedWarning + ": " + message
- self.assert_(
+ self.assertTrue(
observedWarning.startswith(expectedWarning),
f"Expected {observedWarning!r} to start with {expectedWarning!r}",
)
diff --git a/contrib/python/Twisted/py3/twisted/trial/itrial.py b/contrib/python/Twisted/py3/twisted/trial/itrial.py
index 840c6e61eeb..8b74fd758b5 100644
--- a/contrib/python/Twisted/py3/twisted/trial/itrial.py
+++ b/contrib/python/Twisted/py3/twisted/trial/itrial.py
@@ -3,10 +3,9 @@
"""
Interfaces for Trial.
-
-Maintainer: Jonathan Lange
"""
+from unittest import TestCase
import zope.interface as zi
@@ -155,3 +154,21 @@ class IReporter(zi.Interface):
L{IReporter} object, you should assume that the L{IReporter} object is
no longer usable.
"""
+
+
+class IReporterWithDurations(IReporter):
+ """
+ The L{IReporter} interface with the 'durations' additions added to the
+ standard library in 3.12.
+ """
+
+ collectedDurations: list[tuple[str, float]] = zi.Attribute(
+ """
+ The collected durations of the tests reported.
+ """
+ )
+
+ def addDuration(test: TestCase, elapsed: float) -> None:
+ """
+ Collect a duration for C{test} into L{IReporterWithDurations.collectedDurations}.
+ """
diff --git a/contrib/python/Twisted/py3/twisted/trial/reporter.py b/contrib/python/Twisted/py3/twisted/trial/reporter.py
index 2034a83e261..fda7a60c9c9 100644
--- a/contrib/python/Twisted/py3/twisted/trial/reporter.py
+++ b/contrib/python/Twisted/py3/twisted/trial/reporter.py
@@ -16,7 +16,7 @@ import unittest as pyunit
import warnings
from collections import OrderedDict
from types import TracebackType
-from typing import TYPE_CHECKING, List, Optional, Tuple, Type, Union
+from typing import TYPE_CHECKING, Union
from zope.interface import implementer
@@ -36,12 +36,12 @@ try:
except ImportError:
TestProtocolClient = None
-ExcInfo: TypeAlias = Tuple[Type[BaseException], BaseException, TracebackType]
-XUnitFailure = Union[ExcInfo, Tuple[None, None, None]]
+ExcInfo: TypeAlias = tuple[type[BaseException], BaseException, TracebackType]
+XUnitFailure = Union[ExcInfo, tuple[None, None, None]]
TrialFailure = Union[XUnitFailure, Failure]
-def _makeTodo(value: str) -> "Todo":
+def _makeTodo(value: str) -> Todo:
"""
Return a L{Todo} object built from C{value}.
@@ -97,13 +97,16 @@ class TestResult(pyunit.TestResult):
# Used when no todo provided to addExpectedFailure or addUnexpectedSuccess.
_DEFAULT_TODO = "Test expected to fail"
- skips: List[Tuple[itrial.ITestCase, str]]
- expectedFailures: List[Tuple[itrial.ITestCase, str, "Todo"]] # type: ignore[assignment]
- unexpectedSuccesses: List[Tuple[itrial.ITestCase, str]] # type: ignore[assignment]
+ errors: list[
+ tuple[itrial.ITestCase | pyunit.TestCase, str | Failure]
+ ] # type:ignore[assignment]
+ skips: list[tuple[itrial.ITestCase, str]]
+ expectedFailures: list[tuple[itrial.ITestCase, str | Failure, Todo]] # type: ignore[assignment]
+ unexpectedSuccesses: list[tuple[itrial.ITestCase, str]] # type: ignore[assignment]
successes: int
- _testStarted: Optional[int]
+ _testStarted: int | None
# The duration of the test. It is None until the test completes.
- _lastTime: Optional[int]
+ _lastTime: int | None
# Make pytest not think this is test class
__test__ = False
@@ -171,7 +174,13 @@ class TestResult(pyunit.TestResult):
"""
self.failures.append((test, self._getFailure(fail)))
- def addError(self, test, error):
+ def addError(
+ self,
+ test: pyunit.TestCase,
+ error: Failure
+ | tuple[type[BaseException], BaseException, TracebackType]
+ | tuple[None, None, None],
+ ) -> None:
"""
Report an error that occurred while running the given test.
@@ -253,9 +262,9 @@ class TestResult(pyunit.TestResult):
"""
-@implementer(itrial.IReporter)
+@implementer(itrial.IReporterWithDurations)
class TestResultDecorator(
- proxyForInterface(itrial.IReporter, "_originalReporter") # type: ignore[misc]
+ proxyForInterface(itrial.IReporterWithDurations, "_originalReporter") # type: ignore[misc]
):
"""
Base class for TestResult decorators.
diff --git a/contrib/python/Twisted/py3/twisted/trial/runner.py b/contrib/python/Twisted/py3/twisted/trial/runner.py
index ffc554e25c8..83b58e42fe4 100644
--- a/contrib/python/Twisted/py3/twisted/trial/runner.py
+++ b/contrib/python/Twisted/py3/twisted/trial/runner.py
@@ -7,7 +7,7 @@ A miscellany of code used to run Trial tests.
Maintainer: Jonathan Lange
"""
-
+from __future__ import annotations
__all__ = [
"TestSuite",
@@ -35,14 +35,15 @@ import sys
import types
import unittest as pyunit
import warnings
+from collections.abc import Generator
from contextlib import contextmanager
from importlib.machinery import SourceFileLoader
-from typing import Callable, Generator, List, Optional, TextIO, Type, Union
+from typing import Callable, Protocol, TextIO, Union
from zope.interface import implementer
from attrs import define
-from typing_extensions import ParamSpec, Protocol, TypeAlias, TypeGuard
+from typing_extensions import ParamSpec, TypeAlias, TypeGuard
from twisted.internet import defer
from twisted.python import failure, filepath, log, modules, reflect
@@ -275,7 +276,7 @@ _Loadable: TypeAlias = Union[
modules.PythonAttribute,
modules.PythonModule,
pyunit.TestCase,
- Type[pyunit.TestCase],
+ type[pyunit.TestCase],
]
@@ -301,7 +302,7 @@ def name(thing: _Loadable) -> str:
raise TypeError(f"Cannot name {thing!r}")
-def isTestCase(obj: type) -> TypeGuard[Type[pyunit.TestCase]]:
+def isTestCase(obj: type) -> TypeGuard[type[pyunit.TestCase]]:
"""
@return: C{True} if C{obj} is a class that contains test cases, C{False}
otherwise. Used to find all the tests in a module.
@@ -413,7 +414,7 @@ class TestLoader:
methodPrefix = "test"
modulePrefix = "test_"
- suiteFactory: Type[TestSuite] = TestSuite
+ suiteFactory: type[TestSuite] = TestSuite
sorter: Callable[[_Loadable], object] = name
def sort(self, xs):
@@ -714,7 +715,7 @@ class TestLoader:
loadTestsFromName = loadByName
- def loadByNames(self, names: List[str], recurse: bool = False) -> TestSuite:
+ def loadByNames(self, names: list[str], recurse: bool = False) -> TestSuite:
"""
Load some tests by a list of names.
@@ -789,7 +790,7 @@ def _qualNameWalker(qualName):
@contextmanager
-def _testDirectory(workingDirectory: str) -> Generator[None, None, None]:
+def _testDirectory(workingDirectory: str) -> Generator[None]:
"""
A context manager which obtains a lock on a trial working directory
and enters (L{os.chdir}) it and then reverses these things.
@@ -809,7 +810,7 @@ def _testDirectory(workingDirectory: str) -> Generator[None, None, None]:
@contextmanager
-def _logFile(logfile: str) -> Generator[None, None, None]:
+def _logFile(logfile: str) -> Generator[None]:
"""
A context manager which adds a log observer and then removes it.
@@ -834,11 +835,11 @@ def _logFile(logfile: str) -> Generator[None, None, None]:
class _Runner(Protocol):
stream: TextIO
- def run(self, test: Union[pyunit.TestCase, pyunit.TestSuite]) -> itrial.IReporter:
+ def run(self, test: pyunit.TestCase | pyunit.TestSuite) -> itrial.IReporter:
...
def runUntilFailure(
- self, test: Union[pyunit.TestCase, pyunit.TestSuite]
+ self, test: pyunit.TestCase | pyunit.TestSuite
) -> itrial.IReporter:
...
@@ -889,7 +890,7 @@ class TrialRunner:
DRY_RUN = "dry-run"
reporterFactory: Callable[[TextIO, str, bool, log.LogPublisher], itrial.IReporter]
- mode: Optional[str] = None
+ mode: str | None = None
logfile: str = "test.log"
stream: TextIO = sys.stdout
profile: bool = False
@@ -898,7 +899,7 @@ class TrialRunner:
uncleanWarnings: bool = False
workingDirectory: str = "_trial_temp"
_forceGarbageCollection: bool = False
- debugger: Optional[_Debugger] = None
+ debugger: _Debugger | None = None
_exitFirst: bool = False
_log: log.LogPublisher = log # type: ignore[assignment]
@@ -921,7 +922,7 @@ class TrialRunner:
def rterrors(self) -> bool:
return self._realTimeErrors
- def run(self, test: Union[pyunit.TestCase, pyunit.TestSuite]) -> itrial.IReporter:
+ def run(self, test: pyunit.TestCase | pyunit.TestSuite) -> itrial.IReporter:
"""
Run the test or suite and return a result object.
"""
@@ -934,7 +935,7 @@ class TrialRunner:
def _runWithoutDecoration(
self,
- test: Union[pyunit.TestCase, pyunit.TestSuite],
+ test: pyunit.TestCase | pyunit.TestSuite,
forceGarbageCollection: bool = False,
) -> itrial.IReporter:
"""
@@ -964,7 +965,7 @@ class TrialRunner:
return result
def runUntilFailure(
- self, test: Union[pyunit.TestCase, pyunit.TestSuite]
+ self, test: pyunit.TestCase | pyunit.TestSuite
) -> itrial.IReporter:
"""
Repeatedly run C{test} until it fails.
diff --git a/contrib/python/Twisted/py3/twisted/web/_abnf.py b/contrib/python/Twisted/py3/twisted/web/_abnf.py
index 8029943beab..50822417bca 100644
--- a/contrib/python/Twisted/py3/twisted/web/_abnf.py
+++ b/contrib/python/Twisted/py3/twisted/web/_abnf.py
@@ -5,6 +5,7 @@
"""
Tools for pedantically processing the HTTP protocol.
"""
+from __future__ import annotations
def _istoken(b: bytes) -> bool:
@@ -42,7 +43,7 @@ def _decint(data: bytes) -> int:
return int(data)
-def _ishexdigits(b: bytes) -> bool:
+def _ishexdigits(b: bytes | bytearray) -> bool:
"""
Is the string case-insensitively hexidecimal?
@@ -55,7 +56,7 @@ def _ishexdigits(b: bytes) -> bool:
return b != b""
-def _hexint(b: bytes) -> int:
+def _hexint(b: bytes | bytearray) -> int:
"""
Decode a hexadecimal integer.
diff --git a/contrib/python/Twisted/py3/twisted/web/_element.py b/contrib/python/Twisted/py3/twisted/web/_element.py
index 81d724071e4..369a27df3ab 100644
--- a/contrib/python/Twisted/py3/twisted/web/_element.py
+++ b/contrib/python/Twisted/py3/twisted/web/_element.py
@@ -2,17 +2,10 @@
# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.
+from __future__ import annotations
+
import itertools
-from typing import (
- TYPE_CHECKING,
- Any,
- Callable,
- List,
- Optional,
- TypeVar,
- Union,
- overload,
-)
+from typing import TYPE_CHECKING, Any, Callable, TypeVar, overload
from zope.interface import implementer
@@ -70,7 +63,7 @@ class Expose:
@return: The first of C{funcObjs}.
"""
for fObj in itertools.chain([f], funcObjs):
- exposedThrough: List[Expose] = getattr(fObj, "exposedThrough", [])
+ exposedThrough: list[Expose] = getattr(fObj, "exposedThrough", [])
exposedThrough.append(self)
setattr(fObj, "exposedThrough", exposedThrough)
return f
@@ -84,7 +77,7 @@ class Expose:
@overload
def get(
self, instance: object, methodName: str, default: T
- ) -> Union[Callable[..., Any], T]:
+ ) -> Callable[..., Any] | T:
...
def get(
@@ -166,15 +159,15 @@ class Element:
return from C{render}.
"""
- loader: Optional[ITemplateLoader] = None
+ loader: ITemplateLoader | None = None
- def __init__(self, loader: Optional[ITemplateLoader] = None):
+ def __init__(self, loader: ITemplateLoader | None = None):
if loader is not None:
self.loader = loader
def lookupRenderMethod(
self, name: str
- ) -> Callable[[Optional[IRequest], "Tag"], "Flattenable"]:
+ ) -> Callable[[IRequest | None, Tag], Flattenable]:
"""
Look up and return the named render method.
"""
@@ -183,7 +176,7 @@ class Element:
raise MissingRenderMethod(self, name)
return method
- def render(self, request: Optional[IRequest]) -> "Flattenable":
+ def render(self, request: IRequest | None) -> Flattenable:
"""
Implement L{IRenderable} to allow one L{Element} to be embedded in
another's template or rendering output.
diff --git a/contrib/python/Twisted/py3/twisted/web/_flatten.py b/contrib/python/Twisted/py3/twisted/web/_flatten.py
index 12691b87fa8..5ba9fc42c33 100644
--- a/contrib/python/Twisted/py3/twisted/web/_flatten.py
+++ b/contrib/python/Twisted/py3/twisted/web/_flatten.py
@@ -8,25 +8,13 @@ complex or arbitrarily nested, as strings.
"""
from __future__ import annotations
+from collections.abc import Coroutine, Generator, Mapping, Sequence
from inspect import iscoroutine
from io import BytesIO
from sys import exc_info
from traceback import extract_tb
-from types import GeneratorType
-from typing import (
- Any,
- Callable,
- Coroutine,
- Generator,
- List,
- Mapping,
- Optional,
- Sequence,
- Tuple,
- TypeVar,
- Union,
- cast,
-)
+from types import FrameType, GeneratorType
+from typing import Any, Callable, TypeVar, Union, cast
from twisted.internet.defer import Deferred, ensureDeferred
from twisted.python.compat import nativeString
@@ -51,8 +39,8 @@ Flattenable = Union[
CDATA,
Comment,
Tag,
- Tuple[FlattenableRecursive, ...],
- List[FlattenableRecursive],
+ tuple[FlattenableRecursive, ...],
+ list[FlattenableRecursive],
Generator[FlattenableRecursive, None, None],
CharRef,
Deferred[FlattenableRecursive],
@@ -68,7 +56,7 @@ Type alias containing all types that can be flattened by L{flatten()}.
BUFFER_SIZE = 2**16
-def escapeForContent(data: Union[bytes, str]) -> bytes:
+def escapeForContent(data: bytes | str) -> bytes:
"""
Escape some character or UTF-8 byte data for inclusion in an HTML or XML
document, by replacing metacharacters (C{&<>}) with their entity
@@ -87,7 +75,7 @@ def escapeForContent(data: Union[bytes, str]) -> bytes:
return data
-def attributeEscapingDoneOutside(data: Union[bytes, str]) -> bytes:
+def attributeEscapingDoneOutside(data: bytes | str) -> bytes:
"""
Escape some character or UTF-8 byte data for inclusion in the top level of
an attribute. L{attributeEscapingDoneOutside} actually passes the data
@@ -107,7 +95,7 @@ def attributeEscapingDoneOutside(data: Union[bytes, str]) -> bytes:
def writeWithAttributeEscaping(
- write: Callable[[bytes], object]
+ write: Callable[[bytes], object],
) -> Callable[[bytes], None]:
"""
Decorate a C{write} callable so that all output written is properly quoted
@@ -153,7 +141,7 @@ def writeWithAttributeEscaping(
return _write
-def escapedCDATA(data: Union[bytes, str]) -> bytes:
+def escapedCDATA(data: bytes | str) -> bytes:
"""
Escape CDATA for inclusion in a document.
@@ -167,7 +155,7 @@ def escapedCDATA(data: Union[bytes, str]) -> bytes:
return data.replace(b"]]>", b"]]]]><![CDATA[>")
-def escapedComment(data: Union[bytes, str]) -> bytes:
+def escapedComment(data: bytes | str) -> bytes:
"""
Within comments the sequence C{-->} can be mistaken as the end of the comment.
To ensure consistent parsing and valid output the sequence is replaced with C{--&gt;}.
@@ -189,8 +177,8 @@ def escapedComment(data: Union[bytes, str]) -> bytes:
def _getSlotValue(
name: str,
- slotData: Sequence[Optional[Mapping[str, Flattenable]]],
- default: Optional[Flattenable] = None,
+ slotData: Sequence[Mapping[str, Flattenable] | None],
+ default: Flattenable | None = None,
) -> Flattenable:
"""
Find the value of the named slot in the given stack of slot data.
@@ -224,16 +212,16 @@ def _fork(d: Deferred[T]) -> Deferred[T]:
def _flattenElement(
- request: Optional[IRequest],
+ request: IRequest | None,
root: Flattenable,
write: Callable[[bytes], object],
- slotData: List[Optional[Mapping[str, Flattenable]]],
- renderFactory: Optional[IRenderable],
- dataEscaper: Callable[[Union[bytes, str]], bytes],
+ slotData: list[Mapping[str, Flattenable] | None],
+ renderFactory: IRenderable | None,
+ dataEscaper: Callable[[bytes | str], bytes],
# This is annotated as Generator[T, None, None] instead of Iterator[T]
# because mypy does not consider an Iterator to be an instance of
# GeneratorType.
-) -> Generator[Union[Generator[Any, Any, Any], Deferred[Flattenable]], None, None]:
+) -> Generator[Generator[Any, Any, Any] | Deferred[Flattenable], None, None]:
"""
Make C{root} slightly more flat by yielding all its immediate contents as
strings, deferreds or generators that are recursive calls to itself.
@@ -272,10 +260,10 @@ def _flattenElement(
def keepGoing(
newRoot: Flattenable,
- dataEscaper: Callable[[Union[bytes, str]], bytes] = dataEscaper,
- renderFactory: Optional[IRenderable] = renderFactory,
+ dataEscaper: Callable[[bytes | str], bytes] = dataEscaper,
+ renderFactory: IRenderable | None = renderFactory,
write: Callable[[bytes], object] = write,
- ) -> Generator[Union[Flattenable, Deferred[Flattenable]], None, None]:
+ ) -> Generator[Flattenable | Deferred[Flattenable], None, None]:
return _flattenElement(
request, newRoot, write, slotData, renderFactory, dataEscaper
)
@@ -369,7 +357,7 @@ def _flattenElement(
async def _flattenTree(
- request: Optional[IRequest], root: Flattenable, write: Callable[[bytes], object]
+ request: IRequest | None, root: Flattenable, write: Callable[[bytes], object]
) -> None:
"""
Make C{root} into an iterable of L{bytes} and L{Deferred} by doing a depth
@@ -412,7 +400,7 @@ async def _flattenTree(
del buf[:]
bufSize = 0
- stack: List[Generator[Any, Any, Any]] = [
+ stack: list[Generator[Any, Any, Any]] = [
_flattenElement(request, root, bufferedWrite, [], None, escapeForContent)
]
@@ -429,8 +417,12 @@ async def _flattenTree(
except Exception as e:
roots = []
for generator in stack:
- if generator.gi_frame is not None:
- roots.append(generator.gi_frame.f_locals["root"])
+ generatorFrame: FrameType = (
+ # FIXME: typeshed bug?
+ generator.gi_frame # type:ignore[attr-defined]
+ )
+ if generatorFrame is not None:
+ roots.append(generatorFrame.f_locals["root"])
stack.pop()
raise FlattenerError(e, roots, extract_tb(exc_info()[2]))
else:
@@ -441,7 +433,7 @@ async def _flattenTree(
def flatten(
- request: Optional[IRequest], root: Flattenable, write: Callable[[bytes], object]
+ request: IRequest | None, root: Flattenable, write: Callable[[bytes], object]
) -> Deferred[None]:
"""
Incrementally write out a string representation of C{root} using C{write}.
@@ -468,7 +460,7 @@ def flatten(
return ensureDeferred(_flattenTree(request, root, write))
-def flattenString(request: Optional[IRequest], root: Flattenable) -> Deferred[bytes]:
+def flattenString(request: IRequest | None, root: Flattenable) -> Deferred[bytes]:
"""
Collate a string representation of C{root} into a single string.
diff --git a/contrib/python/Twisted/py3/twisted/web/_http2.py b/contrib/python/Twisted/py3/twisted/web/_http2.py
index 301e9ea196b..c42f2da5b2d 100644
--- a/contrib/python/Twisted/py3/twisted/web/_http2.py
+++ b/contrib/python/Twisted/py3/twisted/web/_http2.py
@@ -16,7 +16,6 @@ it has stabilised, it'll be made public.
import io
from collections import deque
-from typing import List
from zope.interface import implementer
@@ -45,7 +44,7 @@ from twisted.python.failure import Failure
from twisted.web.error import ExcessiveBufferingError
# This API is currently considered private.
-__all__: List[str] = []
+__all__: list[str] = []
_END_STREAM_SENTINEL = object()
@@ -63,7 +62,7 @@ class H2Connection(Protocol, TimeoutMixin):
interface between the two objects that allows them to work hand-in-hand here.
@ivar conn: The HTTP/2 connection state machine.
- @type conn: L{h2.connection.H2Connection}
+ @type conn: C{h2.connection.H2Connection}
@ivar streams: A mapping of stream IDs to L{H2Stream} objects, used to call
specific methods on streams when events occur.
@@ -437,7 +436,7 @@ class H2Connection(Protocol, TimeoutMixin):
@param event: The Hyper-h2 event that encodes information about the
received request.
- @type event: L{h2.events.RequestReceived}
+ @type event: C{h2.events.RequestReceived}
"""
stream = H2Stream(
event.stream_id,
@@ -469,7 +468,7 @@ class H2Connection(Protocol, TimeoutMixin):
@param event: The Hyper-h2 event that encodes information about the
received data.
- @type event: L{h2.events.DataReceived}
+ @type event: C{h2.events.DataReceived}
"""
stream = self.streams[event.stream_id]
stream.receiveDataChunk(event.data, event.flow_controlled_length)
@@ -481,7 +480,7 @@ class H2Connection(Protocol, TimeoutMixin):
@param event: The Hyper-h2 event that encodes information about the
completed stream.
- @type event: L{h2.events.StreamEnded}
+ @type event: C{h2.events.StreamEnded}
"""
stream = self.streams[event.stream_id]
stream.requestComplete()
@@ -492,7 +491,7 @@ class H2Connection(Protocol, TimeoutMixin):
@param event: The Hyper-h2 event that encodes information about the
reset stream.
- @type event: L{h2.events.StreamReset}
+ @type event: C{h2.events.StreamReset}
"""
stream = self.streams[event.stream_id]
stream.connectionLost(
@@ -506,7 +505,7 @@ class H2Connection(Protocol, TimeoutMixin):
@param event: The Hyper-h2 event that encodes information about the
stream reprioritization.
- @type event: L{h2.events.PriorityUpdated}
+ @type event: C{h2.events.PriorityUpdated}
"""
try:
self.priority.reprioritize(
@@ -659,7 +658,7 @@ class H2Connection(Protocol, TimeoutMixin):
@param event: The Hyper-h2 event that encodes information about the
flow control window change.
- @type event: L{h2.events.WindowUpdated}
+ @type event: C{h2.events.WindowUpdated}
"""
streamID = event.stream_id
diff --git a/contrib/python/Twisted/py3/twisted/web/_newclient.py b/contrib/python/Twisted/py3/twisted/web/_newclient.py
index 9ae0a0c2ecd..e9fa5673ee7 100644
--- a/contrib/python/Twisted/py3/twisted/web/_newclient.py
+++ b/contrib/python/Twisted/py3/twisted/web/_newclient.py
@@ -29,7 +29,7 @@ Various other classes in this module support this usage:
from __future__ import annotations
import re
-from typing import TYPE_CHECKING, Optional
+from typing import TYPE_CHECKING
from zope.interface import implementer
@@ -606,7 +606,7 @@ def _ensureValidURI(uri):
raise ValueError(f"Invalid URI {uri!r}")
-def _contentLength(connHeaders: Headers) -> Optional[int]:
+def _contentLength(connHeaders: Headers) -> int | None:
"""
Parse the I{Content-Length} connection header.
diff --git a/contrib/python/Twisted/py3/twisted/web/_stan.py b/contrib/python/Twisted/py3/twisted/web/_stan.py
index b165bdb6fda..4075e1dd349 100644
--- a/contrib/python/Twisted/py3/twisted/web/_stan.py
+++ b/contrib/python/Twisted/py3/twisted/web/_stan.py
@@ -21,9 +21,10 @@ cumbersome.
output.
"""
+from __future__ import annotations
from inspect import iscoroutine, isgenerator
-from typing import TYPE_CHECKING, Dict, List, Optional, Union
+from typing import TYPE_CHECKING
from warnings import warn
import attr
@@ -45,12 +46,12 @@ class slot:
The key which must be used in L{Tag.fillSlots} to fill it.
"""
- children: List["Tag"] = attr.ib(init=False, factory=list)
+ children: list[Tag] = attr.ib(init=False, factory=list)
"""
The L{Tag} objects included in this L{slot}'s template.
"""
- default: Optional["Flattenable"] = None
+ default: Flattenable | None = None
"""
The default contents of this slot, if it is left unfilled.
@@ -58,14 +59,14 @@ class slot:
L{None} actually being used.
"""
- filename: Optional[str] = None
+ filename: str | None = None
"""
The name of the XML file from which this tag was parsed.
If it was not parsed from an XML file, L{None}.
"""
- lineNumber: Optional[int] = None
+ lineNumber: int | None = None
"""
The line number on which this tag was encountered in the XML file
from which it was parsed.
@@ -73,7 +74,7 @@ class slot:
If it was not parsed from an XML file, L{None}.
"""
- columnNumber: Optional[int] = None
+ columnNumber: int | None = None
"""
The column number at which this tag was encountered in the XML file
from which it was parsed.
@@ -92,20 +93,20 @@ class Tag:
using pure python syntax.
"""
- tagName: Union[bytes, str]
+ tagName: bytes | str
"""
The name of the represented element.
For a tag like C{<div></div>}, this would be C{"div"}.
"""
- attributes: Dict[Union[bytes, str], "Flattenable"] = attr.ib(factory=dict)
+ attributes: dict[bytes | str, Flattenable] = attr.ib(factory=dict)
"""The attributes of the element."""
- children: List["Flattenable"] = attr.ib(factory=list)
+ children: list[Flattenable] = attr.ib(factory=list)
"""The contents of this C{Tag}."""
- render: Optional[str] = None
+ render: str | None = None
"""
The name of the render method to use for this L{Tag}.
@@ -115,14 +116,14 @@ class Tag:
to determine which method to call.
"""
- filename: Optional[str] = None
+ filename: str | None = None
"""
The name of the XML file from which this tag was parsed.
If it was not parsed from an XML file, L{None}.
"""
- lineNumber: Optional[int] = None
+ lineNumber: int | None = None
"""
The line number on which this tag was encountered in the XML file
from which it was parsed.
@@ -130,7 +131,7 @@ class Tag:
If it was not parsed from an XML file, L{None}.
"""
- columnNumber: Optional[int] = None
+ columnNumber: int | None = None
"""
The column number at which this tag was encountered in the XML file
from which it was parsed.
@@ -138,7 +139,7 @@ class Tag:
If it was not parsed from an XML file, L{None}.
"""
- slotData: Optional[Dict[str, "Flattenable"]] = attr.ib(init=False, default=None)
+ slotData: dict[str, Flattenable] | None = attr.ib(init=False, default=None)
"""
The data which can fill slots.
@@ -147,7 +148,7 @@ class Tag:
the child of a L{Tag}: strings, lists, L{Tag}s, generators, etc.
"""
- def fillSlots(self, **slots: "Flattenable") -> "Tag":
+ def fillSlots(self, **slots: Flattenable) -> Tag:
"""
Remember the slots provided at this position in the DOM.
@@ -162,7 +163,7 @@ class Tag:
self.slotData.update(slots)
return self
- def __call__(self, *children: "Flattenable", **kw: "Flattenable") -> "Tag":
+ def __call__(self, *children: Flattenable, **kw: Flattenable) -> Tag:
"""
Add children and change attributes on this tag.
@@ -203,7 +204,7 @@ class Tag:
self.attributes[k] = v
return self
- def _clone(self, obj: "Flattenable", deep: bool) -> "Flattenable":
+ def _clone(self, obj: Flattenable, deep: bool) -> Flattenable:
"""
Clone a C{Flattenable} object; used by L{Tag.clone}.
@@ -242,7 +243,7 @@ class Tag:
else:
return obj
- def clone(self, deep: bool = True) -> "Tag":
+ def clone(self, deep: bool = True) -> Tag:
"""
Return a clone of this tag. If deep is True, clone all of this tag's
children. Otherwise, just shallow copy the children list without copying
@@ -275,7 +276,7 @@ class Tag:
return newtag
- def clear(self) -> "Tag":
+ def clear(self) -> Tag:
"""
Clear any existing children from this tag.
"""
diff --git a/contrib/python/Twisted/py3/twisted/web/_template_util.py b/contrib/python/Twisted/py3/twisted/web/_template_util.py
index 501941ad121..89b2289e413 100644
--- a/contrib/python/Twisted/py3/twisted/web/_template_util.py
+++ b/contrib/python/Twisted/py3/twisted/web/_template_util.py
@@ -5,24 +5,15 @@
twisted.web.util and twisted.web.template merged to avoid cyclic deps
"""
+from __future__ import annotations
+
import io
import linecache
import warnings
from collections import OrderedDict
+from collections.abc import Mapping
from html import escape
-from typing import (
- IO,
- Any,
- AnyStr,
- Callable,
- Dict,
- List,
- Mapping,
- Optional,
- Tuple,
- Union,
- cast,
-)
+from typing import IO, Any, AnyStr, Callable, Union, cast
from xml.sax import handler, make_parser
from xml.sax.xmlreader import AttributesNSImpl, Locator
@@ -321,18 +312,18 @@ class _NSContext:
A mapping from XML namespaces onto their prefixes in the document.
"""
- def __init__(self, parent: Optional["_NSContext"] = None):
+ def __init__(self, parent: _NSContext | None = None):
"""
Pull out the parent's namespaces, if there's no parent then default to
XML.
"""
self.parent = parent
if parent is not None:
- self.nss: Dict[Optional[str], Optional[str]] = OrderedDict(parent.nss)
+ self.nss: dict[str | None, str | None] = OrderedDict(parent.nss)
else:
self.nss = {"http://www.w3.org/XML/1998/namespace": "xml"}
- def get(self, k: Optional[str], d: Optional[str] = None) -> Optional[str]:
+ def get(self, k: str | None, d: str | None = None) -> str | None:
"""
Get a prefix for a namespace.
@@ -340,13 +331,13 @@ class _NSContext:
"""
return self.nss.get(k, d)
- def __setitem__(self, k: Optional[str], v: Optional[str]) -> None:
+ def __setitem__(self, k: str | None, v: str | None) -> None:
"""
Proxy through to setting the prefix for the namespace.
"""
self.nss.__setitem__(k, v)
- def __getitem__(self, k: Optional[str]) -> Optional[str]:
+ def __getitem__(self, k: str | None) -> str | None:
"""
Proxy through to getting the prefix for the namespace.
"""
@@ -362,7 +353,7 @@ class _ToStan(handler.ContentHandler, handler.EntityResolver):
Document Object Model.
"""
- def __init__(self, sourceFilename: Optional[str]):
+ def __init__(self, sourceFilename: str | None):
"""
@param sourceFilename: the filename the XML was loaded out of.
"""
@@ -383,10 +374,10 @@ class _ToStan(handler.ContentHandler, handler.EntityResolver):
# Depending on our active context, the element type can be Tag, slot
# or str. Since mypy doesn't understand that context, it would be
# a pain to not use Any here.
- self.document: List[Any] = []
+ self.document: list[Any] = []
self.current = self.document
- self.stack: List[Any] = []
- self.xmlnsAttrs: List[Tuple[str, str]] = []
+ self.stack: list[Any] = []
+ self.xmlnsAttrs: list[tuple[str, str]] = []
def endDocument(self) -> None:
"""
@@ -398,7 +389,7 @@ class _ToStan(handler.ContentHandler, handler.EntityResolver):
Processing instructions are ignored.
"""
- def startPrefixMapping(self, prefix: Optional[str], uri: str) -> None:
+ def startPrefixMapping(self, prefix: str | None, uri: str) -> None:
"""
Set up the prefix mapping, which maps fully qualified namespace URIs
onto namespace prefixes.
@@ -420,7 +411,7 @@ class _ToStan(handler.ContentHandler, handler.EntityResolver):
else:
self.xmlnsAttrs.append(("xmlns:%s" % prefix, uri))
- def endPrefixMapping(self, prefix: Optional[str]) -> None:
+ def endPrefixMapping(self, prefix: str | None) -> None:
"""
"Pops the stack" on the prefix mapping.
@@ -432,8 +423,8 @@ class _ToStan(handler.ContentHandler, handler.EntityResolver):
def startElementNS(
self,
- namespaceAndName: Tuple[str, str],
- qname: Optional[str],
+ namespaceAndName: tuple[str | None, str],
+ qname: str | None,
attrs: AttributesNSImpl,
) -> None:
"""
@@ -455,7 +446,7 @@ class _ToStan(handler.ContentHandler, handler.EntityResolver):
if name == "transparent":
name = ""
elif name == "slot":
- default: Optional[str]
+ default: str | None
try:
# Try to get the default value for the slot
default = attrs[(None, "default")]
@@ -560,7 +551,7 @@ class _ToStan(handler.ContentHandler, handler.EntityResolver):
return
self.current.append(ch)
- def endElementNS(self, name: Tuple[str, str], qname: Optional[str]) -> None:
+ def endElementNS(self, name: tuple[str | None, str], qname: str | None) -> None:
"""
A namespace tag is closed. Pop the stack, if there's anything left in
it, otherwise return to the document's namespace.
@@ -604,7 +595,7 @@ class _ToStan(handler.ContentHandler, handler.EntityResolver):
self.current.append(Comment(content))
-def _flatsaxParse(fl: Union[IO[AnyStr], str]) -> List["Flattenable"]:
+def _flatsaxParse(fl: IO[AnyStr] | str) -> list[Flattenable]:
"""
Perform a SAX parse of an XML document with the _ToStan class.
@@ -634,7 +625,7 @@ class XMLString:
An L{ITemplateLoader} that loads and parses XML from a string.
"""
- def __init__(self, s: Union[str, bytes]):
+ def __init__(self, s: str | bytes):
"""
Run the parser on a L{io.StringIO} copy of the string.
@@ -644,10 +635,10 @@ class XMLString:
if not isinstance(s, str):
s = s.decode("utf8")
- self._loadedTemplate: List["Flattenable"] = _flatsaxParse(io.StringIO(s))
+ self._loadedTemplate: list[Flattenable] = _flatsaxParse(io.StringIO(s))
"""The loaded document."""
- def load(self) -> List["Flattenable"]:
+ def load(self) -> list[Flattenable]:
"""
Return the document.
@@ -810,15 +801,15 @@ class TagLoader:
An L{ITemplateLoader} that loads an existing flattenable object.
"""
- def __init__(self, tag: "Flattenable"):
+ def __init__(self, tag: Flattenable):
"""
@param tag: The object which will be loaded.
"""
- self.tag: "Flattenable" = tag
+ self.tag: Flattenable = tag
"""The object which will be loaded."""
- def load(self) -> List["Flattenable"]:
+ def load(self) -> list[Flattenable]:
return [self.tag]
@@ -842,13 +833,13 @@ class XMLFile:
stacklevel=2,
)
- self._loadedTemplate: Optional[List["Flattenable"]] = None
+ self._loadedTemplate: list[Flattenable] | None = None
"""The loaded document, or L{None}, if not loaded."""
self._path: FilePath[Any] = path
"""The file that is being loaded from."""
- def _loadDoc(self) -> List["Flattenable"]:
+ def _loadDoc(self) -> list[Flattenable]:
"""
Read and parse the XML.
@@ -863,7 +854,7 @@ class XMLFile:
def __repr__(self) -> str:
return f"<XMLFile of {self._path!r}>"
- def load(self) -> List["Flattenable"]:
+ def load(self) -> list[Flattenable]:
"""
Return the document, first loading it if necessary.
@@ -1032,8 +1023,8 @@ tags = _TagFactory()
def renderElement(
request: IRequest,
element: IRenderable,
- doctype: Optional[bytes] = b"<!DOCTYPE html>",
- _failElement: Optional[Callable[[Failure], "Element"]] = None,
+ doctype: bytes | None = b"<!DOCTYPE html>",
+ _failElement: Callable[[Failure], Element] | None = None,
) -> object:
"""
Render an element or other L{IRenderable}.
@@ -1058,7 +1049,7 @@ def renderElement(
d = flatten(request, element, request.write)
- def eb(failure: Failure) -> Optional[Deferred[None]]:
+ def eb(failure: Failure) -> Deferred[None] | None:
_moduleLog.failure(
"An error occurred while rendering the response.", failure=failure
)
diff --git a/contrib/python/Twisted/py3/twisted/web/_websocket_impl.py b/contrib/python/Twisted/py3/twisted/web/_websocket_impl.py
index 17ca57f7bf7..692fdd1efe8 100644
--- a/contrib/python/Twisted/py3/twisted/web/_websocket_impl.py
+++ b/contrib/python/Twisted/py3/twisted/web/_websocket_impl.py
@@ -169,7 +169,7 @@ class WebSocketProtocol(typing.Protocol):
A text message was received from the peer.
"""
- def bytesMessageReceived(self, data: bytes) -> None:
+ def bytesMessageReceived(self, data: bytes | bytearray) -> None:
"""
A bytes message was received from the peer.
"""
@@ -317,6 +317,8 @@ class _WebSocketWireProtocol(Generic[_WSP]):
_bootstrap: _Bootstrap
_wsp: _WSP
+ factory: ProtocolFactory[_WebSocketWireProtocol[_WSP]] = field(init=False)
+
# Public attribute.
transport: ITransport = field(init=False)
@@ -354,7 +356,8 @@ class _WebSocketWireProtocol(Generic[_WSP]):
def sendBytesMessage(self, data: bytes) -> None:
t = self.transport
assert t is not None
- t.write(self._wsconn.send(BytesMessage(data)))
+ message = BytesMessage(data)
+ t.write(self._wsconn.send(message))
def ping(self, payload: bytes = b"") -> None:
t = self.transport
diff --git a/contrib/python/Twisted/py3/twisted/web/client.py b/contrib/python/Twisted/py3/twisted/web/client.py
index d3cd11fb845..d3f25eb84ab 100644
--- a/contrib/python/Twisted/py3/twisted/web/client.py
+++ b/contrib/python/Twisted/py3/twisted/web/client.py
@@ -12,15 +12,17 @@ import collections
import os
import warnings
import zlib
+from collections.abc import Hashable, Iterable
from dataclasses import dataclass
from functools import wraps
from http.cookiejar import CookieJar
-from typing import TYPE_CHECKING, Hashable, Iterable, Optional
+from typing import TYPE_CHECKING, Callable, TypeVar
from urllib.parse import urldefrag, urljoin, urlunparse as _urlunparse
from zope.interface import implementer
from incremental import Version
+from typing_extensions import ParamSpec
from twisted.internet import defer, protocol, task
from twisted.internet.abstract import isIPv6Address
@@ -28,6 +30,7 @@ from twisted.internet.defer import Deferred
from twisted.internet.endpoints import HostnameEndpoint, wrapClientTLS
from twisted.internet.interfaces import (
IAddress,
+ IOpenSSLClientConnectionCreator,
IOpenSSLContextFactory,
IProtocol,
IReactorTime,
@@ -220,7 +223,10 @@ def _urljoin(base, url):
"""
base, baseFrag = urldefrag(base)
url, urlFrag = urldefrag(urljoin(base, url))
- return urljoin(url, b"#" + (urlFrag or baseFrag))
+ # We strip the hash to get test pass on Python 3.14
+ # Looks like a regression in 3.14
+ # See https://github.com/twisted/twisted/issues/12427
+ return urljoin(url, b"#" + (urlFrag or baseFrag)).strip(b"#")
def _makeGetterFactory(url, factoryFactory, contextFactory=None, *args, **kwargs):
@@ -283,8 +289,11 @@ else:
platformTrust,
)
+_P = ParamSpec("_P")
+_T = TypeVar("_T")
-def _requireSSL(decoratee):
+
+def _requireSSL(decoratee: Callable[_P, _T]) -> Callable[_P, _T]:
"""
The decorated method requires pyOpenSSL to be present, or it raises
L{NotImplementedError}.
@@ -298,8 +307,8 @@ def _requireSSL(decoratee):
"""
if SSL is None:
- @wraps(decoratee)
- def raiseNotImplemented(*a, **kw):
+ @wraps(decoratee) # type:ignore[unreachable]
+ def raiseNotImplemented(*a: _P.args, **kw: _P.kwargs) -> _T:
"""
pyOpenSSL is not available.
@@ -363,7 +372,9 @@ class BrowserLikePolicyForHTTPS:
self._trustRoot = trustRoot
@_requireSSL
- def creatorForNetloc(self, hostname, port):
+ def creatorForNetloc(
+ self, hostname: bytes, port: int
+ ) -> IOpenSSLClientConnectionCreator:
"""
Create a L{client connection creator
<twisted.internet.interfaces.IOpenSSLClientConnectionCreator>} for a
@@ -418,19 +429,21 @@ class HostnameCachingHTTPSPolicy:
@since: Twisted 19.2.0
"""
- def __init__(self, policyforHTTPS, cacheSize=20):
+ def __init__(self, policyforHTTPS: IPolicyForHTTPS, cacheSize: int = 20) -> None:
"""
@param policyforHTTPS: The IPolicyForHTTPS to wrap.
- @type policyforHTTPS: L{IPolicyForHTTPS}
@param cacheSize: The maximum size of the hostname cache.
- @type cacheSize: L{int}
"""
self._policyForHTTPS = policyforHTTPS
- self._cache = collections.OrderedDict()
+ self._cache: collections.OrderedDict[
+ str, IOpenSSLClientConnectionCreator
+ ] = collections.OrderedDict()
self._cacheSize = cacheSize
- def creatorForNetloc(self, hostname, port):
+ def creatorForNetloc(
+ self, hostname: bytes, port: int
+ ) -> IOpenSSLClientConnectionCreator:
"""
Create a L{client connection creator
<twisted.internet.interfaces.IOpenSSLClientConnectionCreator>} for a
@@ -664,7 +677,7 @@ class _HTTP11ClientFactory(protocol.Factory):
self._quiescentCallback, self._metadata
)
- def buildProtocol(self, addr: IAddress) -> HTTP11ClientProtocol:
+ def buildProtocol(self, addr: IAddress | None) -> HTTP11ClientProtocol:
return HTTP11ClientProtocol(self._quiescentCallback)
@@ -1171,8 +1184,8 @@ class Agent(_AgentBase):
self,
method: bytes,
uri: bytes,
- headers: Optional[Headers] = None,
- bodyProducer: Optional[IBodyProducer] = None,
+ headers: Headers | None = None,
+ bodyProducer: IBodyProducer | None = None,
) -> Deferred[IResponse]:
"""
Issue a request to the server indicated by the given C{uri}.
@@ -1244,7 +1257,7 @@ class ProxyAgent(_AgentBase):
class _FakeStdlibRequest(_RequestBase):
"""
- A fake L{urllib.request.Request} object for L{cookiejar} to work with.
+ A fake L{urllib.request.Request} object for L{http.cookiejar} to work with.
@see: U{urllib.request.Request
<https://docs.python.org/3/library/urllib.request.html#urllib.request.Request>}
@@ -1325,7 +1338,7 @@ class _FakeUrllibResponseInfo(_InfoType):
class _FakeStdlibResponse(_ResponseBase):
"""
- A fake L{urllib.response.Response} object for L{http.cookiejar} to work
+ A fake L{urllib.response.addinfourl} object for L{http.cookiejar} to work
with.
@ivar response: Underlying Twisted Web response.
@@ -1372,8 +1385,8 @@ class CookieAgent:
self,
method: bytes,
uri: bytes,
- headers: Optional[Headers] = None,
- bodyProducer: Optional[IBodyProducer] = None,
+ headers: Headers | None = None,
+ bodyProducer: IBodyProducer | None = None,
) -> Deferred[IResponse]:
"""
Issue a new request to the wrapped L{Agent}.
diff --git a/contrib/python/Twisted/py3/twisted/web/http.py b/contrib/python/Twisted/py3/twisted/web/http.py
index c2894fbd253..29ac7db00ea 100644
--- a/contrib/python/Twisted/py3/twisted/web/http.py
+++ b/contrib/python/Twisted/py3/twisted/web/http.py
@@ -113,15 +113,7 @@ from email import message_from_bytes
from email.message import EmailMessage, Message
from io import BufferedIOBase, BytesIO, TextIOWrapper
from time import gmtime, time
-from typing import (
- AnyStr,
- Callable,
- Dict,
- List,
- Optional,
- Protocol as TypingProtocol,
- Tuple,
-)
+from typing import AnyStr, Callable, Protocol as TypingProtocol
from urllib.parse import (
ParseResultBytes,
unquote_to_bytes as unquote,
@@ -142,7 +134,6 @@ from twisted.internet.interfaces import (
IReactorTime,
ITCPTransport,
)
-from twisted.internet.protocol import Protocol
from twisted.logger import Logger
from twisted.protocols import basic, policies
from twisted.python import log
@@ -527,7 +518,7 @@ def toChunk(data):
return (networkString(f"{len(data):x}"), b"\r\n", data, b"\r\n")
-def fromChunk(data: bytes) -> Tuple[bytes, bytes]:
+def fromChunk(data: bytes) -> tuple[bytes, bytes]:
"""
Convert chunk to string.
@@ -943,7 +934,7 @@ class Request:
URL-encoded body uploads into C{request.args}. This can use large
amounts of memory for large uploads.
"""
- self.notifications: List[Deferred[None]] = []
+ self.notifications: list[Deferred[None]] = []
self.channel = channel
# Cache the client and server information, we'll need this
@@ -953,9 +944,9 @@ class Request:
self.host = self.channel.getHost()
self.requestHeaders: Headers = Headers()
- self.received_cookies: Dict[bytes, bytes] = {}
+ self.received_cookies: dict[bytes, bytes] = {}
self.responseHeaders: Headers = Headers()
- self.cookies: List[bytes] = [] # outgoing cookies
+ self.cookies: list[bytes] = [] # outgoing cookies
self.transport = self.channel.transport
if queued is _QUEUED_SENTINEL:
@@ -1153,7 +1144,7 @@ class Request:
# The following is the public interface that people should be
# writing to.
- def getHeader(self, key: AnyStr) -> Optional[AnyStr]:
+ def getHeader(self, key: AnyStr) -> AnyStr | None:
"""
Get an HTTP request header.
@@ -1444,7 +1435,7 @@ class Request:
cookie += b"; SameSite=" + sameSite
self.cookies.append(cookie)
- def setResponseCode(self, code: int, message: Optional[bytes] = None) -> None:
+ def setResponseCode(self, code: int, message: bytes | None = None) -> None:
"""
Set the HTTP response code.
@@ -2001,7 +1992,7 @@ class _ChunkedTransferDecoder:
self.finishCallback = finishCallback
self._buffer = bytearray()
self._start = 0
- self._trailerHeaders: List[bytearray] = []
+ self._trailerHeaders: list[bytearray] = []
self._maxTrailerHeadersSize = 2**16
self._receivedTrailerHeadersSize = 0
@@ -2328,7 +2319,7 @@ class HTTPChannel(basic.LineReceiver, policies.TimeoutMixin):
totalHeadersSize = 16384
abortTimeout = 15
- length: Optional[int] = 0
+ length: int | None = 0
persistent = 1
__header = b""
__first_line = 1
@@ -2598,7 +2589,7 @@ class HTTPChannel(basic.LineReceiver, policies.TimeoutMixin):
L{HTTPChannel} from a custom data source, call C{dataReceived} on
it directly.
- @see: L{LineReceive.rawDataReceived}
+ @see: L{LineReceiver.rawDataReceived}
"""
if self._handlingRequest:
self._dataBuffer.append(data)
@@ -3115,16 +3106,15 @@ class _GenericHTTPChannelProtocol(proxyForInterface(IProtocol, "_channel")): #
A proxy object that wraps one of the HTTP protocol objects, and switches
between them depending on TLS negotiated protocol.
- @ivar _negotiatedProtocol: The protocol negotiated with ALPN or NPN, if
- any.
+ @ivar _negotiatedProtocol: The protocol negotiated with ALPN, if any.
@type _negotiatedProtocol: Either a bytestring containing the ALPN token
for the negotiated protocol, or L{None} if no protocol has yet been
negotiated.
@ivar _channel: The object capable of behaving like a L{HTTPChannel} that
- is backing this object. By default this is a L{HTTPChannel}, but if a
+ is backing this object. By default this is a L{HTTPChannel}, but if a
HTTP protocol upgrade takes place this may be a different channel
- object. Must implement L{IProtocol}.
+ object. Must implement L{IProtocol}.
@type _channel: L{HTTPChannel}
@ivar _requestFactory: A callable to use to build L{IRequest} objects.
@@ -3299,7 +3289,9 @@ class _GenericHTTPChannelProtocol(proxyForInterface(IProtocol, "_channel")): #
return self._channel.dataReceived(data)
-def _genericHTTPChannelProtocolFactory(self):
+def _genericHTTPChannelProtocolFactory(
+ self: HTTPFactory,
+) -> _GenericHTTPChannelProtocol:
"""
Returns an appropriately initialized _GenericHTTPChannelProtocol.
"""
@@ -3321,7 +3313,7 @@ class _MinimalLogFile(TypingProtocol):
value: type[_MinimalLogFile] = TextIOWrapper
-class HTTPFactory(protocol.ServerFactory):
+class HTTPFactory(protocol.ServerFactory[_GenericHTTPChannelProtocol]):
"""
Factory for HTTP server.
@@ -3348,7 +3340,7 @@ class HTTPFactory(protocol.ServerFactory):
# _genericHTTPChannelProtocolFactory is a callable which returns a proxy
# to a Protocol, instead of a concrete Protocol object, as expected in
# the protocol.Factory interface
- protocol = _genericHTTPChannelProtocolFactory # type: ignore[assignment]
+ protocol = _genericHTTPChannelProtocolFactory
logPath = None
_logFile: _MinimalLogFile | None = None
@@ -3381,7 +3373,7 @@ class HTTPFactory(protocol.ServerFactory):
reactor.
"""
if reactor is None:
- from twisted.internet import reactor # type:ignore[assignment]
+ from twisted.internet import reactor
self.reactor: IReactorTime = reactor # type:ignore[assignment]
if logPath is not None:
@@ -3418,7 +3410,7 @@ class HTTPFactory(protocol.ServerFactory):
def _set_logFile(self, newLogFile: BufferedIOBase | _MinimalLogFile) -> None:
if isinstance(newLogFile, BufferedIOBase):
newLogFile = TextIOWrapper(
- newLogFile, # type:ignore[arg-type]
+ newLogFile, # type:ignore[type-var]
"utf-8",
write_through=True,
newline="\n",
@@ -3434,8 +3426,10 @@ class HTTPFactory(protocol.ServerFactory):
self._logDateTime = datetimeToLogString(self.reactor.seconds())
self._logDateTimeCall = self.reactor.callLater(1, self._updateLogDateTime)
- def buildProtocol(self, addr: IAddress) -> Protocol | None:
- p = protocol.ServerFactory.buildProtocol(self, addr)
+ def buildProtocol(
+ self, addr: IAddress | None
+ ) -> _GenericHTTPChannelProtocol | None:
+ p = super().buildProtocol(addr)
# This is a bit of a hack to ensure that the HTTPChannel timeouts
# occur on the same reactor as the one we're using here. This could
diff --git a/contrib/python/Twisted/py3/twisted/web/http_headers.py b/contrib/python/Twisted/py3/twisted/web/http_headers.py
index 49e945744b4..0fa7b37646f 100644
--- a/contrib/python/Twisted/py3/twisted/web/http_headers.py
+++ b/contrib/python/Twisted/py3/twisted/web/http_headers.py
@@ -7,20 +7,8 @@ An API for storing HTTP header names and values.
"""
from __future__ import annotations
-from typing import (
- AnyStr,
- ClassVar,
- Dict,
- Iterator,
- List,
- Mapping,
- Optional,
- Sequence,
- Tuple,
- TypeVar,
- Union,
- overload,
-)
+from collections.abc import Iterator, Mapping, Sequence
+from typing import AnyStr, ClassVar, TypeVar, overload
from twisted.python.compat import cmp, comparable
from twisted.web._abnf import _istoken
@@ -70,9 +58,9 @@ class Headers:
def __init__(
self,
- rawHeaders: Optional[Mapping[AnyStr, Sequence[AnyStr]]] = None,
+ rawHeaders: Mapping[AnyStr, Sequence[AnyStr]] | None = None,
) -> None:
- self._rawHeaders: Dict[bytes, List[bytes]] = {}
+ self._rawHeaders: dict[bytes, list[bytes]] = {}
if rawHeaders is not None:
for name, values in rawHeaders.items():
self.setRawHeaders(name, values)
@@ -127,9 +115,7 @@ class Headers:
"""
self._rawHeaders.pop(_nameEncoder.encode(name), None)
- def setRawHeaders(
- self, name: Union[str, bytes], values: Sequence[Union[str, bytes]]
- ) -> None:
+ def setRawHeaders(self, name: str | bytes, values: Sequence[str | bytes]) -> None:
"""
Sets the raw representation of the given header.
@@ -144,7 +130,7 @@ class Headers:
@return: L{None}
"""
_name = _nameEncoder.encode(name)
- encodedValues: List[bytes] = []
+ encodedValues: list[bytes] = []
for v in values:
if isinstance(v, str):
_v = v.encode("utf8")
@@ -154,7 +140,7 @@ class Headers:
self._rawHeaders[_name] = encodedValues
- def addRawHeader(self, name: Union[str, bytes], value: Union[str, bytes]) -> None:
+ def addRawHeader(self, name: str | bytes, value: str | bytes) -> None:
"""
Add a new raw value for the given header.
@@ -169,16 +155,16 @@ class Headers:
)
@overload
- def getRawHeaders(self, name: AnyStr) -> Optional[Sequence[AnyStr]]:
+ def getRawHeaders(self, name: AnyStr) -> Sequence[AnyStr] | None:
...
@overload
- def getRawHeaders(self, name: AnyStr, default: _T) -> Union[Sequence[AnyStr], _T]:
+ def getRawHeaders(self, name: AnyStr, default: _T) -> Sequence[AnyStr] | _T:
...
def getRawHeaders(
- self, name: AnyStr, default: Optional[_T] = None
- ) -> Union[Sequence[AnyStr], Optional[_T]]:
+ self, name: AnyStr, default: _T | None = None
+ ) -> Sequence[AnyStr] | _T | None:
"""
Returns a sequence of headers matching the given name as the raw string
given.
@@ -200,7 +186,7 @@ class Headers:
return [v.decode("utf8") for v in values]
return values
- def getAllRawHeaders(self) -> Iterator[Tuple[bytes, Sequence[bytes]]]:
+ def getAllRawHeaders(self) -> Iterator[tuple[bytes, Sequence[bytes]]]:
"""
Return an iterator of key, value pairs of all headers contained in this
object, as L{bytes}. The keys are capitalized in canonical
@@ -223,9 +209,9 @@ class _NameEncoder:
"""
__slots__ = ("_canonicalHeaderCache",)
- _canonicalHeaderCache: Dict[Union[bytes, str], bytes]
+ _canonicalHeaderCache: dict[bytes | str, bytes]
- _caseMappings: ClassVar[Dict[bytes, bytes]] = {
+ _caseMappings: ClassVar[dict[bytes, bytes]] = {
b"Content-Md5": b"Content-MD5",
b"Dnt": b"DNT",
b"Etag": b"ETag",
@@ -240,7 +226,7 @@ class _NameEncoder:
def __init__(self):
self._canonicalHeaderCache = {}
- def encode(self, name: Union[str, bytes]) -> bytes:
+ def encode(self, name: str | bytes) -> bytes:
"""
Encode the name of a header (eg 'Content-Type') to an ISO-8859-1
bytestring if required. It will be canonicalized to Http-Header-Case.
diff --git a/contrib/python/Twisted/py3/twisted/web/iweb.py b/contrib/python/Twisted/py3/twisted/web/iweb.py
index 040b916c738..93b5ac99ba8 100644
--- a/contrib/python/Twisted/py3/twisted/web/iweb.py
+++ b/contrib/python/Twisted/py3/twisted/web/iweb.py
@@ -10,13 +10,15 @@ Interface definitions for L{twisted.web}.
body is not known in advance.
"""
-from typing import TYPE_CHECKING, Callable, List, Optional
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Callable
from zope.interface import Attribute, Interface
from twisted.cred.credentials import IUsernameDigestHash
from twisted.internet.defer import Deferred
-from twisted.internet.interfaces import IPushProducer
+from twisted.internet.interfaces import IOpenSSLClientConnectionCreator, IPushProducer
from twisted.web.http_headers import Headers
if TYPE_CHECKING:
@@ -514,7 +516,7 @@ class IRenderable(Interface):
def lookupRenderMethod(
name: str,
- ) -> Callable[[Optional[IRequest], "Tag"], "Flattenable"]:
+ ) -> Callable[[IRequest | None, Tag], Flattenable]:
"""
Look up and return the render method associated with the given name.
@@ -526,7 +528,7 @@ class IRenderable(Interface):
was encountered.
"""
- def render(request: Optional[IRequest]) -> "Flattenable":
+ def render(request: IRequest | None) -> Flattenable:
"""
Get the document for this L{IRenderable}.
@@ -543,7 +545,7 @@ class ITemplateLoader(Interface):
L{twisted.web.template.Element}'s C{loader} attribute.
"""
- def load() -> List["Flattenable"]:
+ def load() -> list[Flattenable]:
"""
Load a template suitable for rendering.
@@ -724,11 +726,23 @@ class IAgent(Interface):
doSomeRequests(cache)
"""
+ if not TYPE_CHECKING: # pragma: no branch
+
+ def __init__(self) -> None: # type:ignore
+ """
+ IAgent does not have any particular requirement upon its
+ constructor.
+ """
+ # This is a workaround for pydoctor bug
+ # https://github.com/twisted/pydoctor/issues/940
+
+ del __init__
+
def request(
method: bytes,
uri: bytes,
- headers: Optional[Headers] = None,
- bodyProducer: Optional[IBodyProducer] = None,
+ headers: Headers | None = None,
+ bodyProducer: IBodyProducer | None = None,
) -> Deferred[IResponse]:
"""
Request the resource at the given location.
@@ -769,7 +783,7 @@ class IPolicyForHTTPS(Interface):
@since: 14.0
"""
- def creatorForNetloc(hostname, port):
+ def creatorForNetloc(hostname: bytes, port: int) -> IOpenSSLClientConnectionCreator:
"""
Create a L{client connection creator
<twisted.internet.interfaces.IOpenSSLClientConnectionCreator>}
@@ -777,15 +791,11 @@ class IPolicyForHTTPS(Interface):
pair.
@param hostname: The name of the requested remote host.
- @type hostname: L{bytes}
@param port: The number of the requested remote port.
- @type port: L{int}
@return: A client connection creator expressing the security
requirements for the given remote host.
- @rtype: L{client connection creator
- <twisted.internet.interfaces.IOpenSSLClientConnectionCreator>}
"""
diff --git a/contrib/python/Twisted/py3/twisted/web/proxy.py b/contrib/python/Twisted/py3/twisted/web/proxy.py
index e31ec7a65d3..8b41478ab75 100644
--- a/contrib/python/Twisted/py3/twisted/web/proxy.py
+++ b/contrib/python/Twisted/py3/twisted/web/proxy.py
@@ -89,7 +89,7 @@ class ProxyClientFactory(ClientFactory):
"""
# Type is wrong. See: https://twistedmatrix.com/trac/ticket/10006
- protocol = ProxyClient # type: ignore[assignment]
+ protocol = ProxyClient
def __init__(self, command, rest, version, headers, data, father):
self.father = father
diff --git a/contrib/python/Twisted/py3/twisted/web/resource.py b/contrib/python/Twisted/py3/twisted/web/resource.py
index 56595d2995b..9c0e9de4ae5 100644
--- a/contrib/python/Twisted/py3/twisted/web/resource.py
+++ b/contrib/python/Twisted/py3/twisted/web/resource.py
@@ -20,7 +20,7 @@ __all__ = [
]
import warnings
-from typing import Sequence
+from collections.abc import Sequence
from zope.interface import Attribute, Interface, implementer
@@ -65,7 +65,7 @@ class IResource(Interface):
@type request: L{twisted.web.server.Request}
"""
- def putChild(path: bytes, child: "IResource") -> None:
+ def putChild(path: bytes, child: IResource) -> None:
"""
Put a child L{IResource} implementor at the given path.
diff --git a/contrib/python/Twisted/py3/twisted/web/server.py b/contrib/python/Twisted/py3/twisted/web/server.py
index 75344a07588..a1261ea283a 100644
--- a/contrib/python/Twisted/py3/twisted/web/server.py
+++ b/contrib/python/Twisted/py3/twisted/web/server.py
@@ -13,13 +13,14 @@ This is a web server which integrates with the twisted.internet infrastructure.
value.
"""
+from __future__ import annotations
+
import copy
import os
import re
import zlib
from binascii import hexlify
from html import escape
-from typing import List, Optional
from urllib.parse import quote as _quote
from zope.interface import implementer
@@ -87,11 +88,11 @@ class Request(Copyable, http.Request, components.Componentized):
will be transmitted only over HTTPS.
"""
- defaultContentType: Optional[bytes] = b"text/html"
+ defaultContentType: bytes | None = b"text/html"
site = None
appRootURL = None
- prepath: Optional[List[bytes]] = None
- postpath: Optional[List[bytes]] = None
+ prepath: list[bytes] | None = None
+ postpath: list[bytes] | None = None
__pychecker__ = "unusednames=issuer"
_inFakeHead = False
_encoder = None
diff --git a/contrib/python/Twisted/py3/twisted/web/static.py b/contrib/python/Twisted/py3/twisted/web/static.py
index aeffd03fb16..dbbd730b0c8 100644
--- a/contrib/python/Twisted/py3/twisted/web/static.py
+++ b/contrib/python/Twisted/py3/twisted/web/static.py
@@ -13,14 +13,14 @@ import mimetypes
import os
import time
import warnings
+from collections.abc import Sequence
from html import escape
-from typing import Any, Callable, Dict, Sequence
+from typing import Any, Callable, Literal
from urllib.parse import quote, unquote
from zope.interface import implementer
from incremental import Version
-from typing_extensions import Literal
from twisted.internet import abstract, interfaces
from twisted.python import components, filepath, log
@@ -200,7 +200,7 @@ class File(resource.Resource, filepath.FilePath[str]):
contentEncodings = {".gz": "gzip", ".bz2": "bzip2"}
- processors: Dict[str, Callable[[str, Any], Data]] = {}
+ processors: dict[str, Callable[[str, Any], Data]] = {}
indexNames = ["index", "index.html", "index.htm", "index.rpy"]
@@ -283,8 +283,7 @@ class File(resource.Resource, filepath.FilePath[str]):
If this L{File}"s path refers to a directory, return a L{File}
referring to the file named C{path} in that directory.
- If C{path} is the empty string, return a L{DirectoryLister}
- instead.
+ If C{path} is the empty string, return a L{DirectoryLister} instead.
@param path: The current path segment.
@type path: L{bytes}
@@ -292,9 +291,9 @@ class File(resource.Resource, filepath.FilePath[str]):
@param request: The incoming request.
@type request: An that provides L{twisted.web.iweb.IRequest}.
- @return: A resource representing the requested file or
- directory, or L{NoResource} if the path cannot be
- accessed.
+ @return: A resource representing the requested file or directory, or a
+ resource returning a NOT_FOUND error to clients if the path cannot
+ be accessed.
@rtype: An object that provides L{resource.IResource}.
"""
if isinstance(path, bytes):
diff --git a/contrib/python/Twisted/py3/twisted/web/test/requesthelper.py b/contrib/python/Twisted/py3/twisted/web/test/requesthelper.py
index d5c8918b302..74f1e4bb5ef 100644
--- a/contrib/python/Twisted/py3/twisted/web/test/requesthelper.py
+++ b/contrib/python/Twisted/py3/twisted/web/test/requesthelper.py
@@ -10,7 +10,6 @@ from __future__ import annotations
__all__ = ["DummyChannel", "DummyRequest"]
from io import BytesIO
-from typing import Dict, List, Optional
from zope.interface import implementer, verify
@@ -210,12 +209,12 @@ class DummyRequest:
uri = b"http://dummy/"
method = b"GET"
- client: Optional[IAddress] = None
- sitepath: List[bytes]
- written: List[bytes]
- prepath: List[bytes]
- args: Dict[bytes, List[bytes]]
- _finishedDeferreds: List[Deferred[None]]
+ client: IAddress | None = None
+ sitepath: list[bytes]
+ written: list[bytes]
+ prepath: list[bytes]
+ args: dict[bytes, list[bytes]]
+ _finishedDeferreds: list[Deferred[None]]
def registerProducer(self, prod, s):
"""
@@ -238,8 +237,8 @@ class DummyRequest:
def __init__(
self,
postpath: list[bytes],
- session: Optional[Session] = None,
- client: Optional[IAddress] = None,
+ session: Session | None = None,
+ client: IAddress | None = None,
) -> None:
self.sitepath = []
self.written = []
diff --git a/contrib/python/Twisted/py3/twisted/web/wsgi.py b/contrib/python/Twisted/py3/twisted/web/wsgi.py
index e979d30416e..0a3223f982b 100644
--- a/contrib/python/Twisted/py3/twisted/web/wsgi.py
+++ b/contrib/python/Twisted/py3/twisted/web/wsgi.py
@@ -6,9 +6,10 @@ An implementation of
U{Python Web Server Gateway Interface v1.0.1<http://www.python.org/dev/peps/pep-3333/>}.
"""
+from __future__ import annotations
+
from collections.abc import Sequence
from sys import exc_info
-from typing import List, Union
from warnings import warn
from zope.interface import implementer
@@ -34,7 +35,7 @@ from twisted.web.server import NOT_DONE_YET
#
# The following pair of functions -- _wsgiString() and _wsgiStringToBytes() --
# are used to make Twisted's WSGI support compliant with the standard.
-def _wsgiString(string: Union[str, bytes]) -> str:
+def _wsgiString(string: str | bytes) -> str:
"""
Convert C{string} to a WSGI "bytes-as-unicode" string.
@@ -100,7 +101,7 @@ class _ErrorStream:
# will overwrite this value if it is not properly formatted here.
self._log.error(data, system="wsgi", isError=True, message=(data,))
- def writelines(self, iovec: List[str]) -> None:
+ def writelines(self, iovec: list[str]) -> None:
"""
Join the given lines and pass them to C{write} to be handled in the
usual way.
diff --git a/contrib/python/Twisted/py3/twisted/words/im/basesupport.py b/contrib/python/Twisted/py3/twisted/words/im/basesupport.py
index 55519193d4e..8dee4f47df6 100644
--- a/contrib/python/Twisted/py3/twisted/words/im/basesupport.py
+++ b/contrib/python/Twisted/py3/twisted/words/im/basesupport.py
@@ -7,8 +7,6 @@
You will find these useful if you're adding a new protocol to IM.
"""
-from typing import Type
-
from twisted.internet import error
from twisted.internet.protocol import Protocol, connectionDone
from twisted.persisted import styles
@@ -95,7 +93,7 @@ class AbstractClientMixin:
@ivar _logonDeferred: Fired when I am done logging in.
"""
- _protoBase: Type[Protocol] = None # type: ignore[assignment]
+ _protoBase: type[Protocol] = None # type: ignore[assignment]
def __init__(self, account, chatui, logonDeferred):
for base in self.__class__.__bases__:
diff --git a/contrib/python/Twisted/py3/twisted/words/protocols/irc.py b/contrib/python/Twisted/py3/twisted/words/protocols/irc.py
index 2906fa5627a..f31f69cd4de 100644
--- a/contrib/python/Twisted/py3/twisted/words/protocols/irc.py
+++ b/contrib/python/Twisted/py3/twisted/words/protocols/irc.py
@@ -2851,7 +2851,7 @@ class DccSendProtocol(protocol.Protocol, styles.Ephemeral):
class DccSendFactory(protocol.Factory):
- protocol = DccSendProtocol # type: ignore[assignment]
+ protocol = DccSendProtocol
def __init__(self, file):
self.file = file
@@ -2963,7 +2963,7 @@ class DccChat(basic.LineReceiver, styles.Ephemeral):
class DccChatFactory(protocol.ClientFactory):
- protocol = DccChat # type: ignore[assignment]
+ protocol = DccChat
noisy = False
def __init__(self, client, queryData):
diff --git a/contrib/python/Twisted/py3/twisted/words/protocols/jabber/jid.py b/contrib/python/Twisted/py3/twisted/words/protocols/jabber/jid.py
index 52e154fee4f..13e10a6449f 100644
--- a/contrib/python/Twisted/py3/twisted/words/protocols/jabber/jid.py
+++ b/contrib/python/Twisted/py3/twisted/words/protocols/jabber/jid.py
@@ -11,8 +11,7 @@ parse string representations into them with proper checking for illegal
characters, case folding and canonicalisation through
L{stringprep<twisted.words.protocols.jabber.xmpp_stringprep>}.
"""
-
-from typing import Dict, Tuple, Union
+from __future__ import annotations
from twisted.words.protocols.jabber.xmpp_stringprep import (
nameprep,
@@ -27,7 +26,7 @@ class InvalidFormat(Exception):
"""
-def parse(jidstring: str) -> Tuple[Union[str, None], str, Union[str, None]]:
+def parse(jidstring: str) -> tuple[str | None, str, str | None]:
"""
Parse given JID string into its respective parts and apply stringprep.
@@ -75,8 +74,8 @@ def parse(jidstring: str) -> Tuple[Union[str, None], str, Union[str, None]]:
def prep(
- user: Union[str, None], host: str, resource: Union[str, None]
-) -> Tuple[Union[str, None], str, Union[str, None]]:
+ user: str | None, host: str, resource: str | None
+) -> tuple[str | None, str, str | None]:
"""
Perform stringprep on all JID fragments.
@@ -117,7 +116,7 @@ def prep(
return (user, host, resource)
-__internJIDs: Dict[str, "JID"] = {}
+__internJIDs: dict[str, JID] = {}
def internJID(jidstring):
@@ -145,8 +144,8 @@ class JID:
def __init__(
self,
- str: Union[str, None] = None,
- tuple: Union[Tuple[Union[str, None], str, Union[str, None]], None] = None,
+ str: str | None = None,
+ tuple: tuple[str | None, str, str | None] | None = None,
):
if str:
user, host, res = parse(str)
diff --git a/contrib/python/Twisted/py3/twisted/words/protocols/jabber/xmlstream.py b/contrib/python/Twisted/py3/twisted/words/protocols/jabber/xmlstream.py
index 601a879aa8b..a96e51e21af 100644
--- a/contrib/python/Twisted/py3/twisted/words/protocols/jabber/xmlstream.py
+++ b/contrib/python/Twisted/py3/twisted/words/protocols/jabber/xmlstream.py
@@ -21,12 +21,11 @@ Stanzas.
@var Reset: Token to signal that the XML stream has been reset.
@type Reset: Basic object.
"""
-
+from __future__ import annotations
from binascii import hexlify
from hashlib import sha1
from sys import intern
-from typing import Optional, Tuple
from zope.interface import directlyProvides, implementer
@@ -166,7 +165,7 @@ class ConnectAuthenticator(Authenticator):
Authenticator for initiating entities.
"""
- namespace: Optional[str] = None
+ namespace: str | None = None
def __init__(self, otherHost):
self.otherHost = otherHost
@@ -262,7 +261,7 @@ class ListenAuthenticator(Authenticator):
Authenticator for receiving entities.
"""
- namespace: Optional[str] = None
+ namespace: str | None = None
def associateWithStream(self, xmlstream):
"""
@@ -318,7 +317,7 @@ class BaseFeatureInitiatingInitializer:
@type required: C{bool}
"""
- feature: Optional[Tuple[str, str]] = None
+ feature: tuple[str, str] | None = None
def __init__(self, xs, required=False):
self.xmlstream = xs
@@ -678,7 +677,9 @@ class XmlStreamFactory(xmlstream.XmlStreamFactory):
self.authenticator = authenticator
-class XmlStreamServerFactory(xmlstream.BootstrapMixin, protocol.ServerFactory):
+class XmlStreamServerFactory(
+ xmlstream.BootstrapMixin, protocol.ServerFactory[XmlStream]
+):
"""
Factory for Jabber XmlStream objects as a server.
@@ -688,8 +689,7 @@ class XmlStreamServerFactory(xmlstream.BootstrapMixin, protocol.ServerFactory):
with the XmlStream.
"""
- # Type is wrong. See: https://twistedmatrix.com/trac/ticket/10007#ticket
- protocol = XmlStream # type: ignore[assignment]
+ protocol = XmlStream
def __init__(self, authenticatorFactory):
xmlstream.BootstrapMixin.__init__(self)
diff --git a/contrib/python/Twisted/py3/twisted/words/tap.py b/contrib/python/Twisted/py3/twisted/words/tap.py
index d83db71b76e..84018611ef5 100644
--- a/contrib/python/Twisted/py3/twisted/words/tap.py
+++ b/contrib/python/Twisted/py3/twisted/words/tap.py
@@ -4,10 +4,11 @@
"""
Shiny new words service maker
"""
+from __future__ import annotations
import socket
import sys
-from typing import List, Optional, Sequence
+from collections.abc import Sequence
from twisted import plugin
from twisted.application import strports
@@ -19,7 +20,7 @@ from twisted.words import iwords, service
class Options(usage.Options, strcred.AuthOptionMixin):
supportedInterfaces = [credentials.IUsernamePassword]
- optParameters: List[Sequence[Optional[str]]] = [
+ optParameters: list[Sequence[str | None]] = [
(
"hostname",
None,
diff --git a/contrib/python/Twisted/py3/ya.make b/contrib/python/Twisted/py3/ya.make
index 6d1f2879d56..796b6b51a28 100644
--- a/contrib/python/Twisted/py3/ya.make
+++ b/contrib/python/Twisted/py3/ya.make
@@ -2,7 +2,7 @@
PY3_LIBRARY()
-VERSION(25.5.0)
+VERSION(26.4.0)
LICENSE(MIT)
@@ -127,6 +127,7 @@ PY_SRCS(
twisted/internet/_posixstdio.py
twisted/internet/_producer_helpers.py
twisted/internet/_resolver.py
+ twisted/internet/_service_identity.py
twisted/internet/_signals.py
twisted/internet/_sslverify.py
twisted/internet/_threadedselect.py
@@ -273,6 +274,8 @@ PY_SRCS(
twisted/positioning/ipositioning.py
twisted/positioning/nmea.py
twisted/protocols/__init__.py
+ twisted/protocols/_sni.py
+ twisted/protocols/_tls_legacy.py
twisted/protocols/amp.py
twisted/protocols/basic.py
twisted/protocols/finger.py