[Python-checkins] bpo-34793: Drop old-style context managers in asyncio.locks (GH-17533)

Andrew Svetlov webhook-mailer at python.org
Sat Feb 1 06:12:56 EST 2020


https://github.com/python/cpython/commit/90d9ba6ef10af32e8dfe0649789c3a8ccf419e95
commit: 90d9ba6ef10af32e8dfe0649789c3a8ccf419e95
branch: master
author: Andrew Svetlov <andrew.svetlov at gmail.com>
committer: GitHub <noreply at github.com>
date: 2020-02-01T13:12:52+02:00
summary:

bpo-34793: Drop old-style context managers in asyncio.locks (GH-17533)

files:
A Misc/NEWS.d/next/Library/2019-12-09-17-24-29.bpo-34793.D82Dyu.rst
M Doc/library/asyncio-sync.rst
M Doc/whatsnew/3.9.rst
M Lib/asyncio/locks.py
M Lib/test/test_asyncio/test_locks.py
M Lib/test/test_asyncio/test_pep492.py

diff --git a/Doc/library/asyncio-sync.rst b/Doc/library/asyncio-sync.rst
index f080b03bc7c51..84a52cb2d5757 100644
--- a/Doc/library/asyncio-sync.rst
+++ b/Doc/library/asyncio-sync.rst
@@ -347,8 +347,8 @@ BoundedSemaphore
 ---------
 
 
-.. deprecated:: 3.7
+.. versionchanged:: 3.9
 
    Acquiring a lock using ``await lock`` or ``yield from lock`` and/or
    :keyword:`with` statement (``with await lock``, ``with (yield from
-   lock)``) is deprecated.  Use ``async with lock`` instead.
+   lock)``) was removed.  Use ``async with lock`` instead.
diff --git a/Doc/whatsnew/3.9.rst b/Doc/whatsnew/3.9.rst
index c8f407751ec5e..ee37b5a5b7265 100644
--- a/Doc/whatsnew/3.9.rst
+++ b/Doc/whatsnew/3.9.rst
@@ -470,6 +470,11 @@ Removed
   :exc:`DeprecationWarning` since Python 3.8.
   (Contributed by Inada Naoki in :issue:`39377`)
 
+* ``with (await asyncio.lock):`` and ``with (yield from asyncio.lock):`` statements are
+  not longer supported, use ``async with lock`` instead.  The same is correct for
+  ``asyncio.Condition`` and ``asyncio.Semaphore``.
+  (Contributed by Andrew Svetlov in :issue:`34793`.)
+
 
 Porting to Python 3.9
 =====================
diff --git a/Lib/asyncio/locks.py b/Lib/asyncio/locks.py
index d94daeb5a173f..f1ce7324785ba 100644
--- a/Lib/asyncio/locks.py
+++ b/Lib/asyncio/locks.py
@@ -3,96 +3,13 @@
 __all__ = ('Lock', 'Event', 'Condition', 'Semaphore', 'BoundedSemaphore')
 
 import collections
-import types
 import warnings
 
 from . import events
-from . import futures
 from . import exceptions
-from .import coroutines
-
-
-class _ContextManager:
-    """Context manager.
-
-    This enables the following idiom for acquiring and releasing a
-    lock around a block:
-
-        with (yield from lock):
-            <block>
-
-    while failing loudly when accidentally using:
-
-        with lock:
-            <block>
-
-    Deprecated, use 'async with' statement:
-        async with lock:
-            <block>
-    """
-
-    def __init__(self, lock):
-        self._lock = lock
-
-    def __enter__(self):
-        # We have no use for the "as ..."  clause in the with
-        # statement for locks.
-        return None
-
-    def __exit__(self, *args):
-        try:
-            self._lock.release()
-        finally:
-            self._lock = None  # Crudely prevent reuse.
 
 
 class _ContextManagerMixin:
-    def __enter__(self):
-        raise RuntimeError(
-            '"yield from" should be used as context manager expression')
-
-    def __exit__(self, *args):
-        # This must exist because __enter__ exists, even though that
-        # always raises; that's how the with-statement works.
-        pass
-
-    @types.coroutine
-    def __iter__(self):
-        # This is not a coroutine.  It is meant to enable the idiom:
-        #
-        #     with (yield from lock):
-        #         <block>
-        #
-        # as an alternative to:
-        #
-        #     yield from lock.acquire()
-        #     try:
-        #         <block>
-        #     finally:
-        #         lock.release()
-        # Deprecated, use 'async with' statement:
-        #     async with lock:
-        #         <block>
-        warnings.warn("'with (yield from lock)' is deprecated "
-                      "use 'async with lock' instead",
-                      DeprecationWarning, stacklevel=2)
-        yield from self.acquire()
-        return _ContextManager(self)
-
-    # The flag is needed for legacy asyncio.iscoroutine()
-    __iter__._is_coroutine = coroutines._is_coroutine
-
-    async def __acquire_ctx(self):
-        await self.acquire()
-        return _ContextManager(self)
-
-    def __await__(self):
-        warnings.warn("'with await lock' is deprecated "
-                      "use 'async with lock' instead",
-                      DeprecationWarning, stacklevel=2)
-        # To make "with await lock" work.
-        return self.__acquire_ctx().__await__()
-
     async def __aenter__(self):
         await self.acquire()
         # We have no use for the "as ..."  clause in the with
