summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYurii Karabas <1998uriyyo@gmail.com>2020-11-25 13:50:44 +0200
committerGitHub <noreply@github.com>2020-11-25 06:50:44 -0500
commitb9127dd6eedd693cfd716a4444648864e2e00186 (patch)
treee764966cdfca102d15e1a281312d096c0388c2e4
parent7301979b23406220510dd2c7934a21b41b647119 (diff)
downloadcpython-git-b9127dd6eedd693cfd716a4444648864e2e00186.tar.gz
bpo-42392: Improve removal of *loop* parameter in asyncio primitives (GH-23499)
* Update code after merge review from 1st1 * Use a sentinel approach for loop parameter Remove unnecessary _get_running_loop patching * Use more clear function name (_verify_parameter_is_marker -> _verify_no_loop) * Add init method to _LoopBoundMixin to check that loop param wasn't used
-rw-r--r--Lib/asyncio/locks.py24
-rw-r--r--Lib/asyncio/mixins.py14
-rw-r--r--Lib/asyncio/queues.py5
-rw-r--r--Lib/test/test_asyncio/test_locks.py17
-rw-r--r--Lib/test/test_asyncio/utils.py21
5 files changed, 46 insertions, 35 deletions
diff --git a/Lib/asyncio/locks.py b/Lib/asyncio/locks.py
index 6f322c258c..a7453fb1c7 100644
--- a/Lib/asyncio/locks.py
+++ b/Lib/asyncio/locks.py
@@ -19,7 +19,7 @@ class _ContextManagerMixin:
self.release()
-class Lock(_ContextManagerMixin, mixins._LoopBoundedMixin):
+class Lock(_ContextManagerMixin, mixins._LoopBoundMixin):
"""Primitive lock objects.
A primitive lock is a synchronization primitive that is not owned
@@ -73,7 +73,8 @@ class Lock(_ContextManagerMixin, mixins._LoopBoundedMixin):
"""
- def __init__(self):
+ def __init__(self, *, loop=mixins._marker):
+ super().__init__(loop=loop)
self._waiters = None
self._locked = False
@@ -153,7 +154,7 @@ class Lock(_ContextManagerMixin, mixins._LoopBoundedMixin):
fut.set_result(True)
-class Event(mixins._LoopBoundedMixin):
+class Event(mixins._LoopBoundMixin):
"""Asynchronous equivalent to threading.Event.
Class implementing event objects. An event manages a flag that can be set
@@ -162,7 +163,8 @@ class Event(mixins._LoopBoundedMixin):
false.
"""
- def __init__(self):
+ def __init__(self, *, loop=mixins._marker):
+ super().__init__(loop=loop)
self._waiters = collections.deque()
self._value = False
@@ -214,7 +216,7 @@ class Event(mixins._LoopBoundedMixin):
self._waiters.remove(fut)
-class Condition(_ContextManagerMixin, mixins._LoopBoundedMixin):
+class Condition(_ContextManagerMixin, mixins._LoopBoundMixin):
"""Asynchronous equivalent to threading.Condition.
This class implements condition variable objects. A condition variable
@@ -224,7 +226,8 @@ class Condition(_ContextManagerMixin, mixins._LoopBoundedMixin):
A new Lock object is created and used as the underlying lock.
"""
- def __init__(self, lock=None):
+ def __init__(self, lock=None, *, loop=mixins._marker):
+ super().__init__(loop=loop)
if lock is None:
lock = Lock()
elif lock._loop is not self._get_loop():
@@ -328,7 +331,7 @@ class Condition(_ContextManagerMixin, mixins._LoopBoundedMixin):
self.notify(len(self._waiters))
-class Semaphore(_ContextManagerMixin, mixins._LoopBoundedMixin):
+class Semaphore(_ContextManagerMixin, mixins._LoopBoundMixin):
"""A Semaphore implementation.
A semaphore manages an internal counter which is decremented by each
@@ -343,7 +346,8 @@ class Semaphore(_ContextManagerMixin, mixins._LoopBoundedMixin):
ValueError is raised.
"""
- def __init__(self, value=1):
+ def __init__(self, value=1, *, loop=mixins._marker):
+ super().__init__(loop=loop)
if value < 0:
raise ValueError("Semaphore initial value must be >= 0")
self._value = value
@@ -406,9 +410,9 @@ class BoundedSemaphore(Semaphore):
above the initial value.
"""
- def __init__(self, value=1):
+ def __init__(self, value=1, *, loop=mixins._marker):
self._bound_value = value
- super().__init__(value)
+ super().__init__(value, loop=loop)
def release(self):
if self._value >= self._bound_value:
diff --git a/Lib/asyncio/mixins.py b/Lib/asyncio/mixins.py
index dbc4b5facc..650df05ccc 100644
--- a/Lib/asyncio/mixins.py
+++ b/Lib/asyncio/mixins.py
@@ -5,10 +5,20 @@ from . import events
_global_lock = threading.Lock()
+# Used as a sentinel for loop parameter
+_marker = object()
-class _LoopBoundedMixin:
+
+class _LoopBoundMixin:
_loop = None
+ def __init__(self, *, loop=_marker):
+ if loop is not _marker:
+ raise TypeError(
+ f'As of 3.10, the *loop* parameter was removed from '
+ f'{type(self).__name__}() since it is no longer necessary'
+ )
+
def _get_loop(self):
loop = events._get_running_loop()
@@ -17,5 +27,5 @@ class _LoopBoundedMixin:
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')
+ raise RuntimeError(f'{self!r} is bound to a different event loop')
return loop
diff --git a/Lib/asyncio/queues.py b/Lib/asyncio/queues.py
index 78ae9e99cc..a87ec8b215 100644
--- a/Lib/asyncio/queues.py
+++ b/Lib/asyncio/queues.py
@@ -17,7 +17,7 @@ class QueueFull(Exception):
pass
-class Queue(mixins._LoopBoundedMixin):
+class Queue(mixins._LoopBoundMixin):
"""A queue, useful for coordinating producer and consumer coroutines.
If maxsize is less than or equal to zero, the queue size is infinite. If it
@@ -29,7 +29,8 @@ class Queue(mixins._LoopBoundedMixin):
interrupted between calling qsize() and doing an operation on the Queue.
"""
- def __init__(self, maxsize=0):
+ def __init__(self, maxsize=0, *, loop=mixins._marker):
+ super().__init__(loop=loop)
self._maxsize = maxsize
# Futures.
diff --git a/Lib/test/test_asyncio/test_locks.py b/Lib/test/test_asyncio/test_locks.py
index 6c34ef60e3..6194cd0617 100644
--- a/Lib/test/test_asyncio/test_locks.py
+++ b/Lib/test/test_asyncio/test_locks.py
@@ -51,6 +51,23 @@ class LockTests(test_utils.TestCase):
self.assertFalse(lock.locked())
+ def test_lock_doesnt_accept_loop_parameter(self):
+ primitives_cls = [
+ asyncio.Lock,
+ asyncio.Condition,
+ asyncio.Event,
+ asyncio.Semaphore,
+ asyncio.BoundedSemaphore,
+ ]
+
+ for cls in primitives_cls:
+ with self.assertRaisesRegex(
+ TypeError,
+ rf'As of 3.10, the \*loop\* parameter was removed from '
+ rf'{cls.__name__}\(\) since it is no longer necessary'
+ ):
+ cls(loop=self.loop)
+
def test_lock_by_with_statement(self):
loop = asyncio.new_event_loop() # don't use TestLoop quirks
self.set_event_loop(loop)
diff --git a/Lib/test/test_asyncio/utils.py b/Lib/test/test_asyncio/utils.py
index aba90c970a..67180f7eb3 100644
--- a/Lib/test/test_asyncio/utils.py
+++ b/Lib/test/test_asyncio/utils.py
@@ -541,31 +541,10 @@ class TestCase(unittest.TestCase):
self.set_event_loop(loop)
return loop
- def unpatch_get_running_loop(self):
- events._get_running_loop = self._get_running_loop
-
def setUp(self):
- self._get_running_loop = events._get_running_loop
-
- def _get_running_loop():
- frame = sys._getframe(1)
-
- if frame.f_globals['__name__'] == 'asyncio.mixins':
- # When we called from LoopBoundedMixin we should
- # fallback to default implementation of get_running_loop
- try:
- return events.get_running_loop()
- except RuntimeError:
- return None
-
- return None
-
- events._get_running_loop = _get_running_loop
self._thread_cleanup = threading_helper.threading_setup()
def tearDown(self):
- self.unpatch_get_running_loop()
-
events.set_event_loop(None)
# Detect CPython bug #23353: ensure that yield/yield-from is not used