python / cpython

The Python programming language
https://www.python.org
Other
62.68k stars 30.06k forks source link

Three inconsistent module attributes #81700

Open 53cd72ed-7768-4f34-9566-9995ab0e9dae opened 5 years ago

53cd72ed-7768-4f34-9566-9995ab0e9dae commented 5 years ago
BPO 37519
Nosy @warsaw, @brettcannon, @ncoghlan, @ericvsmith, @ericsnowcurrently, @maggyero

Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

Show more details

GitHub fields: ```python assignee = None closed_at = None created_at = labels = ['interpreter-core', 'type-bug', 'library', '3.7'] title = 'Three inconsistent module attributes' updated_at = user = 'https://github.com/maggyero' ``` bugs.python.org fields: ```python activity = actor = 'eric.smith' assignee = 'none' closed = False closed_date = None closer = None components = ['Interpreter Core', 'Library (Lib)'] creation = creator = 'maggyero' dependencies = [] files = [] hgrepos = [] issue_num = 37519 keywords = [] message_count = 4.0 messages = ['347470', '347507', '347960', '347988'] nosy_count = 6.0 nosy_names = ['barry', 'brett.cannon', 'ncoghlan', 'eric.smith', 'eric.snow', 'maggyero'] pr_nums = [] priority = 'normal' resolution = None stage = None status = 'open' superseder = None type = 'behavior' url = 'https://bugs.python.org/issue37519' versions = ['Python 3.7'] ```

53cd72ed-7768-4f34-9566-9995ab0e9dae commented 5 years ago

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:

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:

prints the following module attributes and corresponding spec attributes of the imported 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_importlibexternal.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_searchlocations: None | \_spec.parent: '' | __spec.loader: \<_frozen_importlib_external.SourceFileLoader object at 0x1018896d8>

| 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_importlibexternal.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_searchlocations: ['/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>

| MODULE | __name: 'foobar' | __file: None | __cached: 'not set' | __path: _NamespacePath(['/Users/maggyero/foobar']) | __package: 'foobar' | __loader: \<_frozen_importlibexternal._NamespaceLoader object at 0x1074564a8> | SPEC | \_spec.name: 'foobar' | __spec.origin: None | __spec.cached: None | __spec.submodule_searchlocations: _NamespacePath(['/Users/maggyero/foobar']) | \_spec.parent: 'foobar' | __spec.loader: \<_frozen_importlib_external._NamespaceLoader object at 0x1074564a8>

| MODULE | __name: 'time' | __file: 'not set' | __cached: 'not set' | __path: 'not set' | __package: '' | __loader: \<class '_frozenimportlib.BuiltinImporter'> | SPEC | \_spec.name: 'time' | __spec.origin: 'built-in' | __spec.cached: None | __spec.submodule_searchlocations: None | \_spec.parent: '' | __spec.loader: \<class '_frozen_importlib.BuiltinImporter'>

| MODULE | __name: '_frozen_importlibexternal' | \_file: 'not set' | __cached: 'not set' | __path: 'not set' | __package: '' | __loader: \<class '_frozenimportlib.FrozenImporter'> | SPEC | \_spec.name: '_frozen_importlibexternal' | \_spec.origin: 'frozen' | __spec.cached: None | __spec.submodule_searchlocations: 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:

and running the code:

prints the following module attributes and corresponding spec attributes of the run 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>
$ 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>
$ 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>
brettcannon commented 5 years ago

PEPs actually become historical documents once they are implemented, so could you please check what the official docs say in regards to this to see if there is an inconsistency in the semantics?

53cd72ed-7768-4f34-9566-9995ab0e9dae commented 5 years ago

@brett Cannon

PEPs actually become historical documents once they are implemented

Actually the inconsistency of the values of the 3 module attributes (__file__, __cached__ and __package__) is with respect to the other values within the current implementation (not only with respect to the values specified in PEP-451). Sorry if I did not explain this well. Let me detail:

For __file__, if you look at the current output of the above "Imported modules" section, you have:

which is inconsistent: it should always be 'not set' when __file__ has no meaning.

For __cached__, if you look at the current output of the above "Run modules" section, you have:

which is inconsistent: it should always be 'not set' when __cached__ has no meaning, like it is already the case for __path__ and other module attributes.

For __package__, if you look at the current output of the above "Run modules" section, you have:

which is inconsistent: it should always be '' when there is no parent package for __package__ to refer to.

ericvsmith commented 5 years ago

While some of these might be inconsistent (I haven't really looked at it thoroughly yet), I think it might be problematic to change them at this point, since there's no doubt code out there that depends on the current behavior.

picnixz commented 3 weeks ago

Related: https://github.com/python/cpython/issues/64019