1 from test.test_importlib import util
2
3 abc = util.import_importlib('importlib.abc')
4 init = util.import_importlib('importlib')
5 machinery = util.import_importlib('importlib.machinery')
6 importlib_util = util.import_importlib('importlib.util')
7
8 import importlib.util
9 import os
10 import pathlib
11 import string
12 import sys
13 from test import support
14 import types
15 import unittest
16 import unittest.mock
17 import warnings
18
19
20 class ESC[4;38;5;81mDecodeSourceBytesTests:
21
22 source = "string ='ΓΌ'"
23
24 def test_ut8_default(self):
25 source_bytes = self.source.encode('utf-8')
26 self.assertEqual(self.util.decode_source(source_bytes), self.source)
27
28 def test_specified_encoding(self):
29 source = '# coding=latin-1\n' + self.source
30 source_bytes = source.encode('latin-1')
31 assert source_bytes != source.encode('utf-8')
32 self.assertEqual(self.util.decode_source(source_bytes), source)
33
34 def test_universal_newlines(self):
35 source = '\r\n'.join([self.source, self.source])
36 source_bytes = source.encode('utf-8')
37 self.assertEqual(self.util.decode_source(source_bytes),
38 '\n'.join([self.source, self.source]))
39
40
41 (Frozen_DecodeSourceBytesTests,
42 Source_DecodeSourceBytesTests
43 ) = util.test_both(DecodeSourceBytesTests, util=importlib_util)
44
45
46 class ESC[4;38;5;81mModuleFromSpecTests:
47
48 def test_no_create_module(self):
49 class ESC[4;38;5;81mLoader:
50 def exec_module(self, module):
51 pass
52 spec = self.machinery.ModuleSpec('test', Loader())
53 with self.assertRaises(ImportError):
54 module = self.util.module_from_spec(spec)
55
56 def test_create_module_returns_None(self):
57 class ESC[4;38;5;81mLoader(ESC[4;38;5;149mselfESC[4;38;5;149m.ESC[4;38;5;149mabcESC[4;38;5;149m.ESC[4;38;5;149mLoader):
58 def create_module(self, spec):
59 return None
60 spec = self.machinery.ModuleSpec('test', Loader())
61 module = self.util.module_from_spec(spec)
62 self.assertIsInstance(module, types.ModuleType)
63 self.assertEqual(module.__name__, spec.name)
64
65 def test_create_module(self):
66 name = 'already set'
67 class ESC[4;38;5;81mCustomModule(ESC[4;38;5;149mtypesESC[4;38;5;149m.ESC[4;38;5;149mModuleType):
68 pass
69 class ESC[4;38;5;81mLoader(ESC[4;38;5;149mselfESC[4;38;5;149m.ESC[4;38;5;149mabcESC[4;38;5;149m.ESC[4;38;5;149mLoader):
70 def create_module(self, spec):
71 module = CustomModule(spec.name)
72 module.__name__ = name
73 return module
74 spec = self.machinery.ModuleSpec('test', Loader())
75 module = self.util.module_from_spec(spec)
76 self.assertIsInstance(module, CustomModule)
77 self.assertEqual(module.__name__, name)
78
79 def test___name__(self):
80 spec = self.machinery.ModuleSpec('test', object())
81 module = self.util.module_from_spec(spec)
82 self.assertEqual(module.__name__, spec.name)
83
84 def test___spec__(self):
85 spec = self.machinery.ModuleSpec('test', object())
86 module = self.util.module_from_spec(spec)
87 self.assertEqual(module.__spec__, spec)
88
89 def test___loader__(self):
90 loader = object()
91 spec = self.machinery.ModuleSpec('test', loader)
92 module = self.util.module_from_spec(spec)
93 self.assertIs(module.__loader__, loader)
94
95 def test___package__(self):
96 spec = self.machinery.ModuleSpec('test.pkg', object())
97 module = self.util.module_from_spec(spec)
98 self.assertEqual(module.__package__, spec.parent)
99
100 def test___path__(self):
101 spec = self.machinery.ModuleSpec('test', object(), is_package=True)
102 module = self.util.module_from_spec(spec)
103 self.assertEqual(module.__path__, spec.submodule_search_locations)
104
105 def test___file__(self):
106 spec = self.machinery.ModuleSpec('test', object(), origin='some/path')
107 spec.has_location = True
108 module = self.util.module_from_spec(spec)
109 self.assertEqual(module.__file__, spec.origin)
110
111 def test___cached__(self):
112 spec = self.machinery.ModuleSpec('test', object())
113 spec.cached = 'some/path'
114 spec.has_location = True
115 module = self.util.module_from_spec(spec)
116 self.assertEqual(module.__cached__, spec.cached)
117
118 (Frozen_ModuleFromSpecTests,
119 Source_ModuleFromSpecTests
120 ) = util.test_both(ModuleFromSpecTests, abc=abc, machinery=machinery,
121 util=importlib_util)
122
123
124 class ESC[4;38;5;81mModuleForLoaderTests:
125
126 """Tests for importlib.util.module_for_loader."""
127
128 @classmethod
129 def module_for_loader(cls, func):
130 with warnings.catch_warnings():
131 warnings.simplefilter('ignore', DeprecationWarning)
132 return cls.util.module_for_loader(func)
133
134 def test_warning(self):
135 # Should raise a PendingDeprecationWarning when used.
136 with warnings.catch_warnings():
137 warnings.simplefilter('error', DeprecationWarning)
138 with self.assertRaises(DeprecationWarning):
139 func = self.util.module_for_loader(lambda x: x)
140
141 def return_module(self, name):
142 fxn = self.module_for_loader(lambda self, module: module)
143 return fxn(self, name)
144
145 def raise_exception(self, name):
146 def to_wrap(self, module):
147 raise ImportError
148 fxn = self.module_for_loader(to_wrap)
149 try:
150 fxn(self, name)
151 except ImportError:
152 pass
153
154 def test_new_module(self):
155 # Test that when no module exists in sys.modules a new module is
156 # created.
157 module_name = 'a.b.c'
158 with util.uncache(module_name):
159 module = self.return_module(module_name)
160 self.assertIn(module_name, sys.modules)
161 self.assertIsInstance(module, types.ModuleType)
162 self.assertEqual(module.__name__, module_name)
163
164 def test_reload(self):
165 # Test that a module is reused if already in sys.modules.
166 class ESC[4;38;5;81mFakeLoader:
167 def is_package(self, name):
168 return True
169 @self.module_for_loader
170 def load_module(self, module):
171 return module
172 name = 'a.b.c'
173 module = types.ModuleType('a.b.c')
174 module.__loader__ = 42
175 module.__package__ = 42
176 with util.uncache(name):
177 sys.modules[name] = module
178 loader = FakeLoader()
179 returned_module = loader.load_module(name)
180 self.assertIs(returned_module, sys.modules[name])
181 self.assertEqual(module.__loader__, loader)
182 self.assertEqual(module.__package__, name)
183
184 def test_new_module_failure(self):
185 # Test that a module is removed from sys.modules if added but an
186 # exception is raised.
187 name = 'a.b.c'
188 with util.uncache(name):
189 self.raise_exception(name)
190 self.assertNotIn(name, sys.modules)
191
192 def test_reload_failure(self):
193 # Test that a failure on reload leaves the module in-place.
194 name = 'a.b.c'
195 module = types.ModuleType(name)
196 with util.uncache(name):
197 sys.modules[name] = module
198 self.raise_exception(name)
199 self.assertIs(module, sys.modules[name])
200
201 def test_decorator_attrs(self):
202 def fxn(self, module): pass
203 wrapped = self.module_for_loader(fxn)
204 self.assertEqual(wrapped.__name__, fxn.__name__)
205 self.assertEqual(wrapped.__qualname__, fxn.__qualname__)
206
207 def test_false_module(self):
208 # If for some odd reason a module is considered false, still return it
209 # from sys.modules.
210 class ESC[4;38;5;81mFalseModule(ESC[4;38;5;149mtypesESC[4;38;5;149m.ESC[4;38;5;149mModuleType):
211 def __bool__(self): return False
212
213 name = 'mod'
214 module = FalseModule(name)
215 with util.uncache(name):
216 self.assertFalse(module)
217 sys.modules[name] = module
218 given = self.return_module(name)
219 self.assertIs(given, module)
220
221 def test_attributes_set(self):
222 # __name__, __loader__, and __package__ should be set (when
223 # is_package() is defined; undefined implicitly tested elsewhere).
224 class ESC[4;38;5;81mFakeLoader:
225 def __init__(self, is_package):
226 self._pkg = is_package
227 def is_package(self, name):
228 return self._pkg
229 @self.module_for_loader
230 def load_module(self, module):
231 return module
232
233 name = 'pkg.mod'
234 with util.uncache(name):
235 loader = FakeLoader(False)
236 module = loader.load_module(name)
237 self.assertEqual(module.__name__, name)
238 self.assertIs(module.__loader__, loader)
239 self.assertEqual(module.__package__, 'pkg')
240
241 name = 'pkg.sub'
242 with util.uncache(name):
243 loader = FakeLoader(True)
244 module = loader.load_module(name)
245 self.assertEqual(module.__name__, name)
246 self.assertIs(module.__loader__, loader)
247 self.assertEqual(module.__package__, name)
248
249
250 (Frozen_ModuleForLoaderTests,
251 Source_ModuleForLoaderTests
252 ) = util.test_both(ModuleForLoaderTests, util=importlib_util)
253
254
255 class ESC[4;38;5;81mSetPackageTests:
256
257 """Tests for importlib.util.set_package."""
258
259 def verify(self, module, expect):
260 """Verify the module has the expected value for __package__ after
261 passing through set_package."""
262 fxn = lambda: module
263 wrapped = self.util.set_package(fxn)
264 with warnings.catch_warnings():
265 warnings.simplefilter('ignore', DeprecationWarning)
266 wrapped()
267 self.assertTrue(hasattr(module, '__package__'))
268 self.assertEqual(expect, module.__package__)
269
270 def test_top_level(self):
271 # __package__ should be set to the empty string if a top-level module.
272 # Implicitly tests when package is set to None.
273 module = types.ModuleType('module')
274 module.__package__ = None
275 self.verify(module, '')
276
277 def test_package(self):
278 # Test setting __package__ for a package.
279 module = types.ModuleType('pkg')
280 module.__path__ = ['<path>']
281 module.__package__ = None
282 self.verify(module, 'pkg')
283
284 def test_submodule(self):
285 # Test __package__ for a module in a package.
286 module = types.ModuleType('pkg.mod')
287 module.__package__ = None
288 self.verify(module, 'pkg')
289
290 def test_setting_if_missing(self):
291 # __package__ should be set if it is missing.
292 module = types.ModuleType('mod')
293 if hasattr(module, '__package__'):
294 delattr(module, '__package__')
295 self.verify(module, '')
296
297 def test_leaving_alone(self):
298 # If __package__ is set and not None then leave it alone.
299 for value in (True, False):
300 module = types.ModuleType('mod')
301 module.__package__ = value
302 self.verify(module, value)
303
304 def test_decorator_attrs(self):
305 def fxn(module): pass
306 with warnings.catch_warnings():
307 warnings.simplefilter('ignore', DeprecationWarning)
308 wrapped = self.util.set_package(fxn)
309 self.assertEqual(wrapped.__name__, fxn.__name__)
310 self.assertEqual(wrapped.__qualname__, fxn.__qualname__)
311
312
313 (Frozen_SetPackageTests,
314 Source_SetPackageTests
315 ) = util.test_both(SetPackageTests, util=importlib_util)
316
317
318 class ESC[4;38;5;81mSetLoaderTests:
319
320 """Tests importlib.util.set_loader()."""
321
322 @property
323 def DummyLoader(self):
324 # Set DummyLoader on the class lazily.
325 class ESC[4;38;5;81mDummyLoader:
326 @self.util.set_loader
327 def load_module(self, module):
328 return self.module
329 self.__class__.DummyLoader = DummyLoader
330 return DummyLoader
331
332 def test_no_attribute(self):
333 loader = self.DummyLoader()
334 loader.module = types.ModuleType('blah')
335 try:
336 del loader.module.__loader__
337 except AttributeError:
338 pass
339 with warnings.catch_warnings():
340 warnings.simplefilter('ignore', DeprecationWarning)
341 self.assertEqual(loader, loader.load_module('blah').__loader__)
342
343 def test_attribute_is_None(self):
344 loader = self.DummyLoader()
345 loader.module = types.ModuleType('blah')
346 loader.module.__loader__ = None
347 with warnings.catch_warnings():
348 warnings.simplefilter('ignore', DeprecationWarning)
349 self.assertEqual(loader, loader.load_module('blah').__loader__)
350
351 def test_not_reset(self):
352 loader = self.DummyLoader()
353 loader.module = types.ModuleType('blah')
354 loader.module.__loader__ = 42
355 with warnings.catch_warnings():
356 warnings.simplefilter('ignore', DeprecationWarning)
357 self.assertEqual(42, loader.load_module('blah').__loader__)
358
359
360 (Frozen_SetLoaderTests,
361 Source_SetLoaderTests
362 ) = util.test_both(SetLoaderTests, util=importlib_util)
363
364
365 class ESC[4;38;5;81mResolveNameTests:
366
367 """Tests importlib.util.resolve_name()."""
368
369 def test_absolute(self):
370 # bacon
371 self.assertEqual('bacon', self.util.resolve_name('bacon', None))
372
373 def test_absolute_within_package(self):
374 # bacon in spam
375 self.assertEqual('bacon', self.util.resolve_name('bacon', 'spam'))
376
377 def test_no_package(self):
378 # .bacon in ''
379 with self.assertRaises(ImportError):
380 self.util.resolve_name('.bacon', '')
381
382 def test_in_package(self):
383 # .bacon in spam
384 self.assertEqual('spam.eggs.bacon',
385 self.util.resolve_name('.bacon', 'spam.eggs'))
386
387 def test_other_package(self):
388 # ..bacon in spam.bacon
389 self.assertEqual('spam.bacon',
390 self.util.resolve_name('..bacon', 'spam.eggs'))
391
392 def test_escape(self):
393 # ..bacon in spam
394 with self.assertRaises(ImportError):
395 self.util.resolve_name('..bacon', 'spam')
396
397
398 (Frozen_ResolveNameTests,
399 Source_ResolveNameTests
400 ) = util.test_both(ResolveNameTests, util=importlib_util)
401
402
403 class ESC[4;38;5;81mFindSpecTests:
404
405 class ESC[4;38;5;81mFakeMetaFinder:
406 @staticmethod
407 def find_spec(name, path=None, target=None): return name, path, target
408
409 def test_sys_modules(self):
410 name = 'some_mod'
411 with util.uncache(name):
412 module = types.ModuleType(name)
413 loader = 'a loader!'
414 spec = self.machinery.ModuleSpec(name, loader)
415 module.__loader__ = loader
416 module.__spec__ = spec
417 sys.modules[name] = module
418 found = self.util.find_spec(name)
419 self.assertEqual(found, spec)
420
421 def test_sys_modules_without___loader__(self):
422 name = 'some_mod'
423 with util.uncache(name):
424 module = types.ModuleType(name)
425 del module.__loader__
426 loader = 'a loader!'
427 spec = self.machinery.ModuleSpec(name, loader)
428 module.__spec__ = spec
429 sys.modules[name] = module
430 found = self.util.find_spec(name)
431 self.assertEqual(found, spec)
432
433 def test_sys_modules_spec_is_None(self):
434 name = 'some_mod'
435 with util.uncache(name):
436 module = types.ModuleType(name)
437 module.__spec__ = None
438 sys.modules[name] = module
439 with self.assertRaises(ValueError):
440 self.util.find_spec(name)
441
442 def test_sys_modules_loader_is_None(self):
443 name = 'some_mod'
444 with util.uncache(name):
445 module = types.ModuleType(name)
446 spec = self.machinery.ModuleSpec(name, None)
447 module.__spec__ = spec
448 sys.modules[name] = module
449 found = self.util.find_spec(name)
450 self.assertEqual(found, spec)
451
452 def test_sys_modules_spec_is_not_set(self):
453 name = 'some_mod'
454 with util.uncache(name):
455 module = types.ModuleType(name)
456 try:
457 del module.__spec__
458 except AttributeError:
459 pass
460 sys.modules[name] = module
461 with self.assertRaises(ValueError):
462 self.util.find_spec(name)
463
464 def test_success(self):
465 name = 'some_mod'
466 with util.uncache(name):
467 with util.import_state(meta_path=[self.FakeMetaFinder]):
468 self.assertEqual((name, None, None),
469 self.util.find_spec(name))
470
471 def test_nothing(self):
472 # None is returned upon failure to find a loader.
473 self.assertIsNone(self.util.find_spec('nevergoingtofindthismodule'))
474
475 def test_find_submodule(self):
476 name = 'spam'
477 subname = 'ham'
478 with util.temp_module(name, pkg=True) as pkg_dir:
479 fullname, _ = util.submodule(name, subname, pkg_dir)
480 spec = self.util.find_spec(fullname)
481 self.assertIsNot(spec, None)
482 self.assertIn(name, sorted(sys.modules))
483 self.assertNotIn(fullname, sorted(sys.modules))
484 # Ensure successive calls behave the same.
485 spec_again = self.util.find_spec(fullname)
486 self.assertEqual(spec_again, spec)
487
488 def test_find_submodule_parent_already_imported(self):
489 name = 'spam'
490 subname = 'ham'
491 with util.temp_module(name, pkg=True) as pkg_dir:
492 self.init.import_module(name)
493 fullname, _ = util.submodule(name, subname, pkg_dir)
494 spec = self.util.find_spec(fullname)
495 self.assertIsNot(spec, None)
496 self.assertIn(name, sorted(sys.modules))
497 self.assertNotIn(fullname, sorted(sys.modules))
498 # Ensure successive calls behave the same.
499 spec_again = self.util.find_spec(fullname)
500 self.assertEqual(spec_again, spec)
501
502 def test_find_relative_module(self):
503 name = 'spam'
504 subname = 'ham'
505 with util.temp_module(name, pkg=True) as pkg_dir:
506 fullname, _ = util.submodule(name, subname, pkg_dir)
507 relname = '.' + subname
508 spec = self.util.find_spec(relname, name)
509 self.assertIsNot(spec, None)
510 self.assertIn(name, sorted(sys.modules))
511 self.assertNotIn(fullname, sorted(sys.modules))
512 # Ensure successive calls behave the same.
513 spec_again = self.util.find_spec(fullname)
514 self.assertEqual(spec_again, spec)
515
516 def test_find_relative_module_missing_package(self):
517 name = 'spam'
518 subname = 'ham'
519 with util.temp_module(name, pkg=True) as pkg_dir:
520 fullname, _ = util.submodule(name, subname, pkg_dir)
521 relname = '.' + subname
522 with self.assertRaises(ImportError):
523 self.util.find_spec(relname)
524 self.assertNotIn(name, sorted(sys.modules))
525 self.assertNotIn(fullname, sorted(sys.modules))
526
527 def test_find_submodule_in_module(self):
528 # ModuleNotFoundError raised when a module is specified as
529 # a parent instead of a package.
530 with self.assertRaises(ModuleNotFoundError):
531 self.util.find_spec('module.name')
532
533
534 (Frozen_FindSpecTests,
535 Source_FindSpecTests
536 ) = util.test_both(FindSpecTests, init=init, util=importlib_util,
537 machinery=machinery)
538
539
540 class ESC[4;38;5;81mMagicNumberTests:
541
542 def test_length(self):
543 # Should be 4 bytes.
544 self.assertEqual(len(self.util.MAGIC_NUMBER), 4)
545
546 def test_incorporates_rn(self):
547 # The magic number uses \r\n to come out wrong when splitting on lines.
548 self.assertTrue(self.util.MAGIC_NUMBER.endswith(b'\r\n'))
549
550
551 (Frozen_MagicNumberTests,
552 Source_MagicNumberTests
553 ) = util.test_both(MagicNumberTests, util=importlib_util)
554
555
556 class ESC[4;38;5;81mPEP3147Tests:
557
558 """Tests of PEP 3147-related functions: cache_from_source and source_from_cache."""
559
560 tag = sys.implementation.cache_tag
561
562 @unittest.skipIf(sys.implementation.cache_tag is None,
563 'requires sys.implementation.cache_tag not be None')
564 def test_cache_from_source(self):
565 # Given the path to a .py file, return the path to its PEP 3147
566 # defined .pyc file (i.e. under __pycache__).
567 path = os.path.join('foo', 'bar', 'baz', 'qux.py')
568 expect = os.path.join('foo', 'bar', 'baz', '__pycache__',
569 'qux.{}.pyc'.format(self.tag))
570 self.assertEqual(self.util.cache_from_source(path, optimization=''),
571 expect)
572
573 def test_cache_from_source_no_cache_tag(self):
574 # No cache tag means NotImplementedError.
575 with support.swap_attr(sys.implementation, 'cache_tag', None):
576 with self.assertRaises(NotImplementedError):
577 self.util.cache_from_source('whatever.py')
578
579 def test_cache_from_source_no_dot(self):
580 # Directory with a dot, filename without dot.
581 path = os.path.join('foo.bar', 'file')
582 expect = os.path.join('foo.bar', '__pycache__',
583 'file{}.pyc'.format(self.tag))
584 self.assertEqual(self.util.cache_from_source(path, optimization=''),
585 expect)
586
587 def test_cache_from_source_debug_override(self):
588 # Given the path to a .py file, return the path to its PEP 3147/PEP 488
589 # defined .pyc file (i.e. under __pycache__).
590 path = os.path.join('foo', 'bar', 'baz', 'qux.py')
591 with warnings.catch_warnings():
592 warnings.simplefilter('ignore')
593 self.assertEqual(self.util.cache_from_source(path, False),
594 self.util.cache_from_source(path, optimization=1))
595 self.assertEqual(self.util.cache_from_source(path, True),
596 self.util.cache_from_source(path, optimization=''))
597 with warnings.catch_warnings():
598 warnings.simplefilter('error')
599 with self.assertRaises(DeprecationWarning):
600 self.util.cache_from_source(path, False)
601 with self.assertRaises(DeprecationWarning):
602 self.util.cache_from_source(path, True)
603
604 def test_cache_from_source_cwd(self):
605 path = 'foo.py'
606 expect = os.path.join('__pycache__', 'foo.{}.pyc'.format(self.tag))
607 self.assertEqual(self.util.cache_from_source(path, optimization=''),
608 expect)
609
610 def test_cache_from_source_override(self):
611 # When debug_override is not None, it can be any true-ish or false-ish
612 # value.
613 path = os.path.join('foo', 'bar', 'baz.py')
614 # However if the bool-ishness can't be determined, the exception
615 # propagates.
616 class ESC[4;38;5;81mBearish:
617 def __bool__(self): raise RuntimeError
618 with warnings.catch_warnings():
619 warnings.simplefilter('ignore')
620 self.assertEqual(self.util.cache_from_source(path, []),
621 self.util.cache_from_source(path, optimization=1))
622 self.assertEqual(self.util.cache_from_source(path, [17]),
623 self.util.cache_from_source(path, optimization=''))
624 with self.assertRaises(RuntimeError):
625 self.util.cache_from_source('/foo/bar/baz.py', Bearish())
626
627
628 def test_cache_from_source_optimization_empty_string(self):
629 # Setting 'optimization' to '' leads to no optimization tag (PEP 488).
630 path = 'foo.py'
631 expect = os.path.join('__pycache__', 'foo.{}.pyc'.format(self.tag))
632 self.assertEqual(self.util.cache_from_source(path, optimization=''),
633 expect)
634
635 def test_cache_from_source_optimization_None(self):
636 # Setting 'optimization' to None uses the interpreter's optimization.
637 # (PEP 488)
638 path = 'foo.py'
639 optimization_level = sys.flags.optimize
640 almost_expect = os.path.join('__pycache__', 'foo.{}'.format(self.tag))
641 if optimization_level == 0:
642 expect = almost_expect + '.pyc'
643 elif optimization_level <= 2:
644 expect = almost_expect + '.opt-{}.pyc'.format(optimization_level)
645 else:
646 msg = '{!r} is a non-standard optimization level'.format(optimization_level)
647 self.skipTest(msg)
648 self.assertEqual(self.util.cache_from_source(path, optimization=None),
649 expect)
650
651 def test_cache_from_source_optimization_set(self):
652 # The 'optimization' parameter accepts anything that has a string repr
653 # that passes str.alnum().
654 path = 'foo.py'
655 valid_characters = string.ascii_letters + string.digits
656 almost_expect = os.path.join('__pycache__', 'foo.{}'.format(self.tag))
657 got = self.util.cache_from_source(path, optimization=valid_characters)
658 # Test all valid characters are accepted.
659 self.assertEqual(got,
660 almost_expect + '.opt-{}.pyc'.format(valid_characters))
661 # str() should be called on argument.
662 self.assertEqual(self.util.cache_from_source(path, optimization=42),
663 almost_expect + '.opt-42.pyc')
664 # Invalid characters raise ValueError.
665 with self.assertRaises(ValueError):
666 self.util.cache_from_source(path, optimization='path/is/bad')
667
668 def test_cache_from_source_debug_override_optimization_both_set(self):
669 # Can only set one of the optimization-related parameters.
670 with warnings.catch_warnings():
671 warnings.simplefilter('ignore')
672 with self.assertRaises(TypeError):
673 self.util.cache_from_source('foo.py', False, optimization='')
674
675 @unittest.skipUnless(os.sep == '\\' and os.altsep == '/',
676 'test meaningful only where os.altsep is defined')
677 def test_sep_altsep_and_sep_cache_from_source(self):
678 # Windows path and PEP 3147 where sep is right of altsep.
679 self.assertEqual(
680 self.util.cache_from_source('\\foo\\bar\\baz/qux.py', optimization=''),
681 '\\foo\\bar\\baz\\__pycache__\\qux.{}.pyc'.format(self.tag))
682
683 @unittest.skipIf(sys.implementation.cache_tag is None,
684 'requires sys.implementation.cache_tag not be None')
685 def test_cache_from_source_path_like_arg(self):
686 path = pathlib.PurePath('foo', 'bar', 'baz', 'qux.py')
687 expect = os.path.join('foo', 'bar', 'baz', '__pycache__',
688 'qux.{}.pyc'.format(self.tag))
689 self.assertEqual(self.util.cache_from_source(path, optimization=''),
690 expect)
691
692 @unittest.skipIf(sys.implementation.cache_tag is None,
693 'requires sys.implementation.cache_tag to not be None')
694 def test_source_from_cache(self):
695 # Given the path to a PEP 3147 defined .pyc file, return the path to
696 # its source. This tests the good path.
697 path = os.path.join('foo', 'bar', 'baz', '__pycache__',
698 'qux.{}.pyc'.format(self.tag))
699 expect = os.path.join('foo', 'bar', 'baz', 'qux.py')
700 self.assertEqual(self.util.source_from_cache(path), expect)
701
702 def test_source_from_cache_no_cache_tag(self):
703 # If sys.implementation.cache_tag is None, raise NotImplementedError.
704 path = os.path.join('blah', '__pycache__', 'whatever.pyc')
705 with support.swap_attr(sys.implementation, 'cache_tag', None):
706 with self.assertRaises(NotImplementedError):
707 self.util.source_from_cache(path)
708
709 def test_source_from_cache_bad_path(self):
710 # When the path to a pyc file is not in PEP 3147 format, a ValueError
711 # is raised.
712 self.assertRaises(
713 ValueError, self.util.source_from_cache, '/foo/bar/bazqux.pyc')
714
715 def test_source_from_cache_no_slash(self):
716 # No slashes at all in path -> ValueError
717 self.assertRaises(
718 ValueError, self.util.source_from_cache, 'foo.cpython-32.pyc')
719
720 def test_source_from_cache_too_few_dots(self):
721 # Too few dots in final path component -> ValueError
722 self.assertRaises(
723 ValueError, self.util.source_from_cache, '__pycache__/foo.pyc')
724
725 def test_source_from_cache_too_many_dots(self):
726 with self.assertRaises(ValueError):
727 self.util.source_from_cache(
728 '__pycache__/foo.cpython-32.opt-1.foo.pyc')
729
730 def test_source_from_cache_not_opt(self):
731 # Non-`opt-` path component -> ValueError
732 self.assertRaises(
733 ValueError, self.util.source_from_cache,
734 '__pycache__/foo.cpython-32.foo.pyc')
735
736 def test_source_from_cache_no__pycache__(self):
737 # Another problem with the path -> ValueError
738 self.assertRaises(
739 ValueError, self.util.source_from_cache,
740 '/foo/bar/foo.cpython-32.foo.pyc')
741
742 def test_source_from_cache_optimized_bytecode(self):
743 # Optimized bytecode is not an issue.
744 path = os.path.join('__pycache__', 'foo.{}.opt-1.pyc'.format(self.tag))
745 self.assertEqual(self.util.source_from_cache(path), 'foo.py')
746
747 def test_source_from_cache_missing_optimization(self):
748 # An empty optimization level is a no-no.
749 path = os.path.join('__pycache__', 'foo.{}.opt-.pyc'.format(self.tag))
750 with self.assertRaises(ValueError):
751 self.util.source_from_cache(path)
752
753 @unittest.skipIf(sys.implementation.cache_tag is None,
754 'requires sys.implementation.cache_tag to not be None')
755 def test_source_from_cache_path_like_arg(self):
756 path = pathlib.PurePath('foo', 'bar', 'baz', '__pycache__',
757 'qux.{}.pyc'.format(self.tag))
758 expect = os.path.join('foo', 'bar', 'baz', 'qux.py')
759 self.assertEqual(self.util.source_from_cache(path), expect)
760
761 @unittest.skipIf(sys.implementation.cache_tag is None,
762 'requires sys.implementation.cache_tag to not be None')
763 def test_cache_from_source_respects_pycache_prefix(self):
764 # If pycache_prefix is set, cache_from_source will return a bytecode
765 # path inside that directory (in a subdirectory mirroring the .py file's
766 # path) rather than in a __pycache__ dir next to the py file.
767 pycache_prefixes = [
768 os.path.join(os.path.sep, 'tmp', 'bytecode'),
769 os.path.join(os.path.sep, 'tmp', '\u2603'), # non-ASCII in path!
770 os.path.join(os.path.sep, 'tmp', 'trailing-slash') + os.path.sep,
771 ]
772 drive = ''
773 if os.name == 'nt':
774 drive = 'C:'
775 pycache_prefixes = [
776 f'{drive}{prefix}' for prefix in pycache_prefixes]
777 pycache_prefixes += [r'\\?\C:\foo', r'\\localhost\c$\bar']
778 for pycache_prefix in pycache_prefixes:
779 with self.subTest(path=pycache_prefix):
780 path = drive + os.path.join(
781 os.path.sep, 'foo', 'bar', 'baz', 'qux.py')
782 expect = os.path.join(
783 pycache_prefix, 'foo', 'bar', 'baz',
784 'qux.{}.pyc'.format(self.tag))
785 with util.temporary_pycache_prefix(pycache_prefix):
786 self.assertEqual(
787 self.util.cache_from_source(path, optimization=''),
788 expect)
789
790 @unittest.skipIf(sys.implementation.cache_tag is None,
791 'requires sys.implementation.cache_tag to not be None')
792 def test_cache_from_source_respects_pycache_prefix_relative(self):
793 # If the .py path we are given is relative, we will resolve to an
794 # absolute path before prefixing with pycache_prefix, to avoid any
795 # possible ambiguity.
796 pycache_prefix = os.path.join(os.path.sep, 'tmp', 'bytecode')
797 path = os.path.join('foo', 'bar', 'baz', 'qux.py')
798 root = os.path.splitdrive(os.getcwd())[0] + os.path.sep
799 expect = os.path.join(
800 pycache_prefix,
801 os.path.relpath(os.getcwd(), root),
802 'foo', 'bar', 'baz', f'qux.{self.tag}.pyc')
803 with util.temporary_pycache_prefix(pycache_prefix):
804 self.assertEqual(
805 self.util.cache_from_source(path, optimization=''),
806 expect)
807
808 @unittest.skipIf(sys.implementation.cache_tag is None,
809 'requires sys.implementation.cache_tag to not be None')
810 def test_source_from_cache_inside_pycache_prefix(self):
811 # If pycache_prefix is set and the cache path we get is inside it,
812 # we return an absolute path to the py file based on the remainder of
813 # the path within pycache_prefix.
814 pycache_prefix = os.path.join(os.path.sep, 'tmp', 'bytecode')
815 path = os.path.join(pycache_prefix, 'foo', 'bar', 'baz',
816 f'qux.{self.tag}.pyc')
817 expect = os.path.join(os.path.sep, 'foo', 'bar', 'baz', 'qux.py')
818 with util.temporary_pycache_prefix(pycache_prefix):
819 self.assertEqual(self.util.source_from_cache(path), expect)
820
821 @unittest.skipIf(sys.implementation.cache_tag is None,
822 'requires sys.implementation.cache_tag to not be None')
823 def test_source_from_cache_outside_pycache_prefix(self):
824 # If pycache_prefix is set but the cache path we get is not inside
825 # it, just ignore it and handle the cache path according to the default
826 # behavior.
827 pycache_prefix = os.path.join(os.path.sep, 'tmp', 'bytecode')
828 path = os.path.join('foo', 'bar', 'baz', '__pycache__',
829 f'qux.{self.tag}.pyc')
830 expect = os.path.join('foo', 'bar', 'baz', 'qux.py')
831 with util.temporary_pycache_prefix(pycache_prefix):
832 self.assertEqual(self.util.source_from_cache(path), expect)
833
834
835 (Frozen_PEP3147Tests,
836 Source_PEP3147Tests
837 ) = util.test_both(PEP3147Tests, util=importlib_util)
838
839
840 class ESC[4;38;5;81mMagicNumberTests(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
841 """
842 Test release compatibility issues relating to importlib
843 """
844 @unittest.skipUnless(
845 sys.version_info.releaselevel in ('candidate', 'final'),
846 'only applies to candidate or final python release levels'
847 )
848 def test_magic_number(self):
849 # Each python minor release should generally have a MAGIC_NUMBER
850 # that does not change once the release reaches candidate status.
851
852 # Once a release reaches candidate status, the value of the constant
853 # EXPECTED_MAGIC_NUMBER in this test should be changed.
854 # This test will then check that the actual MAGIC_NUMBER matches
855 # the expected value for the release.
856
857 # In exceptional cases, it may be required to change the MAGIC_NUMBER
858 # for a maintenance release. In this case the change should be
859 # discussed in python-dev. If a change is required, community
860 # stakeholders such as OS package maintainers must be notified
861 # in advance. Such exceptional releases will then require an
862 # adjustment to this test case.
863 EXPECTED_MAGIC_NUMBER = 3495
864 actual = int.from_bytes(importlib.util.MAGIC_NUMBER[:2], 'little')
865
866 msg = (
867 "To avoid breaking backwards compatibility with cached bytecode "
868 "files that can't be automatically regenerated by the current "
869 "user, candidate and final releases require the current "
870 "importlib.util.MAGIC_NUMBER to match the expected "
871 "magic number in this test. Set the expected "
872 "magic number in this test to the current MAGIC_NUMBER to "
873 "continue with the release.\n\n"
874 "Changing the MAGIC_NUMBER for a maintenance release "
875 "requires discussion in python-dev and notification of "
876 "community stakeholders."
877 )
878 self.assertEqual(EXPECTED_MAGIC_NUMBER, actual, msg)
879
880
881 if __name__ == '__main__':
882 unittest.main()