summaryrefslogtreecommitdiffstats
path: root/deluge/decorators.py
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2023-02-19 15:05:52 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2023-02-19 16:15:47 +0000
commitb686174b07bd56af4e5ffaa23c24f27f417fc305 (patch)
tree1ce335620d99341d94e88c159c0b9b0f6f0de5a0 /deluge/decorators.py
parentAdding debian version 2.0.3-4. (diff)
downloaddeluge-b686174b07bd56af4e5ffaa23c24f27f417fc305.tar.xz
deluge-b686174b07bd56af4e5ffaa23c24f27f417fc305.zip
Merging upstream version 2.1.1 (Closes: #1026291).
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'deluge/decorators.py')
-rw-r--r--deluge/decorators.py72
1 files changed, 63 insertions, 9 deletions
diff --git a/deluge/decorators.py b/deluge/decorators.py
index b101712..2f9fcd7 100644
--- a/deluge/decorators.py
+++ b/deluge/decorators.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Copyright (C) 2010 John Garland <johnnybg+deluge@gmail.com>
#
@@ -7,12 +6,13 @@
# See LICENSE for more details.
#
-from __future__ import unicode_literals
-
import inspect
import re
import warnings
from functools import wraps
+from typing import Any, Callable, Coroutine, TypeVar
+
+from twisted.internet import defer
def proxy(proxy_func):
@@ -56,7 +56,7 @@ def overrides(*args):
if inspect.isfunction(args[0]):
return _overrides(stack, args[0])
else:
- # One or more classes are specifed, so return a function that will be
+ # One or more classes are specified, so return a function that will be
# called with the real function as argument
def ret_func(func, **kwargs):
return _overrides(stack, func, explicit_base_classes=args)
@@ -107,7 +107,7 @@ def _overrides(stack, method, explicit_base_classes=None):
for c in base_classes + check_classes:
classes[c] = get_class(c)
- # Verify that the excplicit override class is one of base classes
+ # Verify that the explicit override class is one of base classes
if explicit_base_classes:
from itertools import product
@@ -127,7 +127,7 @@ def _overrides(stack, method, explicit_base_classes=None):
% (
method.__name__,
cls,
- 'File: %s:%s' % (stack[1][1], stack[1][2]),
+ f'File: {stack[1][1]}:{stack[1][2]}',
)
)
@@ -137,7 +137,7 @@ def _overrides(stack, method, explicit_base_classes=None):
% (
method.__name__,
check_classes,
- 'File: %s:%s' % (stack[1][1], stack[1][2]),
+ f'File: {stack[1][1]}:{stack[1][2]}',
)
)
return method
@@ -146,7 +146,7 @@ def _overrides(stack, method, explicit_base_classes=None):
def deprecated(func):
"""This is a decorator which can be used to mark function as deprecated.
- It will result in a warning being emmitted when the function is used.
+ It will result in a warning being emitted when the function is used.
"""
@@ -154,7 +154,7 @@ def deprecated(func):
def depr_func(*args, **kwargs):
warnings.simplefilter('always', DeprecationWarning) # Turn off filter
warnings.warn(
- 'Call to deprecated function {}.'.format(func.__name__),
+ f'Call to deprecated function {func.__name__}.',
category=DeprecationWarning,
stacklevel=2,
)
@@ -162,3 +162,57 @@ def deprecated(func):
return func(*args, **kwargs)
return depr_func
+
+
+class CoroutineDeferred(defer.Deferred):
+ """Wraps a coroutine in a Deferred.
+ It will dynamically pass through the underlying coroutine without wrapping where apporpriate."""
+
+ def __init__(self, coro: Coroutine):
+ # Delay this import to make sure a reactor was installed first
+ from twisted.internet import reactor
+
+ super().__init__()
+ self.coro = coro
+ self.awaited = None
+ self.activate_deferred = reactor.callLater(0, self.activate)
+
+ def __await__(self):
+ if self.awaited in [None, True]:
+ self.awaited = True
+ return self.coro.__await__()
+ # Already in deferred mode
+ return super().__await__()
+
+ def activate(self):
+ """If the result wasn't awaited before the next context switch, we turn it into a deferred."""
+ if self.awaited is None:
+ self.awaited = False
+ try:
+ d = defer.Deferred.fromCoroutine(self.coro)
+ except AttributeError:
+ # Fallback for Twisted <= 21.2 without fromCoroutine
+ d = defer.ensureDeferred(self.coro)
+ d.chainDeferred(self)
+
+ def addCallbacks(self, *args, **kwargs): # noqa: N802
+ assert not self.awaited, 'Cannot add callbacks to an already awaited coroutine.'
+ self.activate()
+ return super().addCallbacks(*args, **kwargs)
+
+
+_RetT = TypeVar('_RetT')
+
+
+def maybe_coroutine(
+ f: Callable[..., Coroutine[Any, Any, _RetT]]
+) -> 'Callable[..., defer.Deferred[_RetT]]':
+ """Wraps a coroutine function to make it usable as a normal function that returns a Deferred."""
+
+ @wraps(f)
+ def wrapper(*args, **kwargs):
+ # Uncomment for quick testing to make sure CoroutineDeferred magic isn't at fault
+ # return defer.ensureDeferred(f(*args, **kwargs))
+ return CoroutineDeferred(f(*args, **kwargs))
+
+ return wrapper