Skip to content

Commit 4841776

Browse files
authored
Update contextlib from 3.13.5, (#6056)
* Update `contextlib` from 3.13.5 * Add `test_contextlib_async.py`
1 parent 710941c commit 4841776

14 files changed

+895
-25
lines changed

Lib/contextlib.py

Lines changed: 36 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ class AbstractContextManager(abc.ABC):
2020

2121
__class_getitem__ = classmethod(GenericAlias)
2222

23+
__slots__ = ()
24+
2325
def __enter__(self):
2426
"""Return `self` upon entering the runtime context."""
2527
return self
@@ -42,6 +44,8 @@ class AbstractAsyncContextManager(abc.ABC):
4244

4345
__class_getitem__ = classmethod(GenericAlias)
4446

47+
__slots__ = ()
48+
4549
async def __aenter__(self):
4650
"""Return `self` upon entering the runtime context."""
4751
return self
@@ -565,11 +569,12 @@ def __enter__(self):
565569
return self
566570

567571
def __exit__(self, *exc_details):
568-
received_exc = exc_details[0] is not None
572+
exc = exc_details[1]
573+
received_exc = exc is not None
569574

570575
# We manipulate the exception state so it behaves as though
571576
# we were actually nesting multiple with statements
572-
frame_exc = sys.exc_info()[1]
577+
frame_exc = sys.exception()
573578
def _fix_exception_context(new_exc, old_exc):
574579
# Context may not be correct, so find the end of the chain
575580
while 1:
@@ -592,24 +597,28 @@ def _fix_exception_context(new_exc, old_exc):
592597
is_sync, cb = self._exit_callbacks.pop()
593598
assert is_sync
594599
try:
600+
if exc is None:
601+
exc_details = None, None, None
602+
else:
603+
exc_details = type(exc), exc, exc.__traceback__
595604
if cb(*exc_details):
596605
suppressed_exc = True
597606
pending_raise = False
598-
exc_details = (None, None, None)
599-
except:
600-
new_exc_details = sys.exc_info()
607+
exc = None
608+
except BaseException as new_exc:
601609
# simulate the stack of exceptions by setting the context
602-
_fix_exception_context(new_exc_details[1], exc_details[1])
610+
_fix_exception_context(new_exc, exc)
603611
pending_raise = True
604-
exc_details = new_exc_details
612+
exc = new_exc
613+
605614
if pending_raise:
606615
try:
607-
# bare "raise exc_details[1]" replaces our carefully
616+
# bare "raise exc" replaces our carefully
608617
# set-up context
609-
fixed_ctx = exc_details[1].__context__
610-
raise exc_details[1]
618+
fixed_ctx = exc.__context__
619+
raise exc
611620
except BaseException:
612-
exc_details[1].__context__ = fixed_ctx
621+
exc.__context__ = fixed_ctx
613622
raise
614623
return received_exc and suppressed_exc
615624

@@ -705,11 +714,12 @@ async def __aenter__(self):
705714
return self
706715

707716
async def __aexit__(self, *exc_details):
708-
received_exc = exc_details[0] is not None
717+
exc = exc_details[1]
718+
received_exc = exc is not None
709719

710720
# We manipulate the exception state so it behaves as though
711721
# we were actually nesting multiple with statements
712-
frame_exc = sys.exc_info()[1]
722+
frame_exc = sys.exception()
713723
def _fix_exception_context(new_exc, old_exc):
714724
# Context may not be correct, so find the end of the chain
715725
while 1:
@@ -731,6 +741,10 @@ def _fix_exception_context(new_exc, old_exc):
731741
while self._exit_callbacks:
732742
is_sync, cb = self._exit_callbacks.pop()
733743
try:
744+
if exc is None:
745+
exc_details = None, None, None
746+
else:
747+
exc_details = type(exc), exc, exc.__traceback__
734748
if is_sync:
735749
cb_suppress = cb(*exc_details)
736750
else:
@@ -739,21 +753,21 @@ def _fix_exception_context(new_exc, old_exc):
739753
if cb_suppress:
740754
suppressed_exc = True
741755
pending_raise = False
742-
exc_details = (None, None, None)
743-
except:
744-
new_exc_details = sys.exc_info()
756+
exc = None
757+
except BaseException as new_exc:
745758
# simulate the stack of exceptions by setting the context
746-
_fix_exception_context(new_exc_details[1], exc_details[1])
759+
_fix_exception_context(new_exc, exc)
747760
pending_raise = True
748-
exc_details = new_exc_details
761+
exc = new_exc
762+
749763
if pending_raise:
750764
try:
751-
# bare "raise exc_details[1]" replaces our carefully
765+
# bare "raise exc" replaces our carefully
752766
# set-up context
753-
fixed_ctx = exc_details[1].__context__
754-
raise exc_details[1]
767+
fixed_ctx = exc.__context__
768+
raise exc
755769
except BaseException:
756-
exc_details[1].__context__ = fixed_ctx
770+
exc.__context__ = fixed_ctx
757771
raise
758772
return received_exc and suppressed_exc
759773

Lib/test/archivetestdata/README.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Test data for `test_zipfile`, `test_tarfile` (and even some others)
2+
3+
## `test_zipfile`
4+
5+
The test executables in this directory are created manually from `header.sh` and
6+
the `testdata_module_inside_zip.py` file. You must have Info-ZIP's zip utility
7+
installed (`apt install zip` on Debian).
8+
9+
### Purpose of `exe_with_zip` and `exe_with_z64`
10+
11+
These are used to test executable files with an appended zipfile, in a scenario
12+
where the executable is _not_ a Python interpreter itself so our automatic
13+
zipimport machinery (that'd look for `__main__.py`) is not being used.
14+
15+
### Updating the test executables
16+
17+
If you update header.sh or the testdata_module_inside_zip.py file, rerun the
18+
commands below. These are expected to be rarely changed, if ever.
19+
20+
#### Standard old format (2.0) zip file
21+
22+
```
23+
zip -0 zip2.zip testdata_module_inside_zip.py
24+
cat header.sh zip2.zip >exe_with_zip
25+
rm zip2.zip
26+
```
27+
28+
#### Modern format (4.5) zip64 file
29+
30+
Redirecting from stdin forces Info-ZIP's zip tool to create a zip64.
31+
32+
```
33+
zip -0 <testdata_module_inside_zip.py >zip64.zip
34+
cat header.sh zip64.zip >exe_with_z64
35+
rm zip64.zip
36+
```

Lib/test/archivetestdata/exe_with_z64

978 Bytes
Binary file not shown.

Lib/test/archivetestdata/exe_with_zip

990 Bytes
Binary file not shown.

Lib/test/archivetestdata/header.sh

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#!/bin/bash
2+
INTERPRETER_UNDER_TEST="$1"
3+
if [[ ! -x "${INTERPRETER_UNDER_TEST}" ]]; then
4+
echo "Interpreter must be the command line argument."
5+
exit 4
6+
fi
7+
EXECUTABLE="$0" exec "${INTERPRETER_UNDER_TEST}" -E - <<END_OF_PYTHON
8+
import os
9+
import zipfile
10+
11+
namespace = {}
12+
13+
filename = os.environ['EXECUTABLE']
14+
print(f'Opening {filename} as a zipfile.')
15+
with zipfile.ZipFile(filename, mode='r') as exe_zip:
16+
for file_info in exe_zip.infolist():
17+
data = exe_zip.read(file_info)
18+
exec(data, namespace, namespace)
19+
break # Only use the first file in the archive.
20+
21+
print('Favorite number in executable:', namespace["FAVORITE_NUMBER"])
22+
23+
### Archive contents will be appended after this file. ###
24+
END_OF_PYTHON
516 Bytes
Binary file not shown.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Test data file to be stored within a zip file.
2+
FAVORITE_NUMBER = 5

Lib/test/archivetestdata/testtar.tar

425 KB
Binary file not shown.
172 Bytes
Binary file not shown.
270 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)