3
import importlib.machinery
9
from test.test_importlib import util
10
from test.support import run_unittest
14
# need to test when nested, so that the top-level path isn't sys.path
15
# need to test dynamic path detection, both at top-level and nested
16
# with dynamic path, check when a loader is returned on path reload (that is,
17
# trying to switch from a namespace package to a regular package)
20
@contextlib.contextmanager
21
def sys_modules_context():
23
Make sure sys.modules is the same object and has the same content
24
when exiting the context as when entering.
26
Similar to importlib.test.util.uncache, but doesn't require explicit
29
sys_modules_saved = sys.modules
30
sys_modules_copy = sys.modules.copy()
34
sys.modules = sys_modules_saved
36
sys.modules.update(sys_modules_copy)
39
@contextlib.contextmanager
40
def namespace_tree_context(**kwargs):
42
Save import state and sys.modules cache and restore it on exit.
45
>>> with namespace_tree_context(path=['/tmp/xxyy/portion1',
46
... '/tmp/xxyy/portion2']):
49
# use default meta_path and path_hooks unless specified otherwise
50
kwargs.setdefault('meta_path', sys.meta_path)
51
kwargs.setdefault('path_hooks', sys.path_hooks)
52
import_context = util.import_state(**kwargs)
53
with import_context, sys_modules_context():
56
class NamespacePackageTest(unittest.TestCase):
58
Subclasses should define self.root and self.paths (under that root)
59
to be added to sys.path.
61
root = os.path.join(os.path.dirname(__file__), 'namespace_pkgs')
64
self.resolved_paths = [
65
os.path.join(self.root, path) for path in self.paths
67
self.ctx = namespace_tree_context(path=self.resolved_paths)
71
# TODO: will we ever want to pass exc_info to __exit__?
72
self.ctx.__exit__(None, None, None)
74
class SingleNamespacePackage(NamespacePackageTest):
77
def test_simple_package(self):
79
self.assertEqual(foo.one.attr, 'portion1 foo one')
81
def test_cant_import_other(self):
82
with self.assertRaises(ImportError):
85
def test_module_repr(self):
87
self.assertEqual(repr(foo), "<module 'foo' (namespace)>")
90
class DynamicPatheNamespacePackage(NamespacePackageTest):
93
def test_dynamic_path(self):
94
# Make sure only 'foo.one' can be imported
96
self.assertEqual(foo.one.attr, 'portion1 foo one')
98
with self.assertRaises(ImportError):
101
# Now modify sys.path
102
sys.path.append(os.path.join(self.root, 'portion2'))
104
# And make sure foo.two is now importable
106
self.assertEqual(foo.two.attr, 'portion2 foo two')
109
class CombinedNamespacePackages(NamespacePackageTest):
110
paths = ['both_portions']
112
def test_imports(self):
115
self.assertEqual(foo.one.attr, 'both_portions foo one')
116
self.assertEqual(foo.two.attr, 'both_portions foo two')
119
class SeparatedNamespacePackages(NamespacePackageTest):
120
paths = ['portion1', 'portion2']
122
def test_imports(self):
125
self.assertEqual(foo.one.attr, 'portion1 foo one')
126
self.assertEqual(foo.two.attr, 'portion2 foo two')
129
class SeparatedOverlappingNamespacePackages(NamespacePackageTest):
130
paths = ['portion1', 'both_portions']
132
def test_first_path_wins(self):
135
self.assertEqual(foo.one.attr, 'portion1 foo one')
136
self.assertEqual(foo.two.attr, 'both_portions foo two')
138
def test_first_path_wins_again(self):
142
self.assertEqual(foo.one.attr, 'both_portions foo one')
143
self.assertEqual(foo.two.attr, 'both_portions foo two')
145
def test_first_path_wins_importing_second_first(self):
148
self.assertEqual(foo.one.attr, 'portion1 foo one')
149
self.assertEqual(foo.two.attr, 'both_portions foo two')
152
class SingleZipNamespacePackage(NamespacePackageTest):
153
paths = ['top_level_portion1.zip']
155
def test_simple_package(self):
157
self.assertEqual(foo.one.attr, 'portion1 foo one')
159
def test_cant_import_other(self):
160
with self.assertRaises(ImportError):
164
class SeparatedZipNamespacePackages(NamespacePackageTest):
165
paths = ['top_level_portion1.zip', 'portion2']
167
def test_imports(self):
170
self.assertEqual(foo.one.attr, 'portion1 foo one')
171
self.assertEqual(foo.two.attr, 'portion2 foo two')
172
self.assertIn('top_level_portion1.zip', foo.one.__file__)
173
self.assertNotIn('.zip', foo.two.__file__)
176
class SingleNestedZipNamespacePackage(NamespacePackageTest):
177
paths = ['nested_portion1.zip/nested_portion1']
179
def test_simple_package(self):
181
self.assertEqual(foo.one.attr, 'portion1 foo one')
183
def test_cant_import_other(self):
184
with self.assertRaises(ImportError):
188
class SeparatedNestedZipNamespacePackages(NamespacePackageTest):
189
paths = ['nested_portion1.zip/nested_portion1', 'portion2']
191
def test_imports(self):
194
self.assertEqual(foo.one.attr, 'portion1 foo one')
195
self.assertEqual(foo.two.attr, 'portion2 foo two')
196
fn = os.path.join('nested_portion1.zip', 'nested_portion1')
197
self.assertIn(fn, foo.one.__file__)
198
self.assertNotIn('.zip', foo.two.__file__)
201
class LegacySupport(NamespacePackageTest):
202
paths = ['not_a_namespace_pkg', 'portion1', 'portion2', 'both_portions']
204
def test_non_namespace_package_takes_precedence(self):
206
with self.assertRaises(ImportError):
208
self.assertIn('__init__', foo.__file__)
209
self.assertNotIn('namespace', str(foo.__loader__).lower())
212
class DynamicPathCalculation(NamespacePackageTest):
213
paths = ['project1', 'project2']
215
def test_project3_fails(self):
216
import parent.child.one
217
self.assertEqual(len(parent.__path__), 2)
218
self.assertEqual(len(parent.child.__path__), 2)
219
import parent.child.two
220
self.assertEqual(len(parent.__path__), 2)
221
self.assertEqual(len(parent.child.__path__), 2)
223
self.assertEqual(parent.child.one.attr, 'parent child one')
224
self.assertEqual(parent.child.two.attr, 'parent child two')
226
with self.assertRaises(ImportError):
227
import parent.child.three
229
self.assertEqual(len(parent.__path__), 2)
230
self.assertEqual(len(parent.child.__path__), 2)
232
def test_project3_succeeds(self):
233
import parent.child.one
234
self.assertEqual(len(parent.__path__), 2)
235
self.assertEqual(len(parent.child.__path__), 2)
236
import parent.child.two
237
self.assertEqual(len(parent.__path__), 2)
238
self.assertEqual(len(parent.child.__path__), 2)
240
self.assertEqual(parent.child.one.attr, 'parent child one')
241
self.assertEqual(parent.child.two.attr, 'parent child two')
243
with self.assertRaises(ImportError):
244
import parent.child.three
247
sys.path.append(os.path.join(self.root, 'project3'))
248
import parent.child.three
250
# the paths dynamically get longer, to include the new directories
251
self.assertEqual(len(parent.__path__), 3)
252
self.assertEqual(len(parent.child.__path__), 3)
254
self.assertEqual(parent.child.three.attr, 'parent child three')
257
class ZipWithMissingDirectory(NamespacePackageTest):
258
paths = ['missing_directory.zip']
260
@unittest.expectedFailure
261
def test_missing_directory(self):
262
# This will fail because missing_directory.zip contains:
263
# Length Date Time Name
264
# --------- ---------- ----- ----
265
# 29 2012-05-03 18:13 foo/one.py
266
# 0 2012-05-03 20:57 bar/
267
# 38 2012-05-03 20:57 bar/two.py
271
# Because there is no 'foo/', the zipimporter currently doesn't
272
# know that foo is a namespace package
276
def test_present_directory(self):
277
# This succeeds because there is a "bar/" in the zip file
279
self.assertEqual(bar.two.attr, 'missing_directory foo two')
282
class ModuleAndNamespacePackageInSameDir(NamespacePackageTest):
283
paths = ['module_and_namespace_package']
285
def test_module_before_namespace_package(self):
286
# Make sure we find the module in preference to the
289
self.assertEqual(a_test.attr, 'in module')
292
if __name__ == "__main__":