diff options
| author | robot-piglet <[email protected]> | 2024-03-16 14:04:41 +0300 | 
|---|---|---|
| committer | robot-piglet <[email protected]> | 2024-03-16 14:15:11 +0300 | 
| commit | d83e7ea91cbc5e5d4ea49195eeab84c364dba4bb (patch) | |
| tree | 5ffdbed1c2a932a8d41089a6ac2766dd783ad15b /contrib/python/Twisted/py3/twisted | |
| parent | f7d1b63f8a2e18da4372628a2917fa0c508ba43a (diff) | |
Intermediate changes
Diffstat (limited to 'contrib/python/Twisted/py3/twisted')
63 files changed, 461 insertions, 305 deletions
| diff --git a/contrib/python/Twisted/py3/twisted/11715.misc b/contrib/python/Twisted/py3/twisted/11715.misc deleted file mode 100644 index e69de29bb2d..00000000000 --- a/contrib/python/Twisted/py3/twisted/11715.misc +++ /dev/null diff --git a/contrib/python/Twisted/py3/twisted/_version.py b/contrib/python/Twisted/py3/twisted/_version.py index 3f2476fe9ca..1d58c477de0 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", 23, 10, 0) +__version__ = Version("Twisted", 24, 3, 0)  __all__ = ["__version__"] diff --git a/contrib/python/Twisted/py3/twisted/application/internet.py b/contrib/python/Twisted/py3/twisted/application/internet.py index 194a4dd8a70..9e702f7e844 100644 --- a/contrib/python/Twisted/py3/twisted/application/internet.py +++ b/contrib/python/Twisted/py3/twisted/application/internet.py @@ -41,7 +41,7 @@ reactor.listen/connect* methods for more information.  from random import random as _goodEnoughRandom  from typing import List -from automat import MethodicalMachine  # type: ignore[import] +from automat import MethodicalMachine  from twisted.application import service  from twisted.internet import task diff --git a/contrib/python/Twisted/py3/twisted/application/runner/_runner.py b/contrib/python/Twisted/py3/twisted/application/runner/_runner.py index 17b21962ae6..01bbaeba1bd 100644 --- a/contrib/python/Twisted/py3/twisted/application/runner/_runner.py +++ b/contrib/python/Twisted/py3/twisted/application/runner/_runner.py @@ -12,7 +12,7 @@ from sys import stderr  from typing import Any, Callable, Mapping, TextIO  from attr import Factory, attrib, attrs -from constantly import NamedConstant  # type: ignore[import] +from constantly import NamedConstant  from twisted.internet.interfaces import IReactorCore  from twisted.logger import ( diff --git a/contrib/python/Twisted/py3/twisted/conch/unix.py b/contrib/python/Twisted/py3/twisted/conch/unix.py index e314e3cd72d..0696aeb1ac6 100644 --- a/contrib/python/Twisted/py3/twisted/conch/unix.py +++ b/contrib/python/Twisted/py3/twisted/conch/unix.py @@ -43,7 +43,7 @@ from twisted.python import components  from twisted.python.compat import nativeString  try: -    import utmp  # type: ignore[import] +    import utmp  except ImportError:      utmp = None diff --git a/contrib/python/Twisted/py3/twisted/copyright.py b/contrib/python/Twisted/py3/twisted/copyright.py index 9aed452d3be..848c7c2fb26 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-2023 Twisted Matrix Laboratories. +Copyright (c) 2001-2024 Twisted Matrix Laboratories.  See LICENSE for details."""  disclaimer = """ diff --git a/contrib/python/Twisted/py3/twisted/internet/_dumbwin32proc.py b/contrib/python/Twisted/py3/twisted/internet/_dumbwin32proc.py index 678f54e59b2..22eb26b1a49 100644 --- a/contrib/python/Twisted/py3/twisted/internet/_dumbwin32proc.py +++ b/contrib/python/Twisted/py3/twisted/internet/_dumbwin32proc.py @@ -12,16 +12,16 @@ import sys  from zope.interface import implementer -import pywintypes  # type: ignore[import] +import pywintypes  # Win32 imports -import win32api  # type: ignore[import] -import win32con  # type: ignore[import] -import win32event  # type: ignore[import] -import win32file  # type: ignore[import] -import win32pipe  # type: ignore[import] -import win32process  # type: ignore[import] -import win32security  # type: ignore[import] +import win32api +import win32con +import win32event +import win32file +import win32pipe +import win32process +import win32security  from twisted.internet import _pollingfile, error  from twisted.internet._baseprocess import BaseProcess diff --git a/contrib/python/Twisted/py3/twisted/internet/_glibbase.py b/contrib/python/Twisted/py3/twisted/internet/_glibbase.py index 4a6d1323ab9..587ec1bd05c 100644 --- a/contrib/python/Twisted/py3/twisted/internet/_glibbase.py +++ b/contrib/python/Twisted/py3/twisted/internet/_glibbase.py @@ -22,7 +22,7 @@ from twisted.internet.abstract import FileDescriptor  from twisted.internet.interfaces import IReactorFDSet, IReadDescriptor, IWriteDescriptor  from twisted.python import log  from twisted.python.monkey import MonkeyPatcher -from ._signals import _UnixWaker +from ._signals import _IWaker, _UnixWaker  def ensureNotImported(moduleNames, errorMessage, preventImports=[]): @@ -77,7 +77,7 @@ def _signalGlue():      reactor main loop which makes our signal handling work with glib's signal      handling.      """ -    from gi import _ossighelper as signalGlue  # type: ignore[import] +    from gi import _ossighelper as signalGlue      patcher = MonkeyPatcher()      patcher.addPatch(signalGlue, "_wakeup_fd_is_active", True) @@ -129,7 +129,7 @@ class GlibReactorBase(posixbase.PosixReactorBase, posixbase._PollLikeMixin):      # Install a waker that knows it needs to call C{_simulate} in order to run      # callbacks queued from a thread: -    def _wakerFactory(self) -> GlibWaker: +    def _wakerFactory(self) -> _IWaker:          return GlibWaker(self)      def __init__(self, glib_module: Any, gtk_module: Any, useGtk: bool = False) -> None: diff --git a/contrib/python/Twisted/py3/twisted/internet/_pollingfile.py b/contrib/python/Twisted/py3/twisted/internet/_pollingfile.py index 758a4cecb72..389182a69ad 100644 --- a/contrib/python/Twisted/py3/twisted/internet/_pollingfile.py +++ b/contrib/python/Twisted/py3/twisted/internet/_pollingfile.py @@ -95,10 +95,10 @@ class _PollingTimer:  # If we ever (let's hope not) need the above functionality on UNIX, this could  # be factored into a different module. -import pywintypes  # type: ignore[import] -import win32api  # type: ignore[import] -import win32file  # type: ignore[import] -import win32pipe  # type: ignore[import] +import pywintypes +import win32api +import win32file +import win32pipe  @implementer(IPushProducer) diff --git a/contrib/python/Twisted/py3/twisted/internet/_posixserialport.py b/contrib/python/Twisted/py3/twisted/internet/_posixserialport.py index 636aefa170f..7858362dda0 100644 --- a/contrib/python/Twisted/py3/twisted/internet/_posixserialport.py +++ b/contrib/python/Twisted/py3/twisted/internet/_posixserialport.py @@ -9,8 +9,7 @@ Serial Port Protocol  # dependent on pyserial ( http://pyserial.sf.net/ )  # only tested w/ 1.18 (5 Dec 2002) -from serial import PARITY_NONE  # type: ignore[import] -from serial import EIGHTBITS, STOPBITS_ONE +from serial import EIGHTBITS, PARITY_NONE, STOPBITS_ONE  from twisted.internet import abstract, fdesc  from twisted.internet.serialport import BaseSerialPort diff --git a/contrib/python/Twisted/py3/twisted/internet/_signals.py b/contrib/python/Twisted/py3/twisted/internet/_signals.py index fa878f6cbad..18793bfbba2 100644 --- a/contrib/python/Twisted/py3/twisted/internet/_signals.py +++ b/contrib/python/Twisted/py3/twisted/internet/_signals.py @@ -271,12 +271,12 @@ class _IWaker(Interface):      disconnected = Attribute("") -    def wakeUp(): +    def wakeUp() -> None:          """          Called when the event should be wake up.          """ -    def doRead(): +    def doRead() -> None:          """          Read some data from my connection and discard it.          """ diff --git a/contrib/python/Twisted/py3/twisted/internet/_sslverify.py b/contrib/python/Twisted/py3/twisted/internet/_sslverify.py index 552c30bbf0e..abb6bddf7c0 100644 --- a/contrib/python/Twisted/py3/twisted/internet/_sslverify.py +++ b/contrib/python/Twisted/py3/twisted/internet/_sslverify.py @@ -13,10 +13,10 @@ from typing import Dict  from zope.interface import Interface, implementer  from OpenSSL import SSL, crypto -from OpenSSL._util import lib as pyOpenSSLlib  # type: ignore[import] +from OpenSSL._util import lib as pyOpenSSLlib  import attr -from constantly import FlagConstant, Flags, NamedConstant, Names  # type: ignore[import] +from constantly import FlagConstant, Flags, NamedConstant, Names  from incremental import Version  from twisted.internet.abstract import isIPAddress, isIPv6Address diff --git a/contrib/python/Twisted/py3/twisted/internet/_win32serialport.py b/contrib/python/Twisted/py3/twisted/internet/_win32serialport.py index 2dda4b9816f..59874ed2f6b 100644 --- a/contrib/python/Twisted/py3/twisted/internet/_win32serialport.py +++ b/contrib/python/Twisted/py3/twisted/internet/_win32serialport.py @@ -10,13 +10,12 @@ Requires PySerial and pywin32.  """ -import win32event  # type: ignore[import] -import win32file  # type: ignore[import] +import win32event +import win32file  # system imports -from serial import PARITY_NONE  # type: ignore[import] -from serial import EIGHTBITS, STOPBITS_ONE -from serial.serialutil import to_bytes  # type: ignore[import] +from serial import EIGHTBITS, PARITY_NONE, STOPBITS_ONE +from serial.serialutil import to_bytes  # twisted imports  from twisted.internet import abstract diff --git a/contrib/python/Twisted/py3/twisted/internet/_win32stdio.py b/contrib/python/Twisted/py3/twisted/internet/_win32stdio.py index f1ac920f606..104d65f348c 100644 --- a/contrib/python/Twisted/py3/twisted/internet/_win32stdio.py +++ b/contrib/python/Twisted/py3/twisted/internet/_win32stdio.py @@ -10,7 +10,7 @@ import os  from zope.interface import implementer -import win32api  # type: ignore[import] +import win32api  from twisted.internet import _pollingfile, main  from twisted.internet.interfaces import ( diff --git a/contrib/python/Twisted/py3/twisted/internet/cfreactor.py b/contrib/python/Twisted/py3/twisted/internet/cfreactor.py index 142c0472ef4..f686092e69d 100644 --- a/contrib/python/Twisted/py3/twisted/internet/cfreactor.py +++ b/contrib/python/Twisted/py3/twisted/internet/cfreactor.py @@ -17,7 +17,7 @@ import sys  from zope.interface import implementer -from CFNetwork import (  # type: ignore[import] +from CFNetwork import (      CFSocketCreateRunLoopSource,      CFSocketCreateWithNative,      CFSocketDisableCallBacks, @@ -30,7 +30,7 @@ from CFNetwork import (  # type: ignore[import]      kCFSocketReadCallBack,      kCFSocketWriteCallBack,  ) -from CoreFoundation import (  # type: ignore[import] +from CoreFoundation import (      CFAbsoluteTimeGetCurrent,      CFRunLoopAddSource,      CFRunLoopAddTimer, diff --git a/contrib/python/Twisted/py3/twisted/internet/defer.py b/contrib/python/Twisted/py3/twisted/internet/defer.py index 17e717cad28..caafb52210e 100644 --- a/contrib/python/Twisted/py3/twisted/internet/defer.py +++ b/contrib/python/Twisted/py3/twisted/internet/defer.py @@ -17,7 +17,7 @@ from asyncio import AbstractEventLoop, Future, iscoroutine  from contextvars import Context as _Context, copy_context as _copy_context  from enum import Enum  from functools import wraps -from sys import exc_info +from sys import exc_info, implementation  from types import CoroutineType, GeneratorType, MappingProxyType, TracebackType  from typing import (      TYPE_CHECKING, @@ -58,6 +58,9 @@ 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):      """ @@ -1583,7 +1586,7 @@ class DeferredList(  # type: ignore[no-redef] # noqa:F811  def _parseDeferredListResult( -    resultList: List[_DeferredListResultItemT[_T]], fireOnOneErrback: bool = False +    resultList: List[_DeferredListResultItemT[_T]], fireOnOneErrback: bool = False, /  ) -> List[_T]:      if __debug__:          for result in resultList: @@ -1617,9 +1620,9 @@ def gatherResults(          is useful to prevent spurious 'Unhandled error in Deferred' messages          from being logged.  This parameter is available since 11.1.0.      """ -    d = DeferredList(deferredList, fireOnOneErrback=True, consumeErrors=consumeErrors) -    d.addCallback(_parseDeferredListResult) -    return cast(Deferred[List[_T]], d) +    return DeferredList( +        deferredList, fireOnOneErrback=True, consumeErrors=consumeErrors +    ).addCallback(_parseDeferredListResult)  class FailureGroup(Exception): @@ -2022,8 +2025,10 @@ def _inlineCallbacks(              appCodeTrace = traceback.tb_next              assert appCodeTrace is not None -            if _PYPY: -                # PyPy as of 3.7 adds an extra frame. +            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 @@ -2116,14 +2121,13 @@ def _addCancelCallbackToDeferred(      @param status: a L{_CancellationStatus} tracking the current status of C{gen}      """      it.callbacks, tmp = [], it.callbacks -    it.addErrback(_handleCancelInlineCallbacks, status) +    it = it.addErrback(_handleCancelInlineCallbacks, status)      it.callbacks.extend(tmp)      it.errback(_InternalInlineCallbacksCancelledError())  def _handleCancelInlineCallbacks( -    result: Failure, -    status: _CancellationStatus[_T], +    result: Failure, status: _CancellationStatus[_T], /  ) -> Deferred[_T]:      """      Propagate the cancellation of an C{@}L{inlineCallbacks} to the diff --git a/contrib/python/Twisted/py3/twisted/internet/endpoints.py b/contrib/python/Twisted/py3/twisted/internet/endpoints.py index 4a4cf55e8e9..7ab1d817319 100644 --- a/contrib/python/Twisted/py3/twisted/internet/endpoints.py +++ b/contrib/python/Twisted/py3/twisted/internet/endpoints.py @@ -22,7 +22,7 @@ from unicodedata import normalize  from zope.interface import directlyProvides, implementer, provider -from constantly import NamedConstant, Names  # type: ignore[import] +from constantly import NamedConstant, Names  from incremental import Version  from twisted.internet import defer, error, fdesc, interfaces, threads diff --git a/contrib/python/Twisted/py3/twisted/internet/gireactor.py b/contrib/python/Twisted/py3/twisted/internet/gireactor.py index e9c072f41a6..4a193a79ec4 100644 --- a/contrib/python/Twisted/py3/twisted/internet/gireactor.py +++ b/contrib/python/Twisted/py3/twisted/internet/gireactor.py @@ -23,7 +23,7 @@ On Python 3, pygobject v3.4 or later is required.  from typing import Union -from gi.repository import GLib  # type:ignore[import] +from gi.repository import GLib  from twisted.internet import _glibbase  from twisted.internet.error import ReactorAlreadyRunning diff --git a/contrib/python/Twisted/py3/twisted/internet/gtk2reactor.py b/contrib/python/Twisted/py3/twisted/internet/gtk2reactor.py index b4e0c4c4e1c..d0fc7440831 100644 --- a/contrib/python/Twisted/py3/twisted/internet/gtk2reactor.py +++ b/contrib/python/Twisted/py3/twisted/internet/gtk2reactor.py @@ -43,13 +43,13 @@ from twisted.python import runtime  try:      if not hasattr(sys, "frozen"):          # Don't want to check this for py2exe -        import pygtk  # type: ignore[import] +        import pygtk          pygtk.require("2.0")  except (ImportError, AttributeError):      pass  # maybe we're using pygtk before this hack existed. -import gobject  # type: ignore[import] +import gobject  if not hasattr(gobject, "IO_HUP"):      # gi.repository's legacy compatibility helper raises an AttributeError with @@ -74,7 +74,7 @@ class Gtk2Reactor(_glibbase.GlibReactorBase):      def __init__(self, useGtk=True):          _gtk = None          if useGtk is True: -            import gtk as _gtk  # type: ignore[import] +            import gtk as _gtk          _glibbase.GlibReactorBase.__init__(self, gobject, _gtk, useGtk=useGtk) diff --git a/contrib/python/Twisted/py3/twisted/internet/iocpreactor/iocpsupport.py b/contrib/python/Twisted/py3/twisted/internet/iocpreactor/iocpsupport.py index 826c976487f..9cb9f16da29 100644 --- a/contrib/python/Twisted/py3/twisted/internet/iocpreactor/iocpsupport.py +++ b/contrib/python/Twisted/py3/twisted/internet/iocpreactor/iocpsupport.py @@ -12,7 +12,7 @@ __all__ = [      "send",  ] -from twisted_iocpsupport.iocpsupport import (  # type: ignore[import] +from twisted_iocpsupport.iocpsupport import (  # type: ignore[import-not-found]      CompletionPort,      Event,      accept, diff --git a/contrib/python/Twisted/py3/twisted/internet/iocpreactor/tcp.py b/contrib/python/Twisted/py3/twisted/internet/iocpreactor/tcp.py index aadd685269c..ef72b7ed7e5 100644 --- a/contrib/python/Twisted/py3/twisted/internet/iocpreactor/tcp.py +++ b/contrib/python/Twisted/py3/twisted/internet/iocpreactor/tcp.py @@ -5,15 +5,19 @@  TCP support for IOCP reactor  """ +from __future__ import annotations +  import errno  import socket  import struct -from typing import Optional +from typing import TYPE_CHECKING, Optional, Union  from zope.interface import classImplements, implementer  from twisted.internet import address, defer, error, interfaces, main  from twisted.internet.abstract import _LogOwner, isIPv6Address +from twisted.internet.address import IPv4Address, IPv6Address +from twisted.internet.interfaces import IProtocol  from twisted.internet.iocpreactor import abstract, iocpsupport as _iocp  from twisted.internet.iocpreactor.const import (      ERROR_CONNECTION_REFUSED, @@ -42,6 +46,9 @@ except ImportError:  else:      _startTLS = __startTLS +if TYPE_CHECKING: +    # Circular import only to describe a type. +    from twisted.internet.iocpreactor.reactor import IOCPReactor  # ConnectEx returns these. XXX: find out what it does for timeout  connectExErrors = { @@ -345,7 +352,15 @@ class Server(Connection):      _tlsClientDefault = False -    def __init__(self, sock, protocol, clientAddr, serverAddr, sessionno, reactor): +    def __init__( +        self, +        sock: socket.socket, +        protocol: IProtocol, +        clientAddr: Union[IPv4Address, IPv6Address], +        serverAddr: Union[IPv4Address, IPv6Address], +        sessionno: int, +        reactor: IOCPReactor, +    ):          """          Server(sock, protocol, client, server, sessionno) diff --git a/contrib/python/Twisted/py3/twisted/internet/posixbase.py b/contrib/python/Twisted/py3/twisted/internet/posixbase.py index bd160ec8656..ed218b37f35 100644 --- a/contrib/python/Twisted/py3/twisted/internet/posixbase.py +++ b/contrib/python/Twisted/py3/twisted/internet/posixbase.py @@ -68,7 +68,7 @@ if unixEnabled:  if platform.isWindows():      try: -        import win32process  # type: ignore[import] +        import win32process          processEnabled = True      except ImportError: diff --git a/contrib/python/Twisted/py3/twisted/internet/process.py b/contrib/python/Twisted/py3/twisted/internet/process.py index ef3b88d9f19..ff7684e358b 100644 --- a/contrib/python/Twisted/py3/twisted/internet/process.py +++ b/contrib/python/Twisted/py3/twisted/internet/process.py @@ -879,7 +879,7 @@ class Process(_BaseProcess):              else:                  fdState.append((eachFD, isCloseOnExec))          if environment is None: -            environment = {} +            environment = os.environ          setSigDef = [              everySignal diff --git a/contrib/python/Twisted/py3/twisted/internet/pyuisupport.py b/contrib/python/Twisted/py3/twisted/internet/pyuisupport.py index bfbffb9cb40..265fea24055 100644 --- a/contrib/python/Twisted/py3/twisted/internet/pyuisupport.py +++ b/contrib/python/Twisted/py3/twisted/internet/pyuisupport.py @@ -11,7 +11,7 @@ See doc/examples/pyuidemo.py for example usage.  """  # System imports -import pyui  # type: ignore[import] +import pyui  # type: ignore[import-not-found]  def _guiUpdate(reactor, delay): diff --git a/contrib/python/Twisted/py3/twisted/internet/serialport.py b/contrib/python/Twisted/py3/twisted/internet/serialport.py index d63d4ce4351..4fda3b9115d 100644 --- a/contrib/python/Twisted/py3/twisted/internet/serialport.py +++ b/contrib/python/Twisted/py3/twisted/internet/serialport.py @@ -26,7 +26,7 @@ __all__ = [  ]  # all of them require pyserial at the moment, so check that first -import serial  # type: ignore[import] +import serial  from serial import (      EIGHTBITS,      FIVEBITS, diff --git a/contrib/python/Twisted/py3/twisted/internet/ssl.py b/contrib/python/Twisted/py3/twisted/internet/ssl.py index 1ad02b3c097..902268bab49 100644 --- a/contrib/python/Twisted/py3/twisted/internet/ssl.py +++ b/contrib/python/Twisted/py3/twisted/internet/ssl.py @@ -53,6 +53,7 @@ APIs listed above.      throughout the documentation.  """ +from __future__ import annotations  from zope.interface import implementedBy, implementer, implementer_only @@ -180,6 +181,8 @@ class Server(tcp.Server):      I am an SSL server.      """ +    server: Port +      def __init__(self, *args, **kwargs):          tcp.Server.__init__(self, *args, **kwargs)          self.startTLS(self.server.ctxFactory) diff --git a/contrib/python/Twisted/py3/twisted/internet/tcp.py b/contrib/python/Twisted/py3/twisted/internet/tcp.py index c87b5b73339..8f85025556b 100644 --- a/contrib/python/Twisted/py3/twisted/internet/tcp.py +++ b/contrib/python/Twisted/py3/twisted/internet/tcp.py @@ -8,13 +8,13 @@ Various asynchronous TCP/IP classes.  End users shouldn't use this module directly - use the reactor APIs instead.  """ -import os +from __future__ import annotations -# System Imports +import os  import socket  import struct  import sys -from typing import Callable, ClassVar, List, Optional +from typing import Callable, ClassVar, List, Optional, Union  from zope.interface import Interface, implementer @@ -24,6 +24,8 @@ import typing_extensions  from twisted.internet.interfaces import (      IHalfCloseableProtocol,      IListeningPort, +    IProtocol, +    IReactorTCP,      ISystemHandle,      ITCPTransport,  ) @@ -781,9 +783,19 @@ class Server(_TLSServerMixin, Connection):      _base = Connection -    _addressType = address.IPv4Address - -    def __init__(self, sock, protocol, client, server, sessionno, reactor): +    _addressType: Union[ +        type[address.IPv4Address], type[address.IPv6Address] +    ] = address.IPv4Address + +    def __init__( +        self, +        sock: socket.socket, +        protocol: IProtocol, +        client: tuple[object, ...], +        server: Port, +        sessionno: int, +        reactor: IReactorTCP, +    ) -> None:          """          Server(sock, protocol, client, server, sessionno) diff --git a/contrib/python/Twisted/py3/twisted/internet/win32eventreactor.py b/contrib/python/Twisted/py3/twisted/internet/win32eventreactor.py index 0e96012ea58..8fbef7d554d 100644 --- a/contrib/python/Twisted/py3/twisted/internet/win32eventreactor.py +++ b/contrib/python/Twisted/py3/twisted/internet/win32eventreactor.py @@ -54,13 +54,7 @@ from weakref import WeakKeyDictionary  from zope.interface import implementer  # Win32 imports -from win32file import (  # type: ignore[import] -    FD_ACCEPT, -    FD_CLOSE, -    FD_CONNECT, -    FD_READ, -    WSAEventSelect, -) +from win32file import FD_ACCEPT, FD_CLOSE, FD_CONNECT, FD_READ, WSAEventSelect  try:      # WSAEnumNetworkEvents was added in pywin32 215 @@ -77,8 +71,8 @@ except ImportError:          return {FD_READ} -import win32gui  # type: ignore[import] -from win32event import (  # type: ignore[import] +import win32gui  # type: ignore[import-untyped] +from win32event import (      QS_ALLINPUT,      WAIT_OBJECT_0,      WAIT_TIMEOUT, diff --git a/contrib/python/Twisted/py3/twisted/internet/wxreactor.py b/contrib/python/Twisted/py3/twisted/internet/wxreactor.py index b988724dfa7..257c5259ce7 100644 --- a/contrib/python/Twisted/py3/twisted/internet/wxreactor.py +++ b/contrib/python/Twisted/py3/twisted/internet/wxreactor.py @@ -26,14 +26,14 @@ real applications.  from queue import Empty, Queue  try: -    from wx import (  # type: ignore[import] +    from wx import (          CallAfter as wxCallAfter,          PySimpleApp as wxPySimpleApp,          Timer as wxTimer,      )  except ImportError:      # older version of wxPython: -    from wxPython.wx import wxPySimpleApp, wxCallAfter, wxTimer  # type: ignore[import] +    from wxPython.wx import wxPySimpleApp, wxCallAfter, wxTimer  from twisted.internet import _threadedselect  from twisted.python import log, runtime diff --git a/contrib/python/Twisted/py3/twisted/logger/_filter.py b/contrib/python/Twisted/py3/twisted/logger/_filter.py index fa4220ea3e0..07e443849a1 100644 --- a/contrib/python/Twisted/py3/twisted/logger/_filter.py +++ b/contrib/python/Twisted/py3/twisted/logger/_filter.py @@ -11,7 +11,7 @@ from typing import Dict, Iterable  from zope.interface import Interface, implementer -from constantly import NamedConstant, Names  # type: ignore[import] +from constantly import NamedConstant, Names  from ._interfaces import ILogObserver, LogEvent  from ._levels import InvalidLogLevelError, LogLevel diff --git a/contrib/python/Twisted/py3/twisted/logger/_format.py b/contrib/python/Twisted/py3/twisted/logger/_format.py index 4bc06ec40c4..59b44c7f723 100644 --- a/contrib/python/Twisted/py3/twisted/logger/_format.py +++ b/contrib/python/Twisted/py3/twisted/logger/_format.py @@ -6,10 +6,12 @@  Tools for formatting logging events.  """ +from __future__ import annotations +  from datetime import datetime as DateTime  from typing import Any, Callable, Iterator, Mapping, Optional, Union, cast -from constantly import NamedConstant  # type: ignore[import] +from constantly import NamedConstant  from twisted.python._tzhelper import FixedOffsetTimeZone  from twisted.python.failure import Failure @@ -164,6 +166,57 @@ def formatEventAsClassicLogText(      return eventText + "\n" +def keycall(key: str, getter: Callable[[str], Any]) -> PotentialCallWrapper: +    """ +    Check to see if C{key} ends with parentheses ("C{()}"); if not, wrap up the +    result of C{get} in a L{PotentialCallWrapper}.  Otherwise, call the result +    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{()}. + +    @param getter: A function which takes a string and returns some other +        object, to be formatted and stringified for a log. + +    @return: A L{PotentialCallWrapper} that will wrap up the result to allow +        for subsequent usages of parens to defer execution to log-format time. +    """ +    callit = key.endswith("()") +    realKey = key[:-2] if callit else key +    value = getter(realKey) +    if callit: +        value = value() +    return PotentialCallWrapper(value) + + +class PotentialCallWrapper(object): +    """ +    Object wrapper that wraps C{getattr()} so as to process call-parentheses +    C{"()"} after a dotted attribute access. +    """ + +    def __init__(self, wrapped: object) -> None: +        self._wrapped = wrapped + +    def __getattr__(self, name: str) -> object: +        return keycall(name, self._wrapped.__getattribute__) + +    def __getitem__(self, name: str) -> object: +        # The sub-object may not be indexable, but if it isn't, that's the +        # caller's problem. +        value = self._wrapped[name]  # type:ignore[index] +        return PotentialCallWrapper(value) + +    def __format__(self, format_spec: str) -> str: +        return self._wrapped.__format__(format_spec) + +    def __repr__(self) -> str: +        return self._wrapped.__repr__() + +    def __str__(self) -> str: +        return self._wrapped.__str__() + +  class CallMapping(Mapping[str, Any]):      """      Read-only mapping that turns a C{()}-suffix in key names into an invocation @@ -190,12 +243,7 @@ class CallMapping(Mapping[str, Any]):          Look up an item in the submapping for this L{CallMapping}, calling it          if C{key} ends with C{"()"}.          """ -        callit = key.endswith("()") -        realKey = key[:-2] if callit else key -        value = self._submapping[realKey] -        if callit: -            value = value() -        return value +        return keycall(key, self._submapping.__getitem__)  def formatWithCall(formatString: str, mapping: Mapping[str, Any]) -> str: diff --git a/contrib/python/Twisted/py3/twisted/logger/_io.py b/contrib/python/Twisted/py3/twisted/logger/_io.py index 7a49718db71..96634212201 100644 --- a/contrib/python/Twisted/py3/twisted/logger/_io.py +++ b/contrib/python/Twisted/py3/twisted/logger/_io.py @@ -9,7 +9,7 @@ File-like object that logs.  import sys  from typing import AnyStr, Iterable, Optional -from constantly import NamedConstant  # type: ignore[import] +from constantly import NamedConstant  from incremental import Version  from twisted.python.deprecate import deprecatedProperty diff --git a/contrib/python/Twisted/py3/twisted/logger/_json.py b/contrib/python/Twisted/py3/twisted/logger/_json.py index 2ecdd43045a..0cc05ce5011 100644 --- a/contrib/python/Twisted/py3/twisted/logger/_json.py +++ b/contrib/python/Twisted/py3/twisted/logger/_json.py @@ -10,7 +10,7 @@ from json import dumps, loads  from typing import IO, Any, AnyStr, Dict, Iterable, Optional, Union, cast  from uuid import UUID -from constantly import NamedConstant  # type: ignore[import] +from constantly import NamedConstant  from twisted.python.failure import Failure  from ._file import FileLogObserver diff --git a/contrib/python/Twisted/py3/twisted/logger/_levels.py b/contrib/python/Twisted/py3/twisted/logger/_levels.py index 800a549f88c..39fab342de4 100644 --- a/contrib/python/Twisted/py3/twisted/logger/_levels.py +++ b/contrib/python/Twisted/py3/twisted/logger/_levels.py @@ -6,7 +6,7 @@  Log levels.  """ -from constantly import NamedConstant, Names  # type: ignore[import] +from constantly import NamedConstant, Names  class InvalidLogLevelError(Exception): diff --git a/contrib/python/Twisted/py3/twisted/logger/_stdlib.py b/contrib/python/Twisted/py3/twisted/logger/_stdlib.py index 030b643883e..abc707e4a82 100644 --- a/contrib/python/Twisted/py3/twisted/logger/_stdlib.py +++ b/contrib/python/Twisted/py3/twisted/logger/_stdlib.py @@ -11,7 +11,7 @@ from typing import Mapping, Tuple  from zope.interface import implementer -from constantly import NamedConstant  # type: ignore[import] +from constantly import NamedConstant  from twisted.python.compat import currentframe  from ._format import formatEvent diff --git a/contrib/python/Twisted/py3/twisted/pair/tuntap.py b/contrib/python/Twisted/py3/twisted/pair/tuntap.py index a5f6dbef3bf..2564257966b 100644 --- a/contrib/python/Twisted/py3/twisted/pair/tuntap.py +++ b/contrib/python/Twisted/py3/twisted/pair/tuntap.py @@ -19,7 +19,7 @@ from typing import Tuple  from zope.interface import Attribute, Interface, implementer -from constantly import FlagConstant, Flags  # type: ignore[import] +from constantly import FlagConstant, Flags  from incremental import Version  from twisted.internet import abstract, defer, error, interfaces, task diff --git a/contrib/python/Twisted/py3/twisted/positioning/base.py b/contrib/python/Twisted/py3/twisted/positioning/base.py index 7ac5398cd95..460f5b4ff20 100644 --- a/contrib/python/Twisted/py3/twisted/positioning/base.py +++ b/contrib/python/Twisted/py3/twisted/positioning/base.py @@ -14,7 +14,7 @@ from typing import ClassVar, Sequence  from zope.interface import implementer -from constantly import NamedConstant, Names  # type: ignore[import] +from constantly import NamedConstant, Names  from twisted.positioning import ipositioning  from twisted.python.util import FancyEqMixin diff --git a/contrib/python/Twisted/py3/twisted/positioning/nmea.py b/contrib/python/Twisted/py3/twisted/positioning/nmea.py index 128c1b7938c..b1f761f5984 100644 --- a/contrib/python/Twisted/py3/twisted/positioning/nmea.py +++ b/contrib/python/Twisted/py3/twisted/positioning/nmea.py @@ -26,7 +26,7 @@ from functools import reduce  from zope.interface import implementer -from constantly import ValueConstant, Values  # type: ignore[import] +from constantly import ValueConstant, Values  from twisted.positioning import _sentence, base, ipositioning  from twisted.positioning.base import Angles diff --git a/contrib/python/Twisted/py3/twisted/protocols/amp.py b/contrib/python/Twisted/py3/twisted/protocols/amp.py index a8c35754a99..ac28a92bfa4 100644 --- a/contrib/python/Twisted/py3/twisted/protocols/amp.py +++ b/contrib/python/Twisted/py3/twisted/protocols/amp.py @@ -202,7 +202,18 @@ from io import BytesIO  from itertools import count  from struct import pack  from types import MethodType -from typing import Any, Callable, Dict, List, Optional, Tuple, Type, TypeVar, Union +from typing import ( +    Any, +    Callable, +    ClassVar, +    Dict, +    List, +    Optional, +    Tuple, +    Type, +    TypeVar, +    Union, +)  from zope.interface import Interface, implementer @@ -1686,18 +1697,23 @@ class Descriptor(Integer):          return outString +_Self = TypeVar("_Self") + +  class _CommandMeta(type):      """      Metaclass hack to establish reverse-mappings for 'errors' and      'fatalErrors' as class vars.      """ -    def __new__(cls, name, bases, attrs): +    def __new__( +        cls: type[_Self], name: str, bases: tuple[type], attrs: dict[str, object] +    ) -> Type[Command]:          reverseErrors = attrs["reverseErrors"] = {}          er = attrs["allErrors"] = {}          if "commandName" not in attrs:              attrs["commandName"] = name.encode("ascii") -        newtype = type.__new__(cls, name, bases, attrs) +        newtype: Type[Command] = type.__new__(cls, name, bases, attrs)  # type:ignore          if not isinstance(newtype.commandName, bytes):              raise TypeError( @@ -1705,12 +1721,12 @@ class _CommandMeta(type):                      newtype.commandName                  )              ) -        for name, _ in newtype.arguments: -            if not isinstance(name, bytes): -                raise TypeError(f"Argument names must be byte strings, got: {name!r}") -        for name, _ in newtype.response: -            if not isinstance(name, bytes): -                raise TypeError(f"Response names must be byte strings, got: {name!r}") +        for bname, _ in newtype.arguments: +            if not isinstance(bname, bytes): +                raise TypeError(f"Argument names must be byte strings, got: {bname!r}") +        for bname, _ in newtype.response: +            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] = {} @@ -1718,9 +1734,9 @@ class _CommandMeta(type):          accumulateClassDict(newtype, "fatalErrors", fatalErrors)          if not isinstance(newtype.errors, dict): -            newtype.errors = dict(newtype.errors) +            newtype.errors = dict(newtype.errors)  # type:ignore[unreachable]          if not isinstance(newtype.fatalErrors, dict): -            newtype.fatalErrors = dict(newtype.fatalErrors) +            newtype.fatalErrors = dict(newtype.fatalErrors)  # type:ignore[unreachable]          for v, k in errors.items():              reverseErrors[k] = v @@ -1729,13 +1745,13 @@ class _CommandMeta(type):              reverseErrors[k] = v              er[v] = k -        for _, name in newtype.errors.items(): -            if not isinstance(name, bytes): -                raise TypeError(f"Error names must be byte strings, got: {name!r}") -        for _, name in newtype.fatalErrors.items(): -            if not isinstance(name, bytes): +        for _, bname in newtype.errors.items(): +            if not isinstance(bname, bytes): +                raise TypeError(f"Error names must be byte strings, got: {bname!r}") +        for _, bname in newtype.fatalErrors.items(): +            if not isinstance(bname, bytes):                  raise TypeError( -                    f"Fatal error names must be byte strings, got: {name!r}" +                    f"Fatal error names must be byte strings, got: {bname!r}"                  )          return newtype @@ -1784,14 +1800,15 @@ class Command(metaclass=_CommandMeta):      want one.      """ -    arguments: List[Tuple[bytes, Argument]] = [] -    response: List[Tuple[bytes, Argument]] = [] -    extra: List[Any] = [] -    errors: Dict[Type[Exception], bytes] = {} -    fatalErrors: Dict[Type[Exception], bytes] = {} +    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]] = {} -    commandType: "Union[Type[Command], Type[Box]]" = Box -    responseType: Type[AmpBox] = Box +    commandType: "ClassVar[Union[Type[Command], Type[Box]]]" = Box +    responseType: ClassVar[Type[AmpBox]] = Box      requiresAnswer = True diff --git a/contrib/python/Twisted/py3/twisted/protocols/haproxy/_v2parser.py b/contrib/python/Twisted/py3/twisted/protocols/haproxy/_v2parser.py index 5b8e5874018..cfcf7c99bcc 100644 --- a/contrib/python/Twisted/py3/twisted/protocols/haproxy/_v2parser.py +++ b/contrib/python/Twisted/py3/twisted/protocols/haproxy/_v2parser.py @@ -13,7 +13,7 @@ from typing import Callable, Tuple, Type, Union  from zope.interface import implementer -from constantly import ValueConstant, Values  # type: ignore[import] +from constantly import ValueConstant, Values  from typing_extensions import Literal  from twisted.internet import address diff --git a/contrib/python/Twisted/py3/twisted/protocols/tls.py b/contrib/python/Twisted/py3/twisted/protocols/tls.py index d2ac2d2cf80..be7a58ce4d3 100644 --- a/contrib/python/Twisted/py3/twisted/protocols/tls.py +++ b/contrib/python/Twisted/py3/twisted/protocols/tls.py @@ -708,7 +708,7 @@ class _AggregateSmallWrites:          self._write = write          self._clock = clock          self._buffer: list[bytes] = [] -        self._bufferLen = 0 +        self._bufferLeft = self.MAX_BUFFER_SIZE          self._scheduled: Optional[IDelayedCall] = None      def write(self, data: bytes) -> None: @@ -719,9 +719,9 @@ class _AggregateSmallWrites:          Accumulating too much data can result in higher memory usage.          """          self._buffer.append(data) -        self._bufferLen += len(data) +        self._bufferLeft -= len(data) -        if self._bufferLen > self.MAX_BUFFER_SIZE: +        if self._bufferLeft < 0:              # We've accumulated enough we should just write it out. No need to              # schedule a flush, since we just flushed everything.              self.flush() @@ -744,7 +744,7 @@ class _AggregateSmallWrites:      def flush(self) -> None:          """Flush any buffered writes."""          if self._buffer: -            self._bufferLen = 0 +            self._bufferLeft = self.MAX_BUFFER_SIZE              self._write(b"".join(self._buffer))              del self._buffer[:] @@ -764,19 +764,20 @@ def _get_default_clock() -> IReactorTime:  class BufferingTLSTransport(TLSMemoryBIOProtocol):      """      A TLS transport implemented by wrapping buffering around a -    ``TLSMemoryBIOProtocol``. +    L{TLSMemoryBIOProtocol}. -    Doing many small writes directly to a ``OpenSSL.SSL.Connection``, as -    implemented in ``TLSMemoryBIOProtocol``, can add significant CPU and +    Doing many small writes directly to a L{OpenSSL.SSL.Connection}, as +    implemented in L{TLSMemoryBIOProtocol}, can add significant CPU and      bandwidth overhead.  Thus, even when writing is possible, small writes will      get aggregated and written as a single write at the next reactor iteration.      """ -    # Note: An implementation based on composition would be nicer, but there's -    # close integration between ``ProtocolWrapper`` subclasses like -    # ``TLSMemoryBIOProtocol`` and the corresponding factory. Composition broke -    # things like ``TLSMemoryBIOFactory.protocols`` having the correct -    # instances, whereas subclassing makes that work. +    # Implementation Note: An implementation based on composition would be +    # nicer, but there's close integration between L{ProtocolWrapper} +    # subclasses like L{TLSMemoryBIOProtocol} and the corresponding factory. An +    # attempt to implement this with broke things like +    # L{TLSMemoryBIOFactory.protocols} having the correct instances, whereas +    # subclassing makes that work.      def __init__(          self, @@ -787,11 +788,10 @@ class BufferingTLSTransport(TLSMemoryBIOProtocol):          super().__init__(factory, wrappedProtocol, _connectWrapped)          actual_write = super().write          self._aggregator = _AggregateSmallWrites(actual_write, factory._clock) - -    def write(self, data: bytes) -> None: -        if isinstance(data, str):  # type: ignore[unreachable] -            raise TypeError("Must write bytes to a TLS transport, not str.") -        self._aggregator.write(data) +        # This is kinda ugly, but speeds things up a lot in a hot path with +        # lots of small TLS writes. May become unnecessary in Python 3.13 or +        # later if JIT and/or inlining becomes a thing. +        self.write = self._aggregator.write  # type: ignore[method-assign]      def writeSequence(self, sequence: Iterable[bytes]) -> None:          self._aggregator.write(b"".join(sequence)) diff --git a/contrib/python/Twisted/py3/twisted/python/_appdirs.py b/contrib/python/Twisted/py3/twisted/python/_appdirs.py index 8ceb1116762..90f5858c96c 100644 --- a/contrib/python/Twisted/py3/twisted/python/_appdirs.py +++ b/contrib/python/Twisted/py3/twisted/python/_appdirs.py @@ -9,7 +9,7 @@ Application data directory support.  import inspect  from typing import cast -import appdirs  # type: ignore[import] +import appdirs  # type: ignore[import-untyped]  from twisted.python.compat import currentframe diff --git a/contrib/python/Twisted/py3/twisted/python/constants.py b/contrib/python/Twisted/py3/twisted/python/constants.py index fbad78e4080..df970363c62 100644 --- a/contrib/python/Twisted/py3/twisted/python/constants.py +++ b/contrib/python/Twisted/py3/twisted/python/constants.py @@ -9,13 +9,6 @@ numeric, and bit flag values.  # Import and re-export Constantly -from constantly import (  # type: ignore[import] -    FlagConstant, -    Flags, -    NamedConstant, -    Names, -    ValueConstant, -    Values, -) +from constantly import FlagConstant, Flags, NamedConstant, Names, ValueConstant, Values  __all__ = ["NamedConstant", "ValueConstant", "FlagConstant", "Names", "Values", "Flags"] diff --git a/contrib/python/Twisted/py3/twisted/python/deprecate.py b/contrib/python/Twisted/py3/twisted/python/deprecate.py index c85b98d6272..c5df556f709 100644 --- a/contrib/python/Twisted/py3/twisted/python/deprecate.py +++ b/contrib/python/Twisted/py3/twisted/python/deprecate.py @@ -601,12 +601,19 @@ def warnAboutFunction(offender, warningString):      """      # inspect.getmodule() is attractive, but somewhat      # broken in Python < 2.6.  See Python bug 4845. +    # In Python 3.13 line numbers returned by findlinestarts +    # can be None for bytecode that does not map to source +    # lines.      offenderModule = sys.modules[offender.__module__]      warn_explicit(          warningString,          category=DeprecationWarning,          filename=inspect.getabsfile(offenderModule), -        lineno=max(lineNumber for _, lineNumber in findlinestarts(offender.__code__)), +        lineno=max( +            lineNumber +            for _, lineNumber in findlinestarts(offender.__code__) +            if lineNumber is not None +        ),          module=offenderModule.__name__,          registry=offender.__globals__.setdefault("__warningregistry__", {}),          module_globals=None, diff --git a/contrib/python/Twisted/py3/twisted/python/failure.py b/contrib/python/Twisted/py3/twisted/python/failure.py index ca893ca4c94..c006d555e55 100644 --- a/contrib/python/Twisted/py3/twisted/python/failure.py +++ b/contrib/python/Twisted/py3/twisted/python/failure.py @@ -516,7 +516,7 @@ class Failure(BaseException):          """          # Note that the actual magic to find the traceback information          # is done in _findFailure. -        return g.throw(self.type, self.value, self.tb) +        return g.throw(self.value.with_traceback(self.tb))      @classmethod      def _findFailure(cls): diff --git a/contrib/python/Twisted/py3/twisted/python/lockfile.py b/contrib/python/Twisted/py3/twisted/python/lockfile.py index c285d4ad102..bea04ee560f 100644 --- a/contrib/python/Twisted/py3/twisted/python/lockfile.py +++ b/contrib/python/Twisted/py3/twisted/python/lockfile.py @@ -39,8 +39,8 @@ else:      # race-conditions duty. - hawkie      try: -        import pywintypes  # type: ignore[import] -        from win32api import OpenProcess  # type: ignore[import] +        import pywintypes +        from win32api import OpenProcess      except ImportError:          kill = None  # type: ignore[assignment]      else: diff --git a/contrib/python/Twisted/py3/twisted/python/modules.py b/contrib/python/Twisted/py3/twisted/python/modules.py index aea6a6ed828..d2294a593a8 100644 --- a/contrib/python/Twisted/py3/twisted/python/modules.py +++ b/contrib/python/Twisted/py3/twisted/python/modules.py @@ -56,6 +56,7 @@ the modules outside the standard library's python-files directory::  @type theSystemPath: L{PythonPath}  """ +from __future__ import annotations  import inspect  import sys @@ -251,7 +252,9 @@ class PythonAttribute:      this class.      """ -    def __init__(self, name, onObject, loaded, pythonValue): +    def __init__( +        self, name: str, onObject: PythonAttribute, loaded: bool, pythonValue: object +    ) -> None:          """          Create a PythonAttribute.  This is a private constructor.  Do not construct          me directly, use PythonModule.iterAttributes. @@ -306,7 +309,9 @@ class PythonModule(_ModuleIteratorHelper):      from.      """ -    def __init__(self, name, filePath, pathEntry): +    def __init__( +        self, name: str, filePath: FilePath[str], pathEntry: PathEntry +    ) -> None:          """          Create a PythonModule.  Do not construct this directly, instead inspect a          PythonPath or other PythonModule instances. diff --git a/contrib/python/Twisted/py3/twisted/python/shortcut.py b/contrib/python/Twisted/py3/twisted/python/shortcut.py index 7d08424bdd0..a735cd67377 100644 --- a/contrib/python/Twisted/py3/twisted/python/shortcut.py +++ b/contrib/python/Twisted/py3/twisted/python/shortcut.py @@ -10,8 +10,8 @@ Requires win32all.  import os -import pythoncom  # type: ignore[import] -from win32com.shell import shell  # type: ignore[import] +import pythoncom  # type: ignore[import-untyped] +from win32com.shell import shell  def open(filename): diff --git a/contrib/python/Twisted/py3/twisted/python/util.py b/contrib/python/Twisted/py3/twisted/python/util.py index 058f1044b37..6047f7eda53 100644 --- a/contrib/python/Twisted/py3/twisted/python/util.py +++ b/contrib/python/Twisted/py3/twisted/python/util.py @@ -40,7 +40,6 @@ from typing import (      Tuple,      TypeVar,      Union, -    cast,  )  from incremental import Version @@ -628,10 +627,8 @@ class FancyStrMixin:              if isinstance(attr, str):                  r.append(f" {attr}={getattr(self, attr)!r}")              elif len(attr) == 2: -                attr = cast(Tuple[str, Callable[[Any], str]], attr)                  r.append((f" {attr[0]}=") + attr[1](getattr(self, attr[0])))              else: -                attr = cast(Tuple[str, str, str], attr)                  r.append((" %s=" + attr[2]) % (attr[1], getattr(self, attr[0])))          r.append(">")          return "".join(r) diff --git a/contrib/python/Twisted/py3/twisted/python/win32.py b/contrib/python/Twisted/py3/twisted/python/win32.py index df1b22fa1ee..f86c1ec90e3 100644 --- a/contrib/python/Twisted/py3/twisted/python/win32.py +++ b/contrib/python/Twisted/py3/twisted/python/win32.py @@ -125,7 +125,7 @@ class _ErrorFormatter:          except ImportError:              WinError = None          try: -            from win32api import FormatMessage  # type: ignore[import] +            from win32api import FormatMessage          except ImportError:              FormatMessage = None          try: diff --git a/contrib/python/Twisted/py3/twisted/spread/flavors.py b/contrib/python/Twisted/py3/twisted/spread/flavors.py index ef98fee272c..4e8d11d7788 100644 --- a/contrib/python/Twisted/py3/twisted/spread/flavors.py +++ b/contrib/python/Twisted/py3/twisted/spread/flavors.py @@ -573,7 +573,7 @@ class RemoteCacheMethod:          """(internal) action method."""          cacheID = self.broker.cachedRemotelyAs(self.cached)          if cacheID is None: -            from pb import ProtocolError  # type: ignore[import] +            from twisted.spread.pb import ProtocolError              raise ProtocolError(                  "You can't call a cached method when the object hasn't been given to the peer yet." @@ -636,7 +636,7 @@ class RemoteCacheObserver:          if isinstance(_name, str):              _name = _name.encode("utf-8")          if cacheID is None: -            from pb import ProtocolError +            from twisted.spread.pb import ProtocolError              raise ProtocolError(                  "You can't call a cached method when the " diff --git a/contrib/python/Twisted/py3/twisted/trial/_dist/functional.py b/contrib/python/Twisted/py3/twisted/trial/_dist/functional.py index 3db4dca5de5..8bb5ecf7a4f 100644 --- a/contrib/python/Twisted/py3/twisted/trial/_dist/functional.py +++ b/contrib/python/Twisted/py3/twisted/trial/_dist/functional.py @@ -79,10 +79,7 @@ def compose(fx: Callable[[_B], _C], fy: Callable[[_A], _B]) -> Callable[[_A], _C  # Discard the result of an awaitable and substitute None in its place. -# -# Ignore the `Cannot infer type argument 1 of "compose"` -# https://github.com/python/mypy/issues/6220 -discardResult: Callable[[Awaitable[_A]], Deferred[None]] = compose(  # type: ignore[misc] +discardResult: Callable[[Awaitable[_A]], Deferred[None]] = compose(      Deferred.fromCoroutine,      partial(flip(sequence), succeed(None)),  ) diff --git a/contrib/python/Twisted/py3/twisted/trial/_synctest.py b/contrib/python/Twisted/py3/twisted/trial/_synctest.py index 2cffc2c79e3..6b1f43dc89d 100644 --- a/contrib/python/Twisted/py3/twisted/trial/_synctest.py +++ b/contrib/python/Twisted/py3/twisted/trial/_synctest.py @@ -1189,9 +1189,14 @@ class SynchronousTestCase(_Assertions):                      if filename != os.path.normcase(aWarning.filename):                          continue + +                    # In Python 3.13 line numbers returned by findlinestarts +                    # can be None for bytecode that does not map to source +                    # lines.                      lineNumbers = [                          lineNumber                          for _, lineNumber in _findlinestarts(aFunction.__code__) +                        if lineNumber is not None                      ]                      if not (min(lineNumbers) <= aWarning.lineno <= max(lineNumbers)):                          continue diff --git a/contrib/python/Twisted/py3/twisted/trial/reporter.py b/contrib/python/Twisted/py3/twisted/trial/reporter.py index 2664b2fe0d5..4ee67ebcf67 100644 --- a/contrib/python/Twisted/py3/twisted/trial/reporter.py +++ b/contrib/python/Twisted/py3/twisted/trial/reporter.py @@ -7,7 +7,7 @@  """  Defines classes that handle the results of tests.  """ - +from __future__ import annotations  import os  import sys @@ -16,7 +16,7 @@ import unittest as pyunit  import warnings  from collections import OrderedDict  from types import TracebackType -from typing import TYPE_CHECKING, List, Tuple, Type, Union +from typing import TYPE_CHECKING, List, Optional, Tuple, Type, Union  from zope.interface import implementer @@ -32,7 +32,7 @@ if TYPE_CHECKING:      from ._synctest import Todo  try: -    from subunit import TestProtocolClient  # type: ignore[import] +    from subunit import TestProtocolClient  except ImportError:      TestProtocolClient = None @@ -87,6 +87,11 @@ class TestResult(pyunit.TestResult):      @ivar successes: count the number of successes achieved by the test run.      @type successes: C{int} + +    @ivar _startTime: The time when the current test was started. It defaults to +    L{None}, which means that the test was skipped. +    @ivar _lastTime: The duration of the current test run. It defaults to +    L{None}, which means that the test was skipped.      """      # Used when no todo provided to addExpectedFailure or addUnexpectedSuccess. @@ -96,6 +101,9 @@ class TestResult(pyunit.TestResult):      expectedFailures: List[Tuple[itrial.ITestCase, str, "Todo"]]  # type: ignore[assignment]      unexpectedSuccesses: List[Tuple[itrial.ITestCase, str]]  # type: ignore[assignment]      successes: int +    _testStarted: Optional[int] +    # The duration of the test. It is None until the test completes. +    _lastTime: Optional[int]      def __init__(self):          super().__init__() @@ -104,6 +112,8 @@ class TestResult(pyunit.TestResult):          self.unexpectedSuccesses = []          self.successes = 0          self._timings = [] +        self._testStarted = None +        self._lastTime = None      def __repr__(self) -> str:          return "<%s run=%d errors=%d failures=%d todos=%d dones=%d skips=%d>" % ( @@ -146,7 +156,8 @@ class TestResult(pyunit.TestResult):          @type test: L{pyunit.TestCase}          """          super().stopTest(test) -        self._lastTime = self._getTime() - self._testStarted +        if self._testStarted is not None: +            self._lastTime = self._getTime() - self._testStarted      def addFailure(self, test, fail):          """ @@ -908,7 +919,7 @@ class _Win32Colorizer:      """      def __init__(self, stream): -        from win32console import (  # type: ignore[import] +        from win32console import (              FOREGROUND_BLUE,              FOREGROUND_GREEN,              FOREGROUND_INTENSITY, @@ -944,7 +955,7 @@ class _Win32Colorizer:              screenBuffer = win32console.GetStdHandle(win32console.STD_OUTPUT_HANDLE)          except ImportError:              return False -        import pywintypes  # type: ignore[import] +        import pywintypes          try:              screenBuffer.SetConsoleTextAttribute( diff --git a/contrib/python/Twisted/py3/twisted/web/_http2.py b/contrib/python/Twisted/py3/twisted/web/_http2.py index 57762e1805d..24c24fc0ffe 100644 --- a/contrib/python/Twisted/py3/twisted/web/_http2.py +++ b/contrib/python/Twisted/py3/twisted/web/_http2.py @@ -21,12 +21,12 @@ from typing import List  from zope.interface import implementer -import h2.config  # type: ignore[import] -import h2.connection  # type: ignore[import] -import h2.errors  # type: ignore[import] -import h2.events  # type: ignore[import] -import h2.exceptions  # type: ignore[import] -import priority  # type: ignore[import] +import h2.config +import h2.connection +import h2.errors +import h2.events +import h2.exceptions +import priority  from twisted.internet._producer_helpers import _PullToPush  from twisted.internet.defer import Deferred diff --git a/contrib/python/Twisted/py3/twisted/web/_template_util.py b/contrib/python/Twisted/py3/twisted/web/_template_util.py index 4a9f7f21002..230c33f3e8f 100644 --- a/contrib/python/Twisted/py3/twisted/web/_template_util.py +++ b/contrib/python/Twisted/py3/twisted/web/_template_util.py @@ -24,7 +24,7 @@ from typing import (      cast,  )  from xml.sax import handler, make_parser -from xml.sax.xmlreader import Locator +from xml.sax.xmlreader import AttributesNSImpl, Locator  from zope.interface import implementer @@ -462,7 +462,7 @@ class _ToStan(handler.ContentHandler, handler.EntityResolver):          self,          namespaceAndName: Tuple[str, str],          qname: Optional[str], -        attrs: Mapping[Tuple[Optional[str], str], str], +        attrs: AttributesNSImpl,      ) -> None:          """          Gets called when we encounter a new xmlns attribute. @@ -505,14 +505,14 @@ class _ToStan(handler.ContentHandler, handler.EntityResolver):          render = None -        attrs = OrderedDict(attrs) -        for k, v in list(attrs.items()): +        ordered = OrderedDict(attrs) +        for k, v in list(ordered.items()):              attrNS, justTheName = k              if attrNS != TEMPLATE_NAMESPACE:                  continue              if justTheName == "render":                  render = v -                del attrs[k] +                del ordered[k]          # nonTemplateAttrs is a dictionary mapping attributes that are *not* in          # TEMPLATE_NAMESPACE to their values.  Those in TEMPLATE_NAMESPACE were @@ -522,7 +522,7 @@ class _ToStan(handler.ContentHandler, handler.EntityResolver):          # preserving the xml namespace prefix given in the document.          nonTemplateAttrs = OrderedDict() -        for (attrNs, attrName), v in attrs.items(): +        for (attrNs, attrName), v in ordered.items():              nsPrefix = self.prefixMap.get(attrNs)              if nsPrefix is None:                  attrKey = attrName diff --git a/contrib/python/Twisted/py3/twisted/web/client.py b/contrib/python/Twisted/py3/twisted/web/client.py index 9a0d5e9e10b..e66b0cf3177 100644 --- a/contrib/python/Twisted/py3/twisted/web/client.py +++ b/contrib/python/Twisted/py3/twisted/web/client.py @@ -6,13 +6,16 @@  HTTP client.  """ +from __future__ import annotations  import collections  import os  import warnings  import zlib +from dataclasses import dataclass  from functools import wraps -from typing import Iterable +from http.cookiejar import CookieJar +from typing import TYPE_CHECKING, Iterable, Optional  from urllib.parse import urldefrag, urljoin, urlunparse as _urlunparse  from zope.interface import implementer @@ -21,6 +24,7 @@ from incremental import Version  from twisted.internet import defer, protocol, task  from twisted.internet.abstract import isIPv6Address +from twisted.internet.defer import Deferred  from twisted.internet.endpoints import HostnameEndpoint, wrapClientTLS  from twisted.internet.interfaces import IOpenSSLContextFactory, IProtocol  from twisted.logger import Logger @@ -43,6 +47,20 @@ from twisted.web.iweb import (      IResponse,  ) +# For the purpose of type-checking we want our faked-out types to be identical to the types they are replacing. +# For the purpose of the impementation, we want to start +# with a blank slate so that we don't accidentally use +# any of the real implementation. + +if TYPE_CHECKING: +    from email.message import EmailMessage as _InfoType +    from http.client import HTTPResponse as _ResponseBase +    from urllib.request import Request as _RequestBase +else: +    _RequestBase = object +    _ResponseBase = object +    _InfoType = object +  def urlunparse(parts):      result = _urlunparse(tuple(p.decode("charmap") for p in parts)) @@ -1200,36 +1218,38 @@ class ProxyAgent(_AgentBase):          ) -class _FakeUrllib2Request: +class _FakeStdlibRequest(_RequestBase):      """ -    A fake C{urllib2.Request} object for C{cookielib} to work with. +    A fake L{urllib.request.Request} object for L{cookiejar} to work with. -    @see: U{http://docs.python.org/library/urllib2.html#request-objects} +    @see: U{urllib.request.Request +        <https://docs.python.org/3/library/urllib.request.html#urllib.request.Request>} -    @type uri: native L{str}      @ivar uri: Request URI. -    @type headers: L{twisted.web.http_headers.Headers}      @ivar headers: Request headers. -    @type type: native L{str}      @ivar type: The scheme of the URI. -    @type host: native L{str}      @ivar host: The host[:port] of the URI.      @since: 11.1      """ -    def __init__(self, uri): +    uri: str +    type: str +    host: str +    # The received headers managed using Twisted API. +    _twistedHeaders: Headers + +    def __init__(self, uri: bytes) -> None:          """ -        Create a fake Urllib2 request. +        Create a fake  request.          @param uri: Request URI. -        @type uri: L{bytes}          """          self.uri = nativeString(uri) -        self.headers = Headers() +        self._twistedHeaders = Headers()          _uri = URI.fromBytes(uri)          self.type = nativeString(_uri.scheme) @@ -1240,19 +1260,19 @@ class _FakeUrllib2Request:              self.host += ":" + str(_uri.port)          self.origin_req_host = nativeString(_uri.host) -        self.unverifiable = lambda _: False +        self.unverifiable = False      def has_header(self, header): -        return self.headers.hasHeader(networkString(header)) +        return self._twistedHeaders.hasHeader(networkString(header))      def add_unredirected_header(self, name, value): -        self.headers.addRawHeader(networkString(name), networkString(value)) +        self._twistedHeaders.addRawHeader(networkString(name), networkString(value))      def get_full_url(self):          return self.uri      def get_header(self, name, default=None): -        headers = self.headers.getRawHeaders(networkString(name), default) +        headers = self._twistedHeaders.getRawHeaders(networkString(name), default)          if headers is not None:              headers = [nativeString(x) for x in headers]              return headers[0] @@ -1269,62 +1289,68 @@ class _FakeUrllib2Request:          return False -class _FakeUrllib2Response: +@dataclass +class _FakeUrllibResponseInfo(_InfoType): +    response: IResponse + +    def get_all(self, name: str, default: bytes) -> list[str]:  # type:ignore[override] +        headers = self.response.headers.getRawHeaders(networkString(name), default) +        h = [nativeString(x) for x in headers] +        return h + + +class _FakeStdlibResponse(_ResponseBase):      """ -    A fake C{urllib2.Response} object for C{cookielib} to work with. +    A fake L{urllib.response.Response} object for L{http.cookiejar} to work +    with. -    @type response: C{twisted.web.iweb.IResponse}      @ivar response: Underlying Twisted Web response.      @since: 11.1      """ -    def __init__(self, response): -        self.response = response +    response: IResponse -    def info(self): -        class _Meta: -            def getheaders(zelf, name): -                # PY2 -                headers = self.response.headers.getRawHeaders(name, []) -                return headers - -            def get_all(zelf, name, default): -                # PY3 -                headers = self.response.headers.getRawHeaders( -                    networkString(name), default -                ) -                h = [nativeString(x) for x in headers] -                return h +    def __init__(self, response: IResponse) -> None: +        self.response = response -        return _Meta() +    def info(self) -> _InfoType: +        result = _FakeUrllibResponseInfo(self.response) +        return result  @implementer(IAgent)  class CookieAgent:      """ -    L{CookieAgent} extends the basic L{Agent} to add RFC-compliant -    handling of HTTP cookies.  Cookies are written to and extracted -    from a C{cookielib.CookieJar} instance. +    L{CookieAgent} extends the basic L{Agent} to add RFC-compliant handling of +    HTTP cookies.  Cookies are written to and extracted from a L{CookieJar} +    instance.      The same cookie jar instance will be used for any requests through this      agent, mutating it whenever a I{Set-Cookie} header appears in a response. -    @type _agent: L{twisted.web.client.Agent}      @ivar _agent: Underlying Twisted Web agent to issue requests through. -    @type cookieJar: C{cookielib.CookieJar}      @ivar cookieJar: Initialized cookie jar to read cookies from and store          cookies to.      @since: 11.1      """ -    def __init__(self, agent, cookieJar): +    _agent: IAgent +    cookieJar: CookieJar + +    def __init__(self, agent: IAgent, cookieJar: CookieJar) -> None:          self._agent = agent          self.cookieJar = cookieJar -    def request(self, method, uri, headers=None, bodyProducer=None): +    def request( +        self, +        method: bytes, +        uri: bytes, +        headers: Optional[Headers] = None, +        bodyProducer: Optional[IBodyProducer] = None, +    ) -> Deferred[IResponse]:          """          Issue a new request to the wrapped L{Agent}. @@ -1337,33 +1363,33 @@ class CookieAgent:          @see: L{Agent.request}          """ -        if headers is None: -            headers = Headers() -        lastRequest = _FakeUrllib2Request(uri) +        actualHeaders = headers if headers is not None else Headers() +        lastRequest = _FakeStdlibRequest(uri)          # Setting a cookie header explicitly will disable automatic request          # cookies. -        if not headers.hasHeader(b"cookie"): +        if not actualHeaders.hasHeader(b"cookie"):              self.cookieJar.add_cookie_header(lastRequest)              cookieHeader = lastRequest.get_header("Cookie", None)              if cookieHeader is not None: -                headers = headers.copy() -                headers.addRawHeader(b"cookie", networkString(cookieHeader)) +                actualHeaders = actualHeaders.copy() +                actualHeaders.addRawHeader(b"cookie", networkString(cookieHeader)) -        d = self._agent.request(method, uri, headers, bodyProducer) -        d.addCallback(self._extractCookies, lastRequest) -        return d +        return self._agent.request( +            method, uri, actualHeaders, bodyProducer +        ).addCallback(self._extractCookies, lastRequest) -    def _extractCookies(self, response, request): +    def _extractCookies( +        self, response: IResponse, request: _FakeStdlibRequest +    ) -> IResponse:          """          Extract response cookies and store them in the cookie jar. -        @type response: L{twisted.web.iweb.IResponse} -        @param response: Twisted Web response. +        @param response: the Twisted Web response that we are processing. -        @param request: A urllib2 compatible request object. +        @param request: A L{_FakeStdlibRequest} wrapping our Twisted request, +            for L{CookieJar} to extract cookies from.          """ -        resp = _FakeUrllib2Response(response) -        self.cookieJar.extract_cookies(resp, request) +        self.cookieJar.extract_cookies(_FakeStdlibResponse(response), request)          return response diff --git a/contrib/python/Twisted/py3/twisted/web/http.py b/contrib/python/Twisted/py3/twisted/web/http.py index 2bad1471dc8..1c598380acc 100644 --- a/contrib/python/Twisted/py3/twisted/web/http.py +++ b/contrib/python/Twisted/py3/twisted/web/http.py @@ -31,6 +31,7 @@ also useful for HTTP clients (such as the chunked encoding parser).      it, as in the HTTP 1.1 chunked I{Transfer-Encoding} (RFC 7230 section 4.1).      This limits how much data may be buffered when decoding the line.  """ +from __future__ import annotations  __all__ = [      "SWITCHING", @@ -100,15 +101,16 @@ __all__ = [  import base64  import binascii  import calendar -import cgi  import math  import os  import re  import tempfile  import time  import warnings +from email import message_from_bytes +from email.message import EmailMessage  from io import BytesIO -from typing import AnyStr, Callable, List, Optional, Tuple +from typing import AnyStr, Callable, Dict, List, Optional, Tuple  from urllib.parse import (      ParseResultBytes,      unquote_to_bytes as unquote, @@ -130,8 +132,6 @@ from twisted.python.compat import nativeString, networkString  from twisted.python.components import proxyForInterface  from twisted.python.deprecate import deprecated  from twisted.python.failure import Failure - -# twisted imports  from twisted.web._responses import (      ACCEPTED,      BAD_GATEWAY, @@ -224,15 +224,40 @@ weekdayname_lower = [name.lower() for name in weekdayname]  monthname_lower = [name and name.lower() for name in monthname] -def _parseHeader(line): -    # cgi.parse_header requires a str -    key, pdict = cgi.parse_header(line.decode("charmap")) +def _parseContentType(line: bytes) -> bytes: +    """ +    Parse the Content-Type header. +    """ +    msg = EmailMessage() +    msg["content-type"] = line.decode("charmap") +    key = msg.get_content_type() +    encodedKey = key.encode("charmap") +    return encodedKey -    # We want the key as bytes, and cgi.parse_multipart (which consumes -    # pdict) expects a dict of str keys but bytes values -    key = key.encode("charmap") -    pdict = {x: y.encode("charmap") for x, y in pdict.items()} -    return (key, pdict) + +class _MultiPartParseException(Exception): +    """ +    Failed to parse the multipart/form-data payload. +    """ + + +def _getMultiPartArgs(content: bytes, ctype: bytes) -> dict[bytes, list[bytes]]: +    """ +    Parse the content of a multipart/form-data request. +    """ +    result = {} +    multiPartHeaders = b"MIME-Version: 1.0\r\n" + b"Content-Type: " + ctype + b"\r\n" +    msg = message_from_bytes(multiPartHeaders + content) +    if not msg.is_multipart(): +        raise _MultiPartParseException("Not a multipart.") + +    for part in msg.get_payload(): +        name = part.get_param("name", header="content-disposition") +        if not name: +            continue +        payload = part.get_payload(decode=True) +        result[name.encode("utf8")] = [payload] +    return result  def urlparse(url): @@ -267,6 +292,9 @@ def parse_qs(qs, keep_blank_values=0, strict_parsing=0):      """      Like C{cgi.parse_qs}, but with support for parsing byte strings on Python 3. +    This was created to help with Python 2 to Python 3 migration. +    Consider using L{urllib.parse.parse_qs}. +      @type qs: C{bytes}      """      d = {} @@ -829,7 +857,7 @@ class Request:      _disconnected = False      _log = Logger() -    def __init__(self, channel, queued=_QUEUED_SENTINEL): +    def __init__(self, channel: HTTPChannel, queued: object = _QUEUED_SENTINEL) -> None:          """          @param channel: the channel we're connected to.          @param queued: (deprecated) are we in the request queue, or can we @@ -845,9 +873,9 @@ class Request:          self.host = self.channel.getHost()          self.requestHeaders: Headers = Headers() -        self.received_cookies = {} +        self.received_cookies: Dict[bytes, bytes] = {}          self.responseHeaders: Headers = Headers() -        self.cookies = []  # outgoing cookies +        self.cookies: List[bytes] = []  # outgoing cookies          self.transport = self.channel.transport          if queued is _QUEUED_SENTINEL: @@ -973,47 +1001,18 @@ class Request:          if self.method == b"POST" and ctype and clength:              mfd = b"multipart/form-data" -            key, pdict = _parseHeader(ctype) -            # This weird CONTENT-LENGTH param is required by -            # cgi.parse_multipart() in some versions of Python 3.7+, see -            # bpo-29979. It looks like this will be relaxed and backported, see -            # https://github.com/python/cpython/pull/8530. -            pdict["CONTENT-LENGTH"] = clength +            key = _parseContentType(ctype)              if key == b"application/x-www-form-urlencoded":                  args.update(parse_qs(self.content.read(), 1))              elif key == mfd:                  try: -                    cgiArgs = cgi.parse_multipart( -                        self.content, -                        pdict, -                        encoding="utf8", -                        errors="surrogateescape", -                    ) - -                    # The parse_multipart function on Python 3.7+ -                    # decodes the header bytes as iso-8859-1 and -                    # decodes the body bytes as utf8 with -                    # surrogateescape -- we want bytes -                    self.args.update( -                        { -                            x.encode("iso-8859-1"): [ -                                z.encode("utf8", "surrogateescape") -                                if isinstance(z, str) -                                else z -                                for z in y -                            ] -                            for x, y in cgiArgs.items() -                            if isinstance(x, str) -                        } -                    ) -                except Exception as e: -                    # It was a bad request, or we got a signal. +                    self.content.seek(0) +                    content = self.content.read() +                    self.args.update(_getMultiPartArgs(content, ctype)) +                except _MultiPartParseException: +                    # It was a bad request.                      self.channel._respondToBadRequestAndDisconnect() -                    if isinstance(e, (TypeError, ValueError, KeyError)): -                        return -                    else: -                        # If it's not a userspace error from CGI, reraise -                        raise +                    return              self.content.seek(0, 0) @@ -1806,7 +1805,6 @@ class _IdentityTransferDecoder:  maxChunkSizeLineLength = 1024 -  _chunkExtChars = (      b"\t !\"#$%&'()*+,-./0123456789:;<=>?@"      b"ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`" @@ -1890,12 +1888,20 @@ class _ChunkedTransferDecoder:          state transition this is truncated at the front so that index 0 is          where the next state shall begin. -    @ivar _start: While in the C{'CHUNK_LENGTH'} state, tracks the index into -        the buffer at which search for CRLF should resume. Resuming the search -        at this position avoids doing quadratic work if the chunk length line -        arrives over many calls to C{dataReceived}. +    @ivar _start: While in the C{'CHUNK_LENGTH'} and C{'TRAILER'} states, +        tracks the index into the buffer at which search for CRLF should resume. +        Resuming the search at this position avoids doing quadratic work if the +        chunk length line arrives over many calls to C{dataReceived}. -        Not used in any other state. +    @ivar _trailerHeaders: Accumulates raw/unparsed trailer headers. +        See https://github.com/twisted/twisted/issues/12014 + +    @ivar _maxTrailerHeadersSize: Maximum bytes for trailer header from the +        response. +    @type _maxTrailerHeadersSize: C{int} + +    @ivar _receivedTrailerHeadersSize: Bytes received so far for the tailer headers. +    @type _receivedTrailerHeadersSize: C{int}      """      state = "CHUNK_LENGTH" @@ -1909,6 +1915,9 @@ class _ChunkedTransferDecoder:          self.finishCallback = finishCallback          self._buffer = bytearray()          self._start = 0 +        self._trailerHeaders: List[bytearray] = [] +        self._maxTrailerHeadersSize = 2**16 +        self._receivedTrailerHeadersSize = 0      def _dataReceived_CHUNK_LENGTH(self) -> bool:          """ @@ -1985,23 +1994,37 @@ class _ChunkedTransferDecoder:      def _dataReceived_TRAILER(self) -> bool:          """ -        Await the carriage return and line feed characters that follow the -        terminal zero-length chunk. Then invoke C{finishCallback} and switch to -        state C{'FINISHED'}. +        Collect trailer headers if received and finish at the terminal zero-length +        chunk. Then invoke C{finishCallback} and switch to state C{'FINISHED'}.          @returns: C{False}, as there is either insufficient data to continue,              or no data remains. - -        @raises _MalformedChunkedDataError: when anything other than CRLF is -            received.          """ -        if len(self._buffer) < 2: +        if ( +            self._receivedTrailerHeadersSize + len(self._buffer) +            > self._maxTrailerHeadersSize +        ): +            raise _MalformedChunkedDataError("Trailer headers data is too long.") + +        eolIndex = self._buffer.find(b"\r\n", self._start) + +        if eolIndex == -1: +            # Still no end of network line marker found. +            # Continue processing more data.              return False -        if not self._buffer.startswith(b"\r\n"): -            raise _MalformedChunkedDataError("Chunk did not end with CRLF") +        if eolIndex > 0: +            # A trailer header was detected. +            self._trailerHeaders.append(self._buffer[0:eolIndex]) +            del self._buffer[0 : eolIndex + 2] +            self._start = 0 +            self._receivedTrailerHeadersSize += eolIndex + 2 +            return True + +        # eolIndex in this part of code is equal to 0          data = memoryview(self._buffer)[2:].tobytes() +          del self._buffer[:]          self.state = "FINISHED"          self.finishCallback(data) diff --git a/contrib/python/Twisted/py3/twisted/web/pages.py b/contrib/python/Twisted/py3/twisted/web/pages.py index 54ea1c431b8..f94f8655b95 100644 --- a/contrib/python/Twisted/py3/twisted/web/pages.py +++ b/contrib/python/Twisted/py3/twisted/web/pages.py @@ -76,7 +76,7 @@ class _ErrorPage(Resource):          return self -def errorPage(code: int, brief: str, detail: str) -> IResource: +def errorPage(code: int, brief: str, detail: str) -> _ErrorPage:      """      Build a resource that responds to all requests with a particular HTTP      status code and an HTML body containing some descriptive text. This is diff --git a/contrib/python/Twisted/py3/twisted/web/resource.py b/contrib/python/Twisted/py3/twisted/web/resource.py index 33b172cdbfd..456db72d120 100644 --- a/contrib/python/Twisted/py3/twisted/web/resource.py +++ b/contrib/python/Twisted/py3/twisted/web/resource.py @@ -7,7 +7,7 @@ Implementation of the lowest-level Resource class.  See L{twisted.web.pages} for some utility implementations.  """ - +from __future__ import annotations  __all__ = [      "IResource", @@ -20,6 +20,7 @@ __all__ = [  ]  import warnings +from typing import Sequence  from zope.interface import Attribute, Interface, implementer @@ -116,6 +117,7 @@ class Resource:      """      entityType = IResource +    allowedMethods: Sequence[bytes]      server = None diff --git a/contrib/python/Twisted/py3/twisted/web/script.py b/contrib/python/Twisted/py3/twisted/web/script.py index bc4a90f748a..794eef8f630 100644 --- a/contrib/python/Twisted/py3/twisted/web/script.py +++ b/contrib/python/Twisted/py3/twisted/web/script.py @@ -77,7 +77,7 @@ def ResourceScript(path, registry):  def ResourceTemplate(path, registry): -    from quixote import ptl_compile  # type: ignore[import] +    from quixote import ptl_compile      glob = {          "__file__": _coerceToFilesystemEncoding("", path), diff --git a/contrib/python/Twisted/py3/twisted/web/server.py b/contrib/python/Twisted/py3/twisted/web/server.py index e8e01ec781b..6392a3168ae 100644 --- a/contrib/python/Twisted/py3/twisted/web/server.py +++ b/contrib/python/Twisted/py3/twisted/web/server.py @@ -101,8 +101,7 @@ class Request(Copyable, http.Request, components.Componentized):          will be transmitted only over HTTPS.      """ -    defaultContentType = b"text/html" - +    defaultContentType: Optional[bytes] = b"text/html"      site = None      appRootURL = None      prepath: Optional[List[bytes]] = None diff --git a/contrib/python/Twisted/py3/twisted/web/soap.py b/contrib/python/Twisted/py3/twisted/web/soap.py index c60bc92b916..cc44b41e2ac 100644 --- a/contrib/python/Twisted/py3/twisted/web/soap.py +++ b/contrib/python/Twisted/py3/twisted/web/soap.py @@ -16,7 +16,7 @@ Pluggable method lookup policies.  """  # SOAPpy -import SOAPpy  # type: ignore[import] +import SOAPpy  from twisted.internet import defer | 
