Skip to content

Commit b6dbac2

Browse files
DinoVshihai1991
authored andcommitted
bpo-39336: Allow packages to not let their child modules be set on them (python#18006)
* bpo-39336: Allow setattr to fail on modules which aren't assignable When attaching a child module to a package if the object in sys.modules raises an AttributeError (e.g. because it is immutable) it causes the whole import to fail. This now allows immutable packages to exist and an ImportWarning is reported and the AttributeError exception is ignored.
1 parent c261c86 commit b6dbac2

File tree

6 files changed

+379
-338
lines changed

6 files changed

+379
-338
lines changed

Lib/importlib/_bootstrap.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -978,7 +978,12 @@ def _find_and_load_unlocked(name, import_):
978978
if parent:
979979
# Set the module as an attribute on its parent.
980980
parent_module = sys.modules[parent]
981-
setattr(parent_module, name.rpartition('.')[2], module)
981+
child = name.rpartition('.')[2]
982+
try:
983+
setattr(parent_module, child, module)
984+
except AttributeError:
985+
msg = f"Cannot set an attribute on {parent!r} for child module {child!r}"
986+
_warnings.warn(msg, ImportWarning)
982987
return module
983988

984989

Lib/test/test_import/__init__.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
temp_dir, DirsOnSysPath)
2727
from test.support import script_helper
2828
from test.test_importlib.util import uncache
29+
from types import ModuleType
2930

3031

3132
skip_if_dont_write_bytecode = unittest.skipIf(
@@ -1339,6 +1340,19 @@ def test_circular_from_import(self):
13391340
str(cm.exception),
13401341
)
13411342

1343+
def test_unwritable_module(self):
1344+
self.addCleanup(unload, "test.test_import.data.unwritable")
1345+
self.addCleanup(unload, "test.test_import.data.unwritable.x")
1346+
1347+
import test.test_import.data.unwritable as unwritable
1348+
with self.assertWarns(ImportWarning):
1349+
from test.test_import.data.unwritable import x
1350+
1351+
self.assertNotEqual(type(unwritable), ModuleType)
1352+
self.assertEqual(type(x), ModuleType)
1353+
with self.assertRaises(AttributeError):
1354+
unwritable.x = 42
1355+
13421356

13431357
if __name__ == '__main__':
13441358
# Test needs to be a package, so we can do relative imports.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import sys
2+
3+
class MyMod(object):
4+
__slots__ = ['__builtins__', '__cached__', '__doc__',
5+
'__file__', '__loader__', '__name__',
6+
'__package__', '__path__', '__spec__']
7+
def __init__(self):
8+
for attr in self.__slots__:
9+
setattr(self, attr, globals()[attr])
10+
11+
12+
sys.modules[__name__] = MyMod()

Lib/test/test_import/data/unwritable/x.py

Whitespace-only changes.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Import loaders which publish immutable module objects can now publish immutable packages in addition to individual modules.

0 commit comments

Comments
 (0)