876 changes: 538 additions & 338 deletions src/twisted/internet/defer.py

Large diffs are not rendered by default.

110 changes: 79 additions & 31 deletions src/twisted/internet/interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
ServerFactory,
)
from twisted.internet.ssl import ClientContextFactory
from twisted.names.dns import Query
from twisted.names.dns import Query, RRHeader
wsanchez marked this conversation as resolved.
Show resolved Hide resolved
from twisted.protocols.tls import TLSMemoryBIOProtocol

from twisted.python.runtime import platform
Expand Down Expand Up @@ -104,7 +104,7 @@ def getDestination() -> IAddress:


class IResolverSimple(Interface):
def getHostByName(name: str, timeout: Sequence[int]) -> "Deferred":
def getHostByName(name: str, timeout: Sequence[int]) -> "Deferred[str]":
"""
Resolve the domain name C{name} into an IP address.
Expand Down Expand Up @@ -221,7 +221,9 @@ def resolveHostName(


class IResolver(IResolverSimple):
def query(query: "Query", timeout: Sequence[int]) -> "Deferred":
def query(
query: "Query", timeout: Sequence[int]
) -> "Deferred[Tuple[RRHeader, RRHeader, RRHeader]]":
"""
Dispatch C{query} to the method which can handle its type.
Expand All @@ -239,7 +241,9 @@ def query(query: "Query", timeout: Sequence[int]) -> "Deferred":
C{NotImplementedError}.
"""

def lookupAddress(name: str, timeout: Sequence[int]) -> "Deferred":
def lookupAddress(
name: str, timeout: Sequence[int]
) -> "Deferred[Tuple[RRHeader, RRHeader, RRHeader]]":
"""
Perform an A record lookup.
Expand All @@ -256,7 +260,9 @@ def lookupAddress(name: str, timeout: Sequence[int]) -> "Deferred":
C{NotImplementedError}.
"""

def lookupAddress6(name: str, timeout: Sequence[int]) -> "Deferred":
def lookupAddress6(
name: str, timeout: Sequence[int]
) -> "Deferred[Tuple[RRHeader, RRHeader, RRHeader]]":
"""
Perform an A6 record lookup.
Expand All @@ -273,7 +279,9 @@ def lookupAddress6(name: str, timeout: Sequence[int]) -> "Deferred":
C{NotImplementedError}.
"""

def lookupIPV6Address(name: str, timeout: Sequence[int]) -> "Deferred":
def lookupIPV6Address(
name: str, timeout: Sequence[int]
) -> "Deferred[Tuple[RRHeader, RRHeader, RRHeader]]":
"""
Perform an AAAA record lookup.
Expand All @@ -290,7 +298,9 @@ def lookupIPV6Address(name: str, timeout: Sequence[int]) -> "Deferred":
C{NotImplementedError}.
"""

def lookupMailExchange(name: str, timeout: Sequence[int]) -> "Deferred":
def lookupMailExchange(
name: str, timeout: Sequence[int]
) -> "Deferred[Tuple[RRHeader, RRHeader, RRHeader]]":
"""
Perform an MX record lookup.
Expand All @@ -307,7 +317,9 @@ def lookupMailExchange(name: str, timeout: Sequence[int]) -> "Deferred":
C{NotImplementedError}.
"""

def lookupNameservers(name: str, timeout: Sequence[int]) -> "Deferred":
def lookupNameservers(
name: str, timeout: Sequence[int]
) -> "Deferred[Tuple[RRHeader, RRHeader, RRHeader]]":
"""
Perform an NS record lookup.
Expand All @@ -324,7 +336,9 @@ def lookupNameservers(name: str, timeout: Sequence[int]) -> "Deferred":
C{NotImplementedError}.
"""

def lookupCanonicalName(name: str, timeout: Sequence[int]) -> "Deferred":
def lookupCanonicalName(
name: str, timeout: Sequence[int]
) -> "Deferred[Tuple[RRHeader, RRHeader, RRHeader]]":
"""
Perform a CNAME record lookup.
Expand All @@ -341,7 +355,9 @@ def lookupCanonicalName(name: str, timeout: Sequence[int]) -> "Deferred":
C{NotImplementedError}.
"""

def lookupMailBox(name: str, timeout: Sequence[int]) -> "Deferred":
def lookupMailBox(
name: str, timeout: Sequence[int]
) -> "Deferred[Tuple[RRHeader, RRHeader, RRHeader]]":
"""
Perform an MB record lookup.
Expand All @@ -358,7 +374,9 @@ def lookupMailBox(name: str, timeout: Sequence[int]) -> "Deferred":
C{NotImplementedError}.
"""

def lookupMailGroup(name: str, timeout: Sequence[int]) -> "Deferred":
def lookupMailGroup(
name: str, timeout: Sequence[int]
) -> "Deferred[Tuple[RRHeader, RRHeader, RRHeader]]":
"""
Perform an MG record lookup.
Expand All @@ -375,7 +393,9 @@ def lookupMailGroup(name: str, timeout: Sequence[int]) -> "Deferred":
C{NotImplementedError}.
"""

def lookupMailRename(name: str, timeout: Sequence[int]) -> "Deferred":
def lookupMailRename(
name: str, timeout: Sequence[int]
) -> "Deferred[Tuple[RRHeader, RRHeader, RRHeader]]":
"""
Perform an MR record lookup.
Expand All @@ -392,7 +412,9 @@ def lookupMailRename(name: str, timeout: Sequence[int]) -> "Deferred":
C{NotImplementedError}.
"""

def lookupPointer(name: str, timeout: Sequence[int]) -> "Deferred":
def lookupPointer(
name: str, timeout: Sequence[int]
) -> "Deferred[Tuple[RRHeader, RRHeader, RRHeader]]":
"""
Perform a PTR record lookup.
Expand All @@ -409,7 +431,9 @@ def lookupPointer(name: str, timeout: Sequence[int]) -> "Deferred":
C{NotImplementedError}.
"""

def lookupAuthority(name: str, timeout: Sequence[int]) -> "Deferred":
def lookupAuthority(
name: str, timeout: Sequence[int]
) -> "Deferred[Tuple[RRHeader, RRHeader, RRHeader]]":
"""
Perform an SOA record lookup.
Expand All @@ -426,7 +450,9 @@ def lookupAuthority(name: str, timeout: Sequence[int]) -> "Deferred":
C{NotImplementedError}.
"""

def lookupNull(name: str, timeout: Sequence[int]) -> "Deferred":
def lookupNull(
name: str, timeout: Sequence[int]
) -> "Deferred[Tuple[RRHeader, RRHeader, RRHeader]]":
"""
Perform a NULL record lookup.
Expand All @@ -443,7 +469,9 @@ def lookupNull(name: str, timeout: Sequence[int]) -> "Deferred":
C{NotImplementedError}.
"""

def lookupWellKnownServices(name: str, timeout: Sequence[int]) -> "Deferred":
def lookupWellKnownServices(
name: str, timeout: Sequence[int]
) -> "Deferred[Tuple[RRHeader, RRHeader, RRHeader]]":
"""
Perform a WKS record lookup.
Expand All @@ -460,7 +488,9 @@ def lookupWellKnownServices(name: str, timeout: Sequence[int]) -> "Deferred":
C{NotImplementedError}.
"""

def lookupHostInfo(name: str, timeout: Sequence[int]) -> "Deferred":
def lookupHostInfo(
name: str, timeout: Sequence[int]
) -> "Deferred[Tuple[RRHeader, RRHeader, RRHeader]]":
"""
Perform a HINFO record lookup.
Expand All @@ -477,7 +507,9 @@ def lookupHostInfo(name: str, timeout: Sequence[int]) -> "Deferred":
C{NotImplementedError}.
"""

def lookupMailboxInfo(name: str, timeout: Sequence[int]) -> "Deferred":
def lookupMailboxInfo(
name: str, timeout: Sequence[int]
) -> "Deferred[Tuple[RRHeader, RRHeader, RRHeader]]":
"""
Perform an MINFO record lookup.
Expand All @@ -494,7 +526,9 @@ def lookupMailboxInfo(name: str, timeout: Sequence[int]) -> "Deferred":
C{NotImplementedError}.
"""

def lookupText(name: str, timeout: Sequence[int]) -> "Deferred":
def lookupText(
name: str, timeout: Sequence[int]
) -> "Deferred[Tuple[RRHeader, RRHeader, RRHeader]]":
"""
Perform a TXT record lookup.
Expand All @@ -511,7 +545,9 @@ def lookupText(name: str, timeout: Sequence[int]) -> "Deferred":
C{NotImplementedError}.
"""

def lookupResponsibility(name: str, timeout: Sequence[int]) -> "Deferred":
def lookupResponsibility(
name: str, timeout: Sequence[int]
) -> "Deferred[Tuple[RRHeader, RRHeader, RRHeader]]":
"""
Perform an RP record lookup.
Expand All @@ -528,7 +564,9 @@ def lookupResponsibility(name: str, timeout: Sequence[int]) -> "Deferred":
C{NotImplementedError}.
"""

def lookupAFSDatabase(name: str, timeout: Sequence[int]) -> "Deferred":
def lookupAFSDatabase(
name: str, timeout: Sequence[int]
) -> "Deferred[Tuple[RRHeader, RRHeader, RRHeader]]":
"""
Perform an AFSDB record lookup.
Expand All @@ -545,7 +583,9 @@ def lookupAFSDatabase(name: str, timeout: Sequence[int]) -> "Deferred":
C{NotImplementedError}.
"""

def lookupService(name: str, timeout: Sequence[int]) -> "Deferred":
def lookupService(
name: str, timeout: Sequence[int]
) -> "Deferred[Tuple[RRHeader, RRHeader, RRHeader]]":
"""
Perform an SRV record lookup.
Expand All @@ -562,7 +602,9 @@ def lookupService(name: str, timeout: Sequence[int]) -> "Deferred":
C{NotImplementedError}.
"""

def lookupAllRecords(name: str, timeout: Sequence[int]) -> "Deferred":
def lookupAllRecords(
name: str, timeout: Sequence[int]
) -> "Deferred[Tuple[RRHeader, RRHeader, RRHeader]]":
"""
Perform an ALL_RECORD lookup.
Expand All @@ -579,7 +621,9 @@ def lookupAllRecords(name: str, timeout: Sequence[int]) -> "Deferred":
C{NotImplementedError}.
"""

def lookupSenderPolicy(name: str, timeout: Sequence[int]) -> "Deferred":
def lookupSenderPolicy(
name: str, timeout: Sequence[int]
) -> "Deferred[Tuple[RRHeader, RRHeader, RRHeader]]":
"""
Perform a SPF record lookup.
Expand All @@ -596,7 +640,9 @@ def lookupSenderPolicy(name: str, timeout: Sequence[int]) -> "Deferred":
C{NotImplementedError}.
"""

def lookupNamingAuthorityPointer(name: str, timeout: Sequence[int]) -> "Deferred":
def lookupNamingAuthorityPointer(
name: str, timeout: Sequence[int]
) -> "Deferred[Tuple[RRHeader, RRHeader, RRHeader]]":
"""
Perform a NAPTR record lookup.
Expand All @@ -613,7 +659,9 @@ def lookupNamingAuthorityPointer(name: str, timeout: Sequence[int]) -> "Deferred
C{NotImplementedError}.
"""

def lookupZone(name: str, timeout: Sequence[int]) -> "Deferred":
def lookupZone(
name: str, timeout: Sequence[int]
) -> "Deferred[Tuple[RRHeader, RRHeader, RRHeader]]":
"""
Perform an AXFR record lookup.
Expand Down Expand Up @@ -1287,7 +1335,7 @@ class IReactorCore(Interface):
"I{during shutdown} and C{False} the rest of the time."
)

