-
-
Notifications
You must be signed in to change notification settings - Fork 32.1k
bpo-42392: Remove loop parameter form asyncio locks and Queue #23420
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
152ac8a
66fff45
0c764ff
a37d2bd
6b3ecfb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,10 +3,9 @@ | |
__all__ = ('Lock', 'Event', 'Condition', 'Semaphore', 'BoundedSemaphore') | ||
|
||
import collections | ||
import warnings | ||
|
||
from . import events | ||
from . import exceptions | ||
from . import mixins | ||
|
||
|
||
class _ContextManagerMixin: | ||
|
@@ -20,7 +19,7 @@ async def __aexit__(self, exc_type, exc, tb): | |
self.release() | ||
|
||
|
||
class Lock(_ContextManagerMixin): | ||
class Lock(_ContextManagerMixin, mixins._LoopBoundedMixin): | ||
"""Primitive lock objects. | ||
|
||
A primitive lock is a synchronization primitive that is not owned | ||
|
@@ -74,16 +73,9 @@ class Lock(_ContextManagerMixin): | |
|
||
""" | ||
|
||
def __init__(self, *, loop=None): | ||
def __init__(self): | ||
self._waiters = None | ||
self._locked = False | ||
if loop is None: | ||
self._loop = events.get_event_loop() | ||
else: | ||
self._loop = loop | ||
warnings.warn("The loop argument is deprecated since Python 3.8, " | ||
"and scheduled for removal in Python 3.10.", | ||
DeprecationWarning, stacklevel=2) | ||
|
||
def __repr__(self): | ||
res = super().__repr__() | ||
|
@@ -109,7 +101,7 @@ async def acquire(self): | |
|
||
if self._waiters is None: | ||
self._waiters = collections.deque() | ||
fut = self._loop.create_future() | ||
fut = self._get_loop().create_future() | ||
self._waiters.append(fut) | ||
|
||
# Finally block should be called before the CancelledError | ||
|
@@ -161,7 +153,7 @@ def _wake_up_first(self): | |
fut.set_result(True) | ||
|
||
|
||
class Event: | ||
class Event(mixins._LoopBoundedMixin): | ||
"""Asynchronous equivalent to threading.Event. | ||
|
||
Class implementing event objects. An event manages a flag that can be set | ||
|
@@ -170,16 +162,9 @@ class Event: | |
false. | ||
""" | ||
|
||
def __init__(self, *, loop=None): | ||
def __init__(self): | ||
self._waiters = collections.deque() | ||
self._value = False | ||
if loop is None: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd actually keep the if loop is not None:
raise TypeError(
f'passing explicit "loop" argument to {type(self).__name__}() '
f'is no longer necessary') There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I thought that we should fully remove Maybe it's better to fully remove What is your opinion? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We could do There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm +1 for raising an explicit error and using the sentinel approach to check if the user passed a loop arg. Especially with how widespread the loop parameter is used throughout existing asyncio code, it's going to be a much smoother transition to have an error message that tells users precisely what they did incorrect rather than the parameter just disappearing. (Also keeping in mind the unfortunate reality that not everyone tests with deprecation warnings on, or might not have gotten around to removing loop throughout their own code since the warnings started in 3.8) As for when the parameter should just be removed entirely without an explicit error message, I think doing that in 3.11 or 3.12 would be fine. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Now I get it, sorry that didn't understand it the first time. Give an explicit error will be better for debugging and will make the transition much easier. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Although I would make the message a little bit more direct and technically specific: if loop is not None:
raise TypeError(
f'As of 3.10, the *loop* parameter was removed from {type(self).__name__}() ')
f'since it is no longer necessary') There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
self._loop = events.get_event_loop() | ||
else: | ||
self._loop = loop | ||
warnings.warn("The loop argument is deprecated since Python 3.8, " | ||
"and scheduled for removal in Python 3.10.", | ||
DeprecationWarning, stacklevel=2) | ||
|
||
def __repr__(self): | ||
res = super().__repr__() | ||
|
@@ -220,7 +205,7 @@ async def wait(self): | |
if self._value: | ||
return True | ||
|
||
fut = self._loop.create_future() | ||
fut = self._get_loop().create_future() | ||
self._waiters.append(fut) | ||
try: | ||
await fut | ||
|
@@ -229,7 +214,7 @@ async def wait(self): | |
self._waiters.remove(fut) | ||
|
||
|
||
class Condition(_ContextManagerMixin): | ||
class Condition(_ContextManagerMixin, mixins._LoopBoundedMixin): | ||
"""Asynchronous equivalent to threading.Condition. | ||
|
||
This class implements condition variable objects. A condition variable | ||
|
@@ -239,18 +224,10 @@ class Condition(_ContextManagerMixin): | |
A new Lock object is created and used as the underlying lock. | ||
""" | ||
|
||
def __init__(self, lock=None, *, loop=None): | ||
if loop is None: | ||
self._loop = events.get_event_loop() | ||
else: | ||
self._loop = loop | ||
warnings.warn("The loop argument is deprecated since Python 3.8, " | ||
"and scheduled for removal in Python 3.10.", | ||
DeprecationWarning, stacklevel=2) | ||
|
||
def __init__(self, lock=None): | ||
if lock is None: | ||
lock = Lock(loop=loop) | ||
elif lock._loop is not self._loop: | ||
lock = Lock() | ||
elif lock._loop is not self._get_loop(): | ||
raise ValueError("loop argument must agree with lock") | ||
|
||
self._lock = lock | ||
|
@@ -284,7 +261,7 @@ async def wait(self): | |
|
||
self.release() | ||
try: | ||
fut = self._loop.create_future() | ||
fut = self._get_loop().create_future() | ||
self._waiters.append(fut) | ||
try: | ||
await fut | ||
|
@@ -351,7 +328,7 @@ def notify_all(self): | |
self.notify(len(self._waiters)) | ||
|
||
|
||
class Semaphore(_ContextManagerMixin): | ||
class Semaphore(_ContextManagerMixin, mixins._LoopBoundedMixin): | ||
"""A Semaphore implementation. | ||
|
||
A semaphore manages an internal counter which is decremented by each | ||
|
@@ -366,18 +343,11 @@ class Semaphore(_ContextManagerMixin): | |
ValueError is raised. | ||
""" | ||
|
||
def __init__(self, value=1, *, loop=None): | ||
def __init__(self, value=1): | ||
if value < 0: | ||
raise ValueError("Semaphore initial value must be >= 0") | ||
self._value = value | ||
self._waiters = collections.deque() | ||
if loop is None: | ||
self._loop = events.get_event_loop() | ||
else: | ||
self._loop = loop | ||
warnings.warn("The loop argument is deprecated since Python 3.8, " | ||
"and scheduled for removal in Python 3.10.", | ||
DeprecationWarning, stacklevel=2) | ||
|
||
def __repr__(self): | ||
res = super().__repr__() | ||
|
@@ -407,7 +377,7 @@ async def acquire(self): | |
True. | ||
""" | ||
while self._value <= 0: | ||
fut = self._loop.create_future() | ||
fut = self._get_loop().create_future() | ||
self._waiters.append(fut) | ||
try: | ||
await fut | ||
|
@@ -436,14 +406,9 @@ class BoundedSemaphore(Semaphore): | |
above the initial value. | ||
""" | ||
|
||
def __init__(self, value=1, *, loop=None): | ||
if loop: | ||
warnings.warn("The loop argument is deprecated since Python 3.8, " | ||
"and scheduled for removal in Python 3.10.", | ||
DeprecationWarning, stacklevel=2) | ||
|
||
def __init__(self, value=1): | ||
self._bound_value = value | ||
super().__init__(value, loop=loop) | ||
super().__init__(value) | ||
|
||
def release(self): | ||
if self._value >= self._bound_value: | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
"""Event loop mixins.""" | ||
|
||
import threading | ||
from . import events | ||
|
||
_global_lock = threading.Lock() | ||
|
||
|
||
class _LoopBoundedMixin: | ||
_loop = None | ||
|
||
def _get_loop(self): | ||
loop = events._get_running_loop() | ||
|
||
if self._loop is None: | ||
with _global_lock: | ||
if self._loop is None: | ||
self._loop = loop | ||
if loop is not self._loop: | ||
raise RuntimeError(f'{type(self).__name__} have already bounded to another loop') | ||
uriyyo marked this conversation as resolved.
Show resolved
Hide resolved
uriyyo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return loop |
Uh oh!
There was an error while loading. Please reload this page.