diff --git a/Lib/test/test_asyncio/test_locks.py b/Lib/test/test_asyncio/test_locks.py
index 9468e740b3c1d..8c93fae2b51c6 100644
--- a/Lib/test/test_asyncio/test_locks.py
+++ b/Lib/test/test_asyncio/test_locks.py
@@ -47,13 +47,7 @@ def test_repr(self):
         self.assertTrue(repr(lock).endswith('[unlocked]>'))
         self.assertTrue(RGX_REPR.match(repr(lock)))
 
-        with self.assertWarns(DeprecationWarning):
-            @asyncio.coroutine
-            def acquire_lock():
-                with self.assertWarns(DeprecationWarning):
-                    yield from lock
-
-        self.loop.run_until_complete(acquire_lock())
+        self.loop.run_until_complete(lock.acquire())
         self.assertTrue(repr(lock).endswith('[locked]>'))
         self.assertTrue(RGX_REPR.match(repr(lock)))
 
@@ -61,18 +55,16 @@ def test_lock(self):
         with self.assertWarns(DeprecationWarning):
             lock = asyncio.Lock(loop=self.loop)
 
-
             @asyncio.coroutine
             def acquire_lock():
-                with self.assertWarns(DeprecationWarning):
-                    return (yield from lock)
-
-        res = self.loop.run_until_complete(acquire_lock())
+                return (yield from lock)
 
-        self.assertTrue(res)
-        self.assertTrue(lock.locked())
+        with self.assertRaisesRegex(
+            TypeError,
+            "object is not iterable"
+        ):
+            self.loop.run_until_complete(acquire_lock())
 
-        lock.release()
         self.assertFalse(lock.locked())
 
     def test_lock_by_with_statement(self):
@@ -90,13 +82,13 @@ def test_lock_by_with_statement(self):
             def test(lock):
                 yield from asyncio.sleep(0.01)
                 self.assertFalse(lock.locked())
-                with self.assertWarns(DeprecationWarning):
-                    with (yield from lock) as _lock:
-                        self.assertIs(_lock, None)
-                        self.assertTrue(lock.locked())
-                        yield from asyncio.sleep(0.01)
-                        self.assertTrue(lock.locked())
-                    self.assertFalse(lock.locked())
+                with self.assertRaisesRegex(
+                    TypeError,
+                    "object is not iterable"
+                ):
+                    with (yield from lock):
+                        pass
+                self.assertFalse(lock.locked())
 
         for primitive in primitives:
             loop.run_until_complete(test(primitive))
@@ -302,52 +294,16 @@ def test_release_no_waiters(self):
         self.assertFalse(lock.locked())
 
     def test_context_manager(self):
-        with self.assertWarns(DeprecationWarning):
-            lock = asyncio.Lock(loop=self.loop)
+        async def f():
+            lock = asyncio.Lock()
+            self.assertFalse(lock.locked())
 
-            @asyncio.coroutine
-            def acquire_lock():
-                with self.assertWarns(DeprecationWarning):
-                    return (yield from lock)
+            async with lock:
+                self.assertTrue(lock.locked())
 
-        with self.loop.run_until_complete(acquire_lock()):
-            self.assertTrue(lock.locked())
+            self.assertFalse(lock.locked())
 
-        self.assertFalse(lock.locked())
-
-    def test_context_manager_cant_reuse(self):
-        with self.assertWarns(DeprecationWarning):
-            lock = asyncio.Lock(loop=self.loop)
-
-            @asyncio.coroutine
-            def acquire_lock():
-                with self.assertWarns(DeprecationWarning):
-                    return (yield from lock)
-
-        # This spells "yield from lock" outside a generator.
-        cm = self.loop.run_until_complete(acquire_lock())
-        with cm:
-            self.assertTrue(lock.locked())
-
-        self.assertFalse(lock.locked())
-
-        with self.assertRaises(AttributeError):
-            with cm:
-                pass
-
-    def test_context_manager_no_yield(self):
-        with self.assertWarns(DeprecationWarning):
-            lock = asyncio.Lock(loop=self.loop)
-
-        try:
-            with lock:
-                self.fail('RuntimeError is not raised in with expression')
-        except RuntimeError as err:
-            self.assertEqual(
-                str(err),
-                '"yield from" should be used as context manager expression')
-
-        self.assertFalse(lock.locked())
+        self.loop.run_until_complete(f())
 
 
 class EventTests(test_utils.TestCase):
@@ -809,33 +765,14 @@ def test_repr(self):
         self.assertTrue(RGX_REPR.match(repr(cond)))
 
     def test_context_manager(self):
