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