New submission from Géry <gery.o...@gmail.com>: Analysis ========
In the next two sections showing the module attributes and corresponding spec attributes of imported modules and run modules, we notice the following rule (which is in accordance with this `PEP 451 section <https://www.python.org/dev/peps/pep-0451/#attributes>`_): If a *spec* attribute is either ``None``, ``'built-in'`` or ``'frozen'``, then the corresponding *module* attribute is not set. However we also notice three exceptions to this rule, that I think are unintended inconsistencies that should be corrected: - ``module.__file__ is None`` (instead of being not set) for imported namespace packages; - ``module.__cached__ is None`` (instead of being not set) for non-package modules run from the file system and run from standard input; - ``module.__package__ is None`` (instead of being ``''``) for non-package modules run from the file system and run from standard input. The first exception was introduced recently (26 February 2018) by this `pull request <https://github.com/python/cpython/pull/5481>`_, which changed the ``module.__spec__.origin`` attribute from ``namespace`` to ``None`` (which I agree with as it avoids conflicting non-namespace-package modules named ``namespace`` with namespace packages) and the ``module.__file__`` attribute from being unset to ``None`` (which I disagree with as it introduces an inconsistency and contradicts PEP 451). Environment: CPython 3.7, MacOS 10.14. Imported modules ================ Running the following code:: import module print("MODULE") for attr in ["__name__", "__file__", "__cached__", "__path__", "__package__", "__loader__"]: print(f"{attr}:", repr(getattr(module, attr, "not set"))) print("SPEC") if hasattr(module, "__spec__"): if module.__spec__ is None: print("__spec__:", repr(module.__spec__)) else: for attr in ["name", "origin", "cached", "submodule_search_locations", "parent", "loader"]: print(f"__spec__.{attr}:", repr(getattr(module.__spec__, attr))) else: print("__spec__: not set") where ``module`` refers to: - a non-package module (e.g., ``pathlib``); - a regular package (e.g., ``json``); - a namespace package; - a built-in module (e.g., ``time``); - a frozen module (e.g., ``_frozen_importlib_external``) prints the following module attributes and corresponding spec attributes of the imported ``module``: - for a non-package module: | MODULE | __name__: 'pathlib' | __file__: '/usr/local/Cellar/python/3.7.0/Frameworks/Python.framework/Versions/3.7/lib/python3.7/pathlib.py' | __cached__: '/usr/local/Cellar/python/3.7.0/Frameworks/Python.framework/Versions/3.7/lib/python3.7/__pycache__/pathlib.cpython-37.pyc' | __path__: 'not set' | __package__: '' | __loader__: <_frozen_importlib_external.SourceFileLoader object at 0x1018896d8> | SPEC | __spec__.name: 'pathlib' | __spec__.origin: '/usr/local/Cellar/python/3.7.0/Frameworks/Python.framework/Versions/3.7/lib/python3.7/pathlib.py' | __spec__.cached: '/usr/local/Cellar/python/3.7.0/Frameworks/Python.framework/Versions/3.7/lib/python3.7/__pycache__/pathlib.cpython-37.pyc' | __spec__.submodule_search_locations: None | __spec__.parent: '' | __spec__.loader: <_frozen_importlib_external.SourceFileLoader object at 0x1018896d8> - for a regular package: | MODULE | __name__: 'json' | __file__: '/usr/local/Cellar/python/3.7.0/Frameworks/Python.framework/Versions/3.7/lib/python3.7/json/__init__.py' | __cached__: '/usr/local/Cellar/python/3.7.0/Frameworks/Python.framework/Versions/3.7/lib/python3.7/json/__pycache__/__init__.cpython-37.pyc' | __path__: ['/usr/local/Cellar/python/3.7.0/Frameworks/Python.framework/Versions/3.7/lib/python3.7/json'] | __package__: 'json' | __loader__: <_frozen_importlib_external.SourceFileLoader object at 0x10f9aa6d8> | SPEC | __spec__.name: 'json' | __spec__.origin: '/usr/local/Cellar/python/3.7.0/Frameworks/Python.framework/Versions/3.7/lib/python3.7/json/__init__.py' | __spec__.cached: '/usr/local/Cellar/python/3.7.0/Frameworks/Python.framework/Versions/3.7/lib/python3.7/json/__pycache__/__init__.cpython-37.pyc' | __spec__.submodule_search_locations: ['/usr/local/Cellar/python/3.7.0/Frameworks/Python.framework/Versions/3.7/lib/python3.7/json'] | __spec__.parent: 'json' | __spec__.loader: <_frozen_importlib_external.SourceFileLoader object at 0x10f9aa6d8> - for a namespace package: | MODULE | __name__: 'foobar' | __file__: None | __cached__: 'not set' | __path__: _NamespacePath(['/Users/maggyero/foobar']) | __package__: 'foobar' | __loader__: <_frozen_importlib_external._NamespaceLoader object at 0x1074564a8> | SPEC | __spec__.name: 'foobar' | __spec__.origin: None | __spec__.cached: None | __spec__.submodule_search_locations: _NamespacePath(['/Users/maggyero/foobar']) | __spec__.parent: 'foobar' | __spec__.loader: <_frozen_importlib_external._NamespaceLoader object at 0x1074564a8> - for a built-in module: | MODULE | __name__: 'time' | __file__: 'not set' | __cached__: 'not set' | __path__: 'not set' | __package__: '' | __loader__: <class '_frozen_importlib.BuiltinImporter'> | SPEC | __spec__.name: 'time' | __spec__.origin: 'built-in' | __spec__.cached: None | __spec__.submodule_search_locations: None | __spec__.parent: '' | __spec__.loader: <class '_frozen_importlib.BuiltinImporter'> - for a frozen module: | MODULE | __name__: '_frozen_importlib_external' | __file__: 'not set' | __cached__: 'not set' | __path__: 'not set' | __package__: '' | __loader__: <class '_frozen_importlib.FrozenImporter'> | SPEC | __spec__.name: '_frozen_importlib_external' | __spec__.origin: 'frozen' | __spec__.cached: None | __spec__.submodule_search_locations: None | __spec__.parent: '' | __spec__.loader: <class '_frozen_importlib.FrozenImporter'> Run modules =========== Putting the following code:: import sys print("MODULE") for attr in ["__name__", "__file__", "__cached__", "__path__", "__package__", "__loader__"]: print(f"{attr}:", repr(getattr(sys.modules[__name__], attr, "not set"))) print("SPEC") if hasattr(sys.modules[__name__], "__spec__"): if sys.modules[__name__].__spec__ is None: print("__spec__:", repr(sys.modules[__name__].__spec__)) else: for attr in ["name", "origin", "cached", "submodule_search_locations", "parent", "loader"]: print(f"__spec__.{attr}:", repr(getattr(sys.modules[__name__].__spec__, attr))) else: print("__spec__: not set") in: - a module.py file for getting a ``module`` non-package module; - a __main__.py file in a module directory with an __init__.py file for getting a ``module`` regular package; - a __main__.py file in a module directory without an __init__.py file for getting a ``module`` namespace package and running the code: - from the file system (``python3 module.py`` for a non-package module, ``python3 module/`` for a package module); - from standard input (``cat module.py | python3`` for a non-package module); - from the module namespace (``python3 -m module``) prints the following module attributes and corresponding spec attributes of the run ``module``: - for a non-package module: | $ python3 module.py | MODULE | __name__: '__main__' | __file__: 'module.py' | __cached__: None | __path__: 'not set' | __package__: None | __loader__: <_frozen_importlib_external.SourceFileLoader object at 0x1051970b8> | SPEC | __spec__: None | | $ cat module.py | python3 | MODULE | __name__: '__main__' | __file__: '<stdin>' | __cached__: None | __path__: 'not set' | __package__: None | __loader__: <class '_frozen_importlib.BuiltinImporter'> | SPEC | __spec__: None | | $ python3 -m module | MODULE | __name__: '__main__' | __file__: '/Users/maggyero/module.py' | __cached__: '/Users/maggyero/__pycache__/module.cpython-37.pyc' | __path__: 'not set' | __package__: '' | __loader__: <_frozen_importlib_external.SourceFileLoader object at 0x1056b16d8> | SPEC | __spec__.name: 'module' | __spec__.origin: '/Users/maggyero/module.py' | __spec__.cached: '/Users/maggyero/__pycache__/module.cpython-37.pyc' | __spec__.submodule_search_locations: None | __spec__.parent: '' | __spec__.loader: <_frozen_importlib_external.SourceFileLoader object at 0x1056b16d8> - for a regular package: | $ python3 module/ | MODULE | __name__: '__main__' | __file__: 'module/__main__.py' | __cached__: 'module/__pycache__/__main__.cpython-37.pyc' | __path__: 'not set' | __package__: '' | __loader__: <_frozen_importlib_external.SourceFileLoader object at 0x10826a550> | SPEC | __spec__.name: '__main__' | __spec__.origin: 'module/__main__.py' | __spec__.cached: 'module/__pycache__/__main__.cpython-37.pyc' | __spec__.submodule_search_locations: None | __spec__.parent: '' | __spec__.loader: <_frozen_importlib_external.SourceFileLoader object at 0x10826a550> | | $ python3 -m module | MODULE | __name__: '__main__' | __file__: '/Users/maggyero/module/__main__.py' | __cached__: '/Users/maggyero/module/__pycache__/__main__.cpython-37.pyc' | __path__: 'not set' | __package__: 'module' | __loader__: <_frozen_importlib_external.SourceFileLoader object at 0x10832d278> | SPEC | __spec__.name: 'module.__main__' | __spec__.origin: '/Users/maggyero/module/__main__.py' | __spec__.cached: '/Users/maggyero/module/__pycache__/__main__.cpython-37.pyc' | __spec__.submodule_search_locations: None | __spec__.parent: 'module' | __spec__.loader: <_frozen_importlib_external.SourceFileLoader object at 0x10832d278> - for a namespace package: | $ python3 module/ | MODULE | __name__: '__main__' | __file__: 'module/__main__.py' | __cached__: 'module/__pycache__/__main__.cpython-37.pyc' | __path__: 'not set' | __package__: '' | __loader__: <_frozen_importlib_external.SourceFileLoader object at 0x107a06518> | SPEC | __spec__.name: '__main__' | __spec__.origin: 'module/__main__.py' | __spec__.cached: 'module/__pycache__/__main__.cpython-37.pyc' | __spec__.submodule_search_locations: None | __spec__.parent: '' | __spec__.loader: <_frozen_importlib_external.SourceFileLoader object at 0x107a06518> | | $ python3 -m module | MODULE | __name__: '__main__' | __file__: '/Users/maggyero/module/__main__.py' | __cached__: '/Users/maggyero/module/__pycache__/__main__.cpython-37.pyc' | __path__: 'not set' | __package__: 'module' | __loader__: <_frozen_importlib_external.SourceFileLoader object at 0x10fb69240> | SPEC | __spec__.name: 'module.__main__' | __spec__.origin: '/Users/maggyero/module/__main__.py' | __spec__.cached: '/Users/maggyero/module/__pycache__/__main__.cpython-37.pyc' | __spec__.submodule_search_locations: None | __spec__.parent: 'module' | __spec__.loader: <_frozen_importlib_external.SourceFileLoader object at 0x10fb69240> ---------- components: Interpreter Core, Library (Lib) messages: 347470 nosy: barry, eric.smith, maggyero, ncoghlan priority: normal severity: normal status: open title: Three inconsistent module attributes type: behavior versions: Python 3.7 _______________________________________ Python tracker <rep...@bugs.python.org> <https://bugs.python.org/issue37519> _______________________________________ _______________________________________________ Python-bugs-list mailing list Unsubscribe: https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com