-        with self.assertWarns(DeprecationWarning):
-            cond = asyncio.Condition(loop=self.loop)
-
-        with self.assertWarns(DeprecationWarning):
-            @asyncio.coroutine
-            def acquire_cond():
-                with self.assertWarns(DeprecationWarning):
-                    return (yield from cond)
-
-        with self.loop.run_until_complete(acquire_cond()):
-            self.assertTrue(cond.locked())
-
-        self.assertFalse(cond.locked())
-
-    def test_context_manager_no_yield(self):
-        with self.assertWarns(DeprecationWarning):
-            cond = asyncio.Condition(loop=self.loop)
-
-        try:
-            with cond:
-                self.fail('RuntimeError is not raised in with expression')
-        except RuntimeError as err:
-            self.assertEqual(
-                str(err),
-                '"yield from" should be used as context manager expression')
+        async def f():
+            cond = asyncio.Condition()
+            self.assertFalse(cond.locked())
+            async with cond:
+                self.assertTrue(cond.locked())
+            self.assertFalse(cond.locked())
 
-        self.assertFalse(cond.locked())
+        self.loop.run_until_complete(f())
 
     def test_explicit_lock(self):
         with self.assertWarns(DeprecationWarning):
@@ -920,16 +857,14 @@ def test_semaphore(self):
         with self.assertWarns(DeprecationWarning):
             @asyncio.coroutine
             def acquire_lock():
-                with self.assertWarns(DeprecationWarning):
-                    return (yield from sem)
+                return (yield from sem)
 
-        res = self.loop.run_until_complete(acquire_lock())
-
-        self.assertTrue(res)
-        self.assertTrue(sem.locked())
-        self.assertEqual(0, sem._value)
+        with self.assertRaisesRegex(
+            TypeError,
+            "'Semaphore' object is not iterable",
+        ):
+            self.loop.run_until_complete(acquire_lock())
 
-        sem.release()
         self.assertFalse(sem.locked())
         self.assertEqual(1, sem._value)
 
@@ -1064,38 +999,6 @@ def test_release_no_waiters(self):
         sem.release()
         self.assertFalse(sem.locked())
 
-    def test_context_manager(self):
-        with self.assertWarns(DeprecationWarning):
-            sem = asyncio.Semaphore(2, loop=self.loop)
-
-            @asyncio.coroutine
-            def acquire_lock():
-                with self.assertWarns(DeprecationWarning):
-                    return (yield from sem)
-
-        with self.loop.run_until_complete(acquire_lock()):
-            self.assertFalse(sem.locked())
-            self.assertEqual(1, sem._value)
-
-            with self.loop.run_until_complete(acquire_lock()):
-                self.assertTrue(sem.locked())
-
-        self.assertEqual(2, sem._value)
-
-    def test_context_manager_no_yield(self):
-        with self.assertWarns(DeprecationWarning):
-            sem = asyncio.Semaphore(2, loop=self.loop)
-
-        try:
-            with sem:
-                self.fail('RuntimeError is not raised in with expression')
-        except RuntimeError as err:
-            self.assertEqual(
-                str(err),
-                '"yield from" should be used as context manager expression')
-
-        self.assertEqual(2, sem._value)
-
 
 if __name__ == '__main__':
     unittest.main()
diff --git a/Lib/test/test_asyncio/test_pep492.py b/Lib/test/test_asyncio/test_pep492.py
index a1f27dd5721c8..c5e3a5c148357 100644
--- a/Lib/test/test_asyncio/test_pep492.py
+++ b/Lib/test/test_asyncio/test_pep492.py
@@ -77,13 +77,12 @@ def test_context_manager_with_await(self):
         async def test(lock):
             await asyncio.sleep(0.01)
             self.assertFalse(lock.locked())
-            with self.assertWarns(DeprecationWarning):
-                with await lock as _lock:
-                    self.assertIs(_lock, None)
-                    self.assertTrue(lock.locked())
-                    await asyncio.sleep(0.01)
-                    self.assertTrue(lock.locked())
-                self.assertFalse(lock.locked())
+            with self.assertRaisesRegex(
+                TypeError,
+                "can't be used in 'await' expression"
+            ):
+                with await lock:
+                    pass
 
         for primitive in primitives:
             self.loop.run_until_complete(test(primitive))
diff --git a/Misc/NEWS.d/next/Library/2019-12-09-17-24-29.bpo-34793.D82Dyu.rst b/Misc/NEWS.d/next/Library/2019-12-09-17-24-29.bpo-34793.D82Dyu.rst
new file mode 100644
index 0000000000000..2089285ecdb7f
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2019-12-09-17-24-29.bpo-34793.D82Dyu.rst
@@ -0,0 +1,3 @@
+Remove support for ``with (await asyncio.lock):`` and ``with (yield from
+asyncio.lock):``.  The same is correct for ``asyncio.Condition`` and
+``asyncio.Semaphore``.



More information about the Python-checkins mailing list