This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Author maggyero
Recipients barry, eric.smith, maggyero, ncoghlan
Date 2019-07-07.12:30:51
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1562502652.41.0.952456238618.issue37519@roundup.psfhosted.org>
In-reply-to
Content
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>
History
Date User Action Args
2019-07-07 12:30:52maggyerosetrecipients: + maggyero, barry, ncoghlan, eric.smith
2019-07-07 12:30:52maggyerosetmessageid: <1562502652.41.0.952456238618.issue37519@roundup.psfhosted.org>
2019-07-07 12:30:52maggyerolinkissue37519 messages
2019-07-07 12:30:51maggyerocreate