def resolve(name: str, timeout: Sequence[int]) -> "Deferred":
def resolve(name: str, timeout: Sequence[int]) -> "Deferred[str]":
"""
Return a L{twisted.internet.defer.Deferred} that will resolve
a hostname.
Expand Down Expand Up @@ -2559,15 +2607,15 @@ def setTTL(ttl: int) -> None:
Set time to live on multicast packets.
"""

def joinGroup(addr: str, interface: str) -> "Deferred":
def joinGroup(addr: str, interface: str) -> "Deferred[None]":
"""
Join a multicast group. Returns L{Deferred} of success or failure.
If an error occurs, the returned L{Deferred} will fail with
L{error.MulticastJoinError}.
"""

def leaveGroup(addr: str, interface: str) -> "Deferred":
def leaveGroup(addr: str, interface: str) -> "Deferred[None]":
"""
Leave multicast group, return L{Deferred} of success.
"""
Expand All @@ -2581,7 +2629,7 @@ class IStreamClientEndpoint(Interface):
@since: 10.1
"""

def connect(protocolFactory: IProtocolFactory) -> "Deferred":
def connect(protocolFactory: IProtocolFactory) -> "Deferred[IProtocol]":
"""
Connect the C{protocolFactory} to the location specified by this
L{IStreamClientEndpoint} provider.
Expand All @@ -2602,7 +2650,7 @@ class IStreamServerEndpoint(Interface):
@since: 10.1
"""

def listen(protocolFactory: IProtocolFactory) -> "Deferred":
def listen(protocolFactory: IProtocolFactory) -> "Deferred[IListeningPort]":
"""
Listen with C{protocolFactory} at the location specified by this
L{IStreamServerEndpoint} provider.
Expand Down
Empty file.
Empty file.
2 changes: 1 addition & 1 deletion src/twisted/persisted/crefutil.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ def __init__(self, id):
from twisted.internet.defer import Deferred


