Skip to content

Commit df8f089

Browse files
committed
gh-152659: Address review feedback from brettcannon
1 parent 40795f2 commit df8f089

2 files changed

Lines changed: 64 additions & 0 deletions

File tree

‎Lib/test/support/import_helper.py‎

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,39 @@ def _save_and_remove_modules(names):
105105
return orig_modules
106106

107107

108+
_PARENT_ATTR_MISSING = object()
109+
110+
111+
def _save_parent_attrs(names):
112+
parent_attrs = {}
113+
prefixes = tuple(name + '.' for name in names)
114+
removed = {modname for modname in sys.modules
115+
if modname in names or modname.startswith(prefixes)}
116+
for modname in set(names) | removed:
117+
parent_name, _, attr = modname.rpartition('.')
118+
if not parent_name:
119+
continue
120+
if parent_name in removed or parent_name.startswith(prefixes):
121+
continue
122+
if (parent := sys.modules.get(parent_name)) is None:
123+
continue
124+
parent_attrs[parent_name, attr] = getattr(parent, attr, _PARENT_ATTR_MISSING)
125+
return parent_attrs
126+
127+
128+
def _restore_parent_attrs(parent_attrs):
129+
for (parent_name, attr), value in parent_attrs.items():
130+
if (parent := sys.modules.get(parent_name)) is None:
131+
continue
132+
if value is _PARENT_ATTR_MISSING:
133+
try:
134+
delattr(parent, attr)
135+
except AttributeError:
136+
pass
137+
else:
138+
setattr(parent, attr, value)
139+
140+
108141
@contextlib.contextmanager
109142
def frozen_modules(enabled=True):
110143
"""Force frozen modules to be used (or not).
@@ -179,6 +212,7 @@ def import_fresh_module(name, fresh=(), blocked=(), *,
179212
fresh = list(fresh)
180213
blocked = list(blocked)
181214
names = {name, *fresh, *blocked}
215+
orig_parent_attrs = _save_parent_attrs(names)
182216
orig_modules = _save_and_remove_modules(names)
183217
for modname in blocked:
184218
sys.modules[modname] = None
@@ -195,6 +229,7 @@ def import_fresh_module(name, fresh=(), blocked=(), *,
195229
finally:
196230
_save_and_remove_modules(names)
197231
sys.modules.update(orig_modules)
232+
_restore_parent_attrs(orig_parent_attrs)
198233

199234

200235
class CleanImport(object):

‎Lib/test/test_support.py‎

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,35 @@ def test_import_module(self):
9191
def test_import_fresh_module(self):
9292
import_helper.import_fresh_module("ftplib")
9393

94+
def test_import_fresh_module_restores_parent_attr(self):
95+
import importlib.util
96+
97+
name = importlib.util.__name__
98+
original_module = sys.modules[name]
99+
self.assertIs(importlib.util, original_module)
100+
101+
fresh_module = import_helper.import_fresh_module(name)
102+
103+
self.assertIsNot(fresh_module, original_module)
104+
self.assertIs(sys.modules[name], original_module)
105+
self.assertIs(importlib.util, original_module)
106+
107+
def test_import_fresh_module_removes_added_parent_attr(self):
108+
import xml
109+
110+
name = "xml.sax"
111+
self.enterContext(import_helper.CleanImport(name))
112+
if hasattr(xml, "sax"):
113+
self.addCleanup(setattr, xml, "sax", xml.sax)
114+
del xml.sax
115+
self.assertFalse(hasattr(xml, "sax"))
116+
117+
fresh_module = import_helper.import_fresh_module(name)
118+
119+
self.assertIsNotNone(fresh_module)
120+
self.assertNotIn(name, sys.modules)
121+
self.assertFalse(hasattr(xml, "sax"))
122+
94123
def test_get_attribute(self):
95124
self.assertEqual(support.get_attribute(self, "test_get_attribute"),
96125
self.test_get_attribute)

0 commit comments

Comments
 (0)