Skip to content

Commit f8bb5b9

Browse files
Merge branch 'main' into fix-150075-tar-addfile-no-offsets
2 parents 2705a52 + c35b0f2 commit f8bb5b9

6 files changed

Lines changed: 133 additions & 0 deletions

File tree

Lib/test/test_crossinterp.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,10 @@ def ignore_byteswarning():
157157
{},
158158
{1: 7, 2: 8, 3: 9},
159159
{1: [1], 2: (2,), 3: {3: 4}},
160+
# frozendict
161+
frozendict(),
162+
frozendict({1: 7, 2: 8, 3: 9}),
163+
frozendict({1: [1], 2: (2,), 3: {3: 4}, 4: frozendict({5: 6})}),
160164
# set
161165
set(),
162166
{1, 2, 3},

Lib/test/test_lazy_import/__init__.py

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,59 @@ def test_from_import_with_module_getattr(self):
9898
""")
9999
assert_python_ok("-c", code)
100100

101+
@support.requires_subprocess()
102+
def test_from_import_with_module_getattr_raising(self):
103+
"""Lazy from import should respect module-level __getattr__."""
104+
code = textwrap.dedent("""
105+
lazy from test.test_lazy_import.data.module_with_getattr import raising_attr
106+
107+
try:
108+
raising_attr
109+
except ValueError as exc:
110+
assert str(exc) == 'from_getattr', exc
111+
else:
112+
assert False, f'ValueError is not raised: {raising_attr}'
113+
""")
114+
assert_python_ok("-c", code)
115+
116+
@support.requires_subprocess()
117+
def test_from_import_with_module_getattr_missing(self):
118+
"""Lazy from import should respect module-level __getattr__."""
119+
for attr in ("missing_attr", "import_error_attr"):
120+
with self.subTest(attr=attr):
121+
code = textwrap.dedent(f"""
122+
lazy from test.test_lazy_import.data.module_with_getattr import {attr}
123+
124+
try:
125+
{attr}
126+
except ImportError as exc:
127+
assert '{attr}' in str(exc), exc
128+
assert exc.__cause__ is not None
129+
else:
130+
assert False, ('ImportError is not raised', {attr})
131+
""")
132+
assert_python_ok("-c", code)
133+
134+
@support.requires_subprocess()
135+
def test_from_import_with_module_getattr_warning(self):
136+
"""Lazy from import should respect module-level __getattr__."""
137+
code = textwrap.dedent("""
138+
import warnings
139+
140+
with warnings.catch_warnings(record=True) as log:
141+
lazy from test.test_lazy_import.data.module_with_getattr import warning_attr
142+
143+
assert log == []
144+
145+
with warnings.catch_warnings(record=True) as log:
146+
warning_attr
147+
assert warning_attr == 'from_warning_attr', warning_attr
148+
assert len(log) == 1, log
149+
assert isinstance(log[0].message, UserWarning), log
150+
assert str(log[0].message) == 'from_getattr', log
151+
""")
152+
assert_python_ok("-c", code)
153+
101154
@support.requires_subprocess()
102155
def test_from_import_with_imported_module_getattr(self):
103156
"""Lazy from import should not shadow an imported module's __getattr__."""
@@ -463,6 +516,59 @@ def test_lazy_import_pkg_cross_import(self):
463516
self.assertEqual(type(g["x"]), int)
464517
self.assertEqual(type(g["b"]), types.LazyImportType)
465518

519+
@support.requires_subprocess()
520+
def test_package_from_import_with_module_getattr_raising(self):
521+
"""Lazy from import should respect a package's __getattr__."""
522+
code = textwrap.dedent("""
523+
lazy from test.test_lazy_import.data.pkg import raising_attr
524+
525+
try:
526+
raising_attr
527+
except ValueError as exc:
528+
assert str(exc) == 'from_getattr', exc
529+
else:
530+
assert False, f'ValueError is not raised: {raising_attr}'
531+
""")
532+
assert_python_ok("-c", code)
533+
534+
@support.requires_subprocess()
535+
def test_package_from_import_with_module_getattr_missing(self):
536+
"""Lazy from import should respect package's __getattr__."""
537+
for attr in ("missing_attr", "import_error_attr"):
538+
with self.subTest(attr=attr):
539+
code = textwrap.dedent(f"""
540+
lazy from test.test_lazy_import.data.pkg import {attr}
541+
542+
try:
543+
{attr}
544+
except ImportError as exc:
545+
assert '{attr}' in str(exc), exc
546+
assert exc.__cause__ is not None
547+
else:
548+
assert False, ('ImportError is not raised', {attr})
549+
""")
550+
assert_python_ok("-c", code)
551+
552+
@support.requires_subprocess()
553+
def test_from_import_with_module_getattr_warning(self):
554+
"""Lazy from import should respect package's __getattr__."""
555+
code = textwrap.dedent("""
556+
import warnings
557+
558+
with warnings.catch_warnings(record=True) as log:
559+
lazy from test.test_lazy_import.data.pkg import warning_attr
560+
561+
assert log == []
562+
563+
with warnings.catch_warnings(record=True) as log:
564+
warning_attr
565+
assert warning_attr == 'from_warning_attr', warning_attr
566+
assert len(log) == 1, log
567+
assert isinstance(log[0].message, UserWarning), log
568+
assert str(log[0].message) == 'from_getattr', log
569+
""")
570+
assert_python_ok("-c", code)
571+
466572
@support.requires_subprocess()
467573
def test_package_from_import_with_module_getattr(self):
468574
"""Lazy from import should respect a package's __getattr__."""
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
11
def __getattr__(name):
22
if name == "dynamic_attr":
33
return "from_getattr"
4+
elif name == "raising_attr":
5+
raise ValueError("from_getattr")
6+
elif name == "import_error_attr":
7+
raise ImportError(name)
8+
elif name == "warning_attr":
9+
import warnings
10+
warnings.warn("from_getattr", category=UserWarning)
11+
return "from_warning_attr"
412
raise AttributeError(name)

Lib/test/test_lazy_import/data/pkg/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,12 @@
33
def __getattr__(name):
44
if name == "dynamic_attr":
55
return "from_getattr"
6+
elif name == "raising_attr":
7+
raise ValueError("from_getattr")
8+
elif name == "import_error_attr":
9+
raise ImportError(name)
10+
elif name == "warning_attr":
11+
import warnings
12+
warnings.warn("from_getattr", category=UserWarning)
13+
return "from_warning_attr"
614
raise AttributeError(name)

Lib/unittest/mock.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3113,6 +3113,10 @@ def _mock_call(self, *args, **kwargs):
31133113

31143114
return ret_value
31153115

3116+
def _increment_mock_call(self, /, *args, **kwargs):
3117+
with self._mock_calls_events_lock:
3118+
super()._increment_mock_call(*args, **kwargs)
3119+
31163120
def wait_until_called(self, *, timeout=_timeout_unset):
31173121
"""Wait until the mock object is called.
31183122
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix race condition in :class:`unittest.mock.ThreadingMock` where
2+
concurrent calls could lose increments to ``call_count`` and other
3+
attributes due to a missing lock in ``_increment_mock_call``.

0 commit comments

Comments
 (0)