class _Defer(Deferred, NotKnown):
class _Defer(Deferred[object], NotKnown):
def __init__(self):
Deferred.__init__(self)
NotKnown.__init__(self)
Expand Down
15 changes: 8 additions & 7 deletions src/twisted/python/compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
from io import StringIO as NativeStringIO
from io import TextIOBase
from sys import intern
from types import MethodType as _MethodType
from types import FrameType, MethodType as _MethodType
wsanchez marked this conversation as resolved.
Show resolved Hide resolved
from typing import Any, AnyStr, cast
from urllib.parse import quote as urlquote
from urllib.parse import unquote as urlunquote
Expand Down Expand Up @@ -187,22 +187,21 @@ def items(d):
return list(d.items())


def currentframe(n=0):
def currentframe(n: int = 0) -> FrameType:
"""
In Python 3, L{inspect.currentframe} does not take a stack-level argument.
Restore that functionality from Python 2 so we don't have to re-implement
the C{f_back}-walking loop in places where it's called.
@param n: The number of stack levels above the caller to walk.
@type n: L{int}
@return: a frame, n levels up the stack from the caller.
@rtype: L{types.FrameType}
"""
f = inspect.currentframe()
for x in range(n + 1):
assert f is not None
f = f.f_back
assert f is not None
return f


