Skip to content

Commit 8054773

Browse files
committed
Merge remote-tracking branch 'origin/master' into bpo-30052-regenerate-cross-references
2 parents f0b3c0b + 1374dbb commit 8054773

File tree

18 files changed

+440
-65
lines changed

18 files changed

+440
-65
lines changed

Doc/library/contextlib.rst

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,36 @@ Functions and classes provided:
8080
Use of :class:`ContextDecorator`.
8181

8282

83+
.. decorator:: asynccontextmanager
84+
85+
Similar to :func:`~contextlib.contextmanager`, but creates an
86+
:ref:`asynchronous context manager <async-context-managers>`.
87+
88+
This function is a :term:`decorator` that can be used to define a factory
89+
function for :keyword:`async with` statement asynchronous context managers,
90+
without needing to create a class or separate :meth:`__aenter__` and
91+
:meth:`__aexit__` methods. It must be applied to an :term:`asynchronous
92+
generator` function.
93+
94+
A simple example::
95+
96+
from contextlib import asynccontextmanager
97+
98+
@asynccontextmanager
99+
async def get_connection():
100+
conn = await acquire_db_connection()
101+
try:
102+
yield
103+
finally:
104+
await release_db_connection(conn)
105+
106+
async def get_all_users():
107+
async with get_connection() as conn:
108+
return conn.query('SELECT ...')
109+
110+
.. versionadded:: 3.7
111+
112+
83113
.. function:: closing(thing)
84114

85115
Return a context manager that closes *thing* upon completion of the block. This

Doc/library/idle.rst

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ Go to File/Line
244244
single: stack viewer
245245

246246
Debugger (toggle)
247-
When actived, code entered in the Shell or run from an Editor will run
247+
When activated, code entered in the Shell or run from an Editor will run
248248
under the debugger. In the Editor, breakpoints can be set with the context
249249
menu. This feature is still incomplete and somewhat experimental.
250250

@@ -372,7 +372,7 @@ the :kbd:`Command` key on Mac OSX.
372372

373373
* :kbd:`C-l` center window around the insertion point
374374

