@@ -1126,6 +1126,139 @@ def test_safe_exists(tmp_path: Path) -> None:
1126
1126
assert safe_exists (p ) is False
1127
1127
1128
1128
1129
+ def test_import_sets_module_as_attribute (pytester : Pytester ) -> None :
1130
+ """Unittest test for #12194."""
1131
+ pytester .path .joinpath ("foo/bar/baz" ).mkdir (parents = True )
1132
+ pytester .path .joinpath ("foo/__init__.py" ).touch ()
1133
+ pytester .path .joinpath ("foo/bar/__init__.py" ).touch ()
1134
+ pytester .path .joinpath ("foo/bar/baz/__init__.py" ).touch ()
1135
+ pytester .syspathinsert ()
1136
+
1137
+ # Import foo.bar.baz and ensure parent modules also ended up imported.
1138
+ baz = import_path (
1139
+ pytester .path .joinpath ("foo/bar/baz/__init__.py" ),
1140
+ mode = ImportMode .importlib ,
1141
+ root = pytester .path ,
1142
+ consider_namespace_packages = False ,
1143
+ )
1144
+ assert baz .__name__ == "foo.bar.baz"
1145
+ foo = sys .modules ["foo" ]
1146
+ assert foo .__name__ == "foo"
1147
+ bar = sys .modules ["foo.bar" ]
1148
+ assert bar .__name__ == "foo.bar"
1149
+
1150
+ # Check parent modules have an attribute pointing to their children.
1151
+ assert bar .baz is baz
1152
+ assert foo .bar is bar
1153
+
1154
+ # Ensure we returned the "foo.bar" module cached in sys.modules.
1155
+ bar_2 = import_path (
1156
+ pytester .path .joinpath ("foo/bar/__init__.py" ),
1157
+ mode = ImportMode .importlib ,
1158
+ root = pytester .path ,
1159
+ consider_namespace_packages = False ,
1160
+ )
1161
+ assert bar_2 is bar
1162
+
1163
+
1164
+ def test_import_sets_module_as_attribute_without_init_files (pytester : Pytester ) -> None :
1165
+ """Similar to test_import_sets_module_as_attribute, but without __init__.py files."""
1166
+ pytester .path .joinpath ("foo/bar" ).mkdir (parents = True )
1167
+ pytester .path .joinpath ("foo/bar/baz.py" ).touch ()
1168
+ pytester .syspathinsert ()
1169
+
1170
+ # Import foo.bar.baz and ensure parent modules also ended up imported.
1171
+ baz = import_path (
1172
+ pytester .path .joinpath ("foo/bar/baz.py" ),
1173
+ mode = ImportMode .importlib ,
1174
+ root = pytester .path ,
1175
+ consider_namespace_packages = False ,
1176
+ )
1177
+ assert baz .__name__ == "foo.bar.baz"
1178
+ foo = sys .modules ["foo" ]
1179
+ assert foo .__name__ == "foo"
1180
+ bar = sys .modules ["foo.bar" ]
1181
+ assert bar .__name__ == "foo.bar"
1182
+
1183
+ # Check parent modules have an attribute pointing to their children.
1184
+ assert bar .baz is baz
1185
+ assert foo .bar is bar
1186
+
1187
+ # Ensure we returned the "foo.bar.baz" module cached in sys.modules.
1188
+ baz_2 = import_path (
1189
+ pytester .path .joinpath ("foo/bar/baz.py" ),
1190
+ mode = ImportMode .importlib ,
1191
+ root = pytester .path ,
1192
+ consider_namespace_packages = False ,
1193
+ )
1194
+ assert baz_2 is baz
1195
+
1196
+
1197
+ def test_import_sets_module_as_attribute_regression (pytester : Pytester ) -> None :
1198
+ """Regression test for #12194."""
1199
+ pytester .path .joinpath ("foo/bar/baz" ).mkdir (parents = True )
1200
+ pytester .path .joinpath ("foo/__init__.py" ).touch ()
1201
+ pytester .path .joinpath ("foo/bar/__init__.py" ).touch ()
1202
+ pytester .path .joinpath ("foo/bar/baz/__init__.py" ).touch ()
1203
+ f = pytester .makepyfile (
1204
+ """
1205
+ import foo
1206
+ from foo.bar import baz
1207
+ foo.bar.baz
1208
+
1209
+ def test_foo() -> None:
1210
+ pass
1211
+ """
1212
+ )
1213
+
1214
+ pytester .syspathinsert ()
1215
+ result = pytester .runpython (f )
1216
+ assert result .ret == 0
1217
+
1218
+ result = pytester .runpytest ("--import-mode=importlib" , "--doctest-modules" )
1219
+ assert result .ret == 0
1220
+
1221
+
1222
+ def test_import_submodule_not_namespace (pytester : Pytester ) -> None :
1223
+ """
1224
+ Regression test for importing a submodule 'foo.bar' while there is a 'bar' directory
1225
+ reachable from sys.path -- ensuring the top-level module does not end up imported as a namespace
1226
+ package.
1227
+
1228
+ #12194
1229
+ https://github.com/pytest-dev/pytest/pull/12208#issuecomment-2056458432
1230
+ """
1231
+ pytester .syspathinsert ()
1232
+ # Create package 'foo' with a submodule 'bar'.
1233
+ pytester .path .joinpath ("foo" ).mkdir ()
1234
+ foo_path = pytester .path .joinpath ("foo/__init__.py" )
1235
+ foo_path .touch ()
1236
+ bar_path = pytester .path .joinpath ("foo/bar.py" )
1237
+ bar_path .touch ()
1238
+ # Create top-level directory in `sys.path` with the same name as that submodule.
1239
+ pytester .path .joinpath ("bar" ).mkdir ()
1240
+
1241
+ # Import `foo`, then `foo.bar`, and check they were imported from the correct location.
1242
+ foo = import_path (
1243
+ foo_path ,
1244
+ mode = ImportMode .importlib ,
1245
+ root = pytester .path ,
1246
+ consider_namespace_packages = False ,
1247
+ )
1248
+ bar = import_path (
1249
+ bar_path ,
1250
+ mode = ImportMode .importlib ,
1251
+ root = pytester .path ,
1252
+ consider_namespace_packages = False ,
1253
+ )
1254
+ assert foo .__name__ == "foo"
1255
+ assert bar .__name__ == "foo.bar"
1256
+ assert foo .__file__ is not None
1257
+ assert bar .__file__ is not None
1258
+ assert Path (foo .__file__ ) == foo_path
1259
+ assert Path (bar .__file__ ) == bar_path
1260
+
1261
+
1129
1262
class TestNamespacePackages :
1130
1263
"""Test import_path support when importing from properly namespace packages."""
1131
1264
0 commit comments