Expand All @@ -225,14 +224,16 @@ def execfile(filename, globals, locals=None):
exec(code, globals, locals)


def cmp(a, b):
# type note: Can't find a Comparable type, despite
# https://github.com/python/typing/issues/59
def cmp(a: object, b: object) -> int:
"""
Compare two objects.
Returns a negative number if C{a < b}, zero if they are equal, and a
positive number if C{a > b}.
"""
if a < b:
if a < b: # type: ignore[operator]
return -1
elif a == b:
return 0
Expand Down Expand Up @@ -500,7 +501,7 @@ def _constructMethod(cls, name, self):
@type self: any object
@return: a bound method
@rtype: L{types.MethodType}
@rtype: L{_MethodType}
"""
func = cls.__dict__[name]
return _MethodType(func, self)
Expand Down
38 changes: 38 additions & 0 deletions src/twisted/test/test_defer.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,44 @@ def testTwoCallbacks(self):
self.assertEqual(self.callbackResults, (("hello",), {}))
self.assertEqual(self.callback2Results, (("hello",), {}))

def test_addCallbacksNoneErrback(self):
"""
If given None for an errback, addCallbacks uses a pass-through.
"""
error = GenericError("oopsie")
deferred = defer.Deferred()
deferred.addCallbacks(self._callback, None)
deferred.errback(error)
deferred.addErrback(self._errback)
self.assertIsNone(self.callbackResults)
self.assertEqual(len(self.errbackResults[0]), 1)
self.assertEqual(self.errbackResults[0][0].value, error)
self.assertEqual(self.errbackResults[1], {})

def test_addCallbacksNoneCallbackArgs(self):
"""
If given None as a callback args and kwargs, () and {} are used.
"""
deferred = defer.Deferred()
deferred.addCallbacks(self._callback, self._errback, None, None, (), {})
deferred.callback("hello")
self.assertIsNone(self.errbackResults)
self.assertEqual(self.callbackResults, (("hello",), {}))

def test_addCallbacksNoneErrbackArgs(self):
"""
If given None as a errback args and kwargs, () and {} are used.
"""
error = GenericError("oopsie")
deferred = defer.Deferred()
deferred.addCallbacks(self._callback, self._errback, (), {}, None, None)
deferred.errback(error)
deferred.addErrback(self._errback)
self.assertIsNone(self.callbackResults)
self.assertEqual(len(self.errbackResults[0]), 1)
self.assertEqual(self.errbackResults[0][0].value, error)
self.assertEqual(self.errbackResults[1], {})

def testDeferredList(self):
defr1 = defer.Deferred()
defr2 = defer.Deferred()
Expand Down