From 57b46f5b0ed0314c3733b96e6ce2f99d526db4ed Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Mon, 2 Mar 2009 14:38:26 +0000 Subject: Expose importlib.util.set___package__. --- Doc/library/importlib.rst | 34 ++++++++++++++++----------- Lib/importlib/NOTES | 26 ++++----------------- Lib/importlib/test/test_util.py | 51 ++++++++++++++++++++++++++++++++++++++++- Lib/importlib/util.py | 1 + 4 files changed, 77 insertions(+), 35 deletions(-) diff --git a/Doc/library/importlib.rst b/Doc/library/importlib.rst index dc015c7..4a0ff0e 100644 --- a/Doc/library/importlib.rst +++ b/Doc/library/importlib.rst @@ -167,20 +167,28 @@ an :term:`importer`. A :term:`decorator` for a :term:`loader` which handles selecting the proper module object to load with. The decorated method is expected to have a call signature of ``method(self, module_object)`` for which the second argument - will be the module object to be used (note that the decorator will not work - on static methods because of the assumption of two arguments). + will be the module object to be used by the loader (note that the decorator + will not work on static methods because of the assumption of two + arguments). The decorated method will take in the name of the module to be loaded as - normal. If the module is not found in :data:`sys.modules` then a new one is - constructed with its :attr:`__name__` attribute set. Otherwise the module - found in :data:`sys.modules` will be passed into the method. If an + expected for a :term:`loader`. If the module is not found in + :data:`sys.modules` then a new one is constructed with its + :attr:`__name__` attribute set. Otherwise the module found in + :data:`sys.modules` will be passed into the method. If an exception is raised by the decorated method and a module was added to :data:`sys.modules` it will be removed to prevent a partially initialized - module from being in left in :data:`sys.modules` If an exception is raised - by the decorated method and a module was added to :data:`sys.modules` it - will be removed to prevent a partially initialized module from being in - left in :data:`sys.modules`. If the module was already in - :data:`sys.modules` then it is left alone. - - Use of this decorator handles all the details of what module a loader - should use as specified by :pep:`302`. + module from being in left in :data:`sys.modules`. If the module was already + in :data:`sys.modules` then it is left alone. + + Use of this decorator handles all the details of what module object a + loader should initialize as specified by :pep:`302`. + + +.. function:: set___package__(method) + + A :term:`decorator` for a :term:`loader` to set the :attr:`__package__` + attribute on the module returned by the loader. If :attr:`__package__` is + set and has a value other than :keyword:`None` it will not be changed. + Note that the module returned by the loader is what has the attribute + set on and not the module found in :data:`sys.modules`. diff --git a/Lib/importlib/NOTES b/Lib/importlib/NOTES index 22103a1..72b7da8 100644 --- a/Lib/importlib/NOTES +++ b/Lib/importlib/NOTES @@ -1,10 +1,6 @@ to do ///// -* Implement PEP 302 protocol for loaders (should just be a matter of testing). - - + Source/bytecode. - * Public API left to expose (w/ docs!) + abc @@ -27,27 +23,15 @@ to do * get_code * get_source - - (?) SourceLoader(ResourceLoader) + - PyLoader(ResourceLoader) * source_path - * bytecode_path - * write_bytecode (not abstract) - - + util - - - set___package__ decorator - + machinery + - PyPycLoader(PyLoader) - - Extensions importers - - * ExtensionFinder - * (?) Loader - - - Source/bytecode importers - - * SourceFinder - * (?) Loader + * source_mtime + * bytecode_path + * write_bytecode + test (Really want to worry about compatibility with future versions?) diff --git a/Lib/importlib/test/test_util.py b/Lib/importlib/test/test_util.py index 476b43f..8bd35f1 100644 --- a/Lib/importlib/test/test_util.py +++ b/Lib/importlib/test/test_util.py @@ -60,9 +60,58 @@ class ModuleForLoaderTests(unittest.TestCase): self.assert_(sys.modules[name] is module) +class SetPackageTests(unittest.TestCase): + + + """Tests for importlib.util.set___package__.""" + + def verify(self, module, expect): + """Verify the module has the expected value for __package__ after + passing through set___package__.""" + fxn = lambda: module + wrapped = util.set___package__(fxn) + wrapped() + self.assert_(hasattr(module, '__package__')) + self.assertEqual(expect, module.__package__) + + def test_top_level(self): + # __package__ should be set to the empty string if a top-level module. + # Implicitly tests when package is set to None. + module = imp.new_module('module') + module.__package__ = None + self.verify(module, '') + + def test_package(self): + # Test setting __package__ for a package. + module = imp.new_module('pkg') + module.__path__ = [''] + module.__package__ = None + self.verify(module, 'pkg') + + def test_submodule(self): + # Test __package__ for a module in a package. + module = imp.new_module('pkg.mod') + module.__package__ = None + self.verify(module, 'pkg') + + def test_setting_if_missing(self): + # __package__ should be set if it is missing. + module = imp.new_module('mod') + if hasattr(module, '__package__'): + delattr(module, '__package__') + self.verify(module, '') + + def test_leaving_alone(self): + # If __package__ is set and not None then leave it alone. + for value in (True, False): + module = imp.new_module('mod') + module.__package__ = value + self.verify(module, value) + + def test_main(): from test import support - support.run_unittest(ModuleForLoaderTests) + support.run_unittest(ModuleForLoaderTests, SetPackageTests) if __name__ == '__main__': diff --git a/Lib/importlib/util.py b/Lib/importlib/util.py index 6250d5b..2b6154b 100644 --- a/Lib/importlib/util.py +++ b/Lib/importlib/util.py @@ -1,2 +1,3 @@ """Utility code for constructing importers, etc.""" from ._bootstrap import module_for_loader +from ._bootstrap import set___package__ -- cgit v0.12