375-
* :kbd:`C-b` go backwards one character without deleting (usually you can
375+
* :kbd:`C-b` go backward one character without deleting (usually you can
376376
also use the cursor key for this)
377377

378378
* :kbd:`C-f` go forward one character without deleting (usually you can
@@ -394,7 +394,7 @@ After a block-opening statement, the next line is indented by 4 spaces (in the
394394
Python Shell window by one tab). After certain keywords (break, return etc.)
395395
the next line is dedented. In leading indentation, :kbd:`Backspace` deletes up
396396
to 4 spaces if they are there. :kbd:`Tab` inserts spaces (in the Python
397-
Shell window one tab), number depends on Indent width. Currently tabs
397+
Shell window one tab), number depends on Indent width. Currently, tabs
398398
are restricted to four spaces due to Tcl/Tk limitations.
399399

400400
See also the indent/dedent region commands in the edit menu.
@@ -418,7 +418,7 @@ If there is only one possible completion for the characters entered, a
418418
:kbd:`C-space` will open a completions window. In an empty
419419
string, this will contain the files in the current directory. On a
420420
blank line, it will contain the built-in and user-defined functions and
421-
classes in the current name spaces, plus any modules imported. If some
421+
classes in the current namespaces, plus any modules imported. If some
422422
characters have been entered, the ACW will attempt to be more specific.
423423

424424
If a string of characters is typed, the ACW selection will jump to the
@@ -557,7 +557,7 @@ IDLE-console differences
557557

558558
As much as possible, the result of executing Python code with IDLE is the
559559
same as executing the same code in a console window. However, the different
560-
interface and operation occasionally affects visible results. For instance,
560+
interface and operation occasionally affect visible results. For instance,
561561
``sys.modules`` starts with more entries.
562562

563563
IDLE also replaces ``sys.stdin``, ``sys.stdout``, and ``sys.stderr`` with
@@ -583,7 +583,7 @@ If firewall software complains anyway, you can ignore it.
583583

584584
If the attempt to make the socket connection fails, Idle will notify you.
585585
Such failures are sometimes transient, but if persistent, the problem
586-
may be either a firewall blocking the connecton or misconfiguration of
586+
may be either a firewall blocking the connection or misconfiguration of
587587
a particular system. Until the problem is fixed, one can run Idle with
588588
the -n command line switch.
589589

@@ -619,14 +619,14 @@ Setting preferences
619619

620620
The font preferences, highlighting, keys, and general preferences can be
621621
changed via Configure IDLE on the Option menu. Keys can be user defined;
622-
IDLE ships with four built in key sets. In addition a user can create a
622+
IDLE ships with four built-in key sets. In addition, a user can create a
623623
custom key set in the Configure IDLE dialog under the keys tab.
624624

625625

626626
Extensions
627627
^^^^^^^^^^
628628

629-
IDLE contains an extension facility. Peferences for extensions can be
629+
IDLE contains an extension facility. Preferences for extensions can be
630630
changed with Configure Extensions. See the beginning of config-extensions.def
631631
in the idlelib directory for further information. The default extensions
632632
are currently:

Doc/reference/datamodel.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2575,6 +2575,8 @@ An example of an asynchronous iterable object::
25752575
result in a :exc:`RuntimeError`.
25762576

25772577

2578+
.. _async-context-managers:
2579+
25782580
Asynchronous Context Managers
25792581
-----------------------------
25802582

Doc/using/windows.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -550,9 +550,9 @@ Shebang Lines
550550

551551
If the first line of a script file starts with ``#!``, it is known as a
552552
"shebang" line. Linux and other Unix like operating systems have native
553-
support for such lines and are commonly used on such systems to indicate how
554-
a script should be executed. This launcher allows the same facilities to be
555-
using with Python scripts on Windows and the examples above demonstrate their
553+
support for such lines and they are commonly used on such systems to indicate
554+
how a script should be executed. This launcher allows the same facilities to
555+
be used with Python scripts on Windows and the examples above demonstrate their
556556
use.
557557

558558
To allow shebang lines in Python scripts to be portable between Unix and

Doc/whatsnew/3.7.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,12 @@ New Modules
9595
Improved Modules
9696
================
9797

98+
contextlib
99+
----------
100+
101+
:func:`contextlib.asynccontextmanager` has been added. (Contributed by
102+
Jelle Zijlstra in :issue:`29679`.)
103+
98104
distutils
99105
---------
100106

Lib/contextlib.py

Lines changed: 93 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
from collections import deque
55
from functools import wraps
66

7-
__all__ = ["contextmanager", "closing", "AbstractContextManager",
8-
"ContextDecorator", "ExitStack", "redirect_stdout",
9-
"redirect_stderr", "suppress"]
7+
__all__ = ["asynccontextmanager", "contextmanager", "closing",
8+
"AbstractContextManager", "ContextDecorator", "ExitStack",
9+
"redirect_stdout", "redirect_stderr", "suppress"]
1010

1111

1212
class AbstractContextManager(abc.ABC):
@@ -54,8 +54,8 @@ def inner(*args, **kwds):
5454
return inner
5555

5656

57-
class _GeneratorContextManager(ContextDecorator, AbstractContextManager):
58-
"""Helper for @contextmanager decorator."""
57+
class _GeneratorContextManagerBase:
58+
"""Shared functionality for @contextmanager and @asynccontextmanager."""
5959

6060
def __init__(self, func, args, kwds):
6161
self.gen = func(*args, **kwds)
@@ -71,6 +71,12 @@ def __init__(self, func, args, kwds):
7171
# for the class instead.
7272
# See http://bugs.python.org/issue19404 for more details.
7373

74+
75+
class _GeneratorContextManager(_GeneratorContextManagerBase,
76+
AbstractContextManager,
77+
ContextDecorator):
78+
"""Helper for @contextmanager decorator."""
79+
7480
def _recreate_cm(self):
7581
# _GCM instances are one-shot context managers, so the
7682
# CM must be recreated each time a decorated function is
@@ -121,12 +127,61 @@ def __exit__(self, type, value, traceback):
121127
# fixes the impedance mismatch between the throw() protocol
122128
# and the __exit__() protocol.
123129
#
130+
# This cannot use 'except BaseException as exc' (as in the
131+
# async implementation) to maintain compatibility with
132+
# Python 2, where old-style class exceptions are not caught
133+
# by 'except BaseException'.
124134
if sys.exc_info()[1] is value:
125135
return False
126136
raise
127137
raise RuntimeError("generator didn't stop after throw()")
128138

129139

140+
class _AsyncGeneratorContextManager(_GeneratorContextManagerBase):
141+
"""Helper for @asynccontextmanager."""
142+
143+
async def __aenter__(self):
144+
try:
145+
return await self.gen.__anext__()
146+
except StopAsyncIteration:
147+
raise RuntimeError("generator didn't yield") from None
148+
149+
async def __aexit__(self, typ, value, traceback):
150+
if typ is None:
151+
try:
152+
await self.gen.__anext__()
153+
except StopAsyncIteration:
154+
return
155+
else:
156+
raise RuntimeError("generator didn't stop")
157+
else:
158+
if value is None:
159+
value = typ()
160+
# See _GeneratorContextManager.__exit__ for comments on subtleties
161+
# in this implementation
162+
try:
163+
await self.gen.athrow(typ, value, traceback)
164+
raise RuntimeError("generator didn't stop after throw()")
165+
except StopAsyncIteration as exc:
166+
return exc is not value
167+
except RuntimeError as exc:
168+
if exc is value:
169+
return False
170+
# Avoid suppressing if a StopIteration exception
171+
# was passed to throw() and later wrapped into a RuntimeError
172+
# (see PEP 479 for sync generators; async generators also
173+
# have this behavior). But do this only if the exception wrapped
174+
# by the RuntimeError is actully Stop(Async)Iteration (see
175+
# issue29692).
176+
if isinstance(value, (StopIteration, StopAsyncIteration)):
177+
if exc.__cause__ is value:
178+
return False
179+
raise
180+
except BaseException as exc:
181+
if exc is not value:
182+
raise
183+
184+
130185
def contextmanager(func):
131186
"""@contextmanager decorator.
132187
@@ -153,14 +208,46 @@ def some_generator(<arguments>):
153208
<body>
154209
finally:
155210
<cleanup>
156-
157211
"""
158212
@wraps(func)
159213
def helper(*args, **kwds):
160214
return _GeneratorContextManager(func, args, kwds)
161215
return helper
162216

163217

218+
def asynccontextmanager(func):
219+
"""@asynccontextmanager decorator.
220+
221+
Typical usage:
222+
223+
@asynccontextmanager
224+
async def some_async_generator(<arguments>):
225+
<setup>
226+
try:
227+
yield <value>
228+
finally:
229+
<cleanup>
230+
231+
This makes this:
232+
233+
async with some_async_generator(<arguments>) as <variable>:
234+
<body>
235+
236+
equivalent to this:
237+
238+
<setup>
239+
try:
240+
<variable> = <value>
241+
<body>
242+
finally:
243+
<cleanup>
244+
"""
245+
@wraps(func)
246+
def helper(*args, **kwds):
247+
return _AsyncGeneratorContextManager(func, args, kwds)
248+
return helper
249+
250+
164251
class closing(AbstractContextManager):
165252
"""Context to automatically close something at the end of a block.
166253

Lib/test/support/__init__.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2119,20 +2119,24 @@ def swap_attr(obj, attr, new_val):
21192119
restoring the old value at the end of the block. If `attr` doesn't
21202120
exist on `obj`, it will be created and then deleted at the end of the
21212121
block.
2122+
2123+
The old value (or None if it doesn't exist) will be assigned to the
2124+
target of the "as" clause, if there is one.
21222125
"""
21232126
if hasattr(obj, attr):
21242127
real_val = getattr(obj, attr)
21252128
setattr(obj, attr, new_val)
21262129
try:
2127-
yield
2130+
yield real_val
21282131
finally:
21292132
setattr(obj, attr, real_val)
21302133
else:
21312134
setattr(obj, attr, new_val)
21322135
try:
21332136
yield
21342137
finally:
2135-
delattr(obj, attr)
2138+
if hasattr(obj, attr):
2139+
delattr(obj, attr)
21362140

21372141
@contextlib.contextmanager
21382142
def swap_item(obj, item, new_val):
@@ -2146,20 +2150,24 @@ def swap_item(obj, item, new_val):
21462150
restoring the old value at the end of the block. If `item` doesn't
21472151
exist on `obj`, it will be created and then deleted at the end of the
21482152
block.
2153+
2154+
The old value (or None if it doesn't exist) will be assigned to the
2155+
target of the "as" clause, if there is one.
21492156
"""
21502157
if item in obj:
21512158
real_val = obj[item]
21522159
obj[item] = new_val
21532160
try:
2154-
yield
2161+
yield real_val
21552162
finally:
21562163
obj[item] = real_val
21572164
else:
21582165
obj[item] = new_val
21592166
try:
21602167
yield
21612168
finally:
2162-
del obj[item]
2169+
if item in obj:
2170+
del obj[item]
21632171

21642172
def strip_python_stderr(stderr):
21652173
"""Strip the stderr of a Python process from potential debug output

0 commit comments

Comments
 (0)