Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 4c14b5d

Browse files
committed
#17115,17116: Have modules initialize the __package__ and __loader__
attributes to None. The long-term goal is for people to be able to rely on these attributes existing and checking for None to see if they have been set. Since import itself sets these attributes when a loader does not the only instances when the attributes are None are from someone overloading __import__() and not using a loader or someone creating a module from scratch. This patch also unifies module initialization. Before you could have different attributes with default values depending on how the module object was created. Now the only way to not get the same default set of attributes is to circumvent initialization by calling ModuleType.__new__() directly.
1 parent 4cfc0b5 commit 4c14b5d

15 files changed

Lines changed: 264 additions & 220 deletions

File tree

Doc/c-api/module.rst

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,20 @@ There are only a few functions special to module objects.
3535
single: __name__ (module attribute)
3636
single: __doc__ (module attribute)
3737
single: __file__ (module attribute)
38+
single: __package__ (module attribute)
39+
single: __loader__ (module attribute)
3840
3941
Return a new module object with the :attr:`__name__` attribute set to *name*.
40-
Only the module's :attr:`__doc__` and :attr:`__name__` attributes are filled in;
41-
the caller is responsible for providing a :attr:`__file__` attribute.
42+
The module's :attr:`__name__`, :attr:`__doc__`, :attr:`__package__`, and
43+
:attr:`__loader__` attributes are filled in (all but :attr:`__name__` are set
44+
to ``None``); the caller is responsible for providing a :attr:`__file__`
45+
attribute.
4246
4347
.. versionadded:: 3.3
4448
49+
.. versionchanged:: 3.4
50+
:attr:`__package__` and :attr:`__loader__` are set to ``None``.
51+
4552
4653
.. c:function:: PyObject* PyModule_New(const char *name)
4754

Doc/library/importlib.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -827,7 +827,7 @@ an :term:`importer`.
827827
decorator as it subsumes this functionality.
828828

829829
.. versionchanged:: 3.4
830-
Set ``__loader__`` if set to ``None`` as well if the attribute does not
830+
Set ``__loader__`` if set to ``None``, as if the attribute does not
831831
exist.
832832

833833

Doc/reference/import.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -423,8 +423,8 @@ Here are the exact rules used:
423423
* If the module has a ``__file__`` attribute, this is used as part of the
424424
module's repr.
425425

426-
* If the module has no ``__file__`` but does have a ``__loader__``, then the
427-
loader's repr is used as part of the module's repr.
426+
* If the module has no ``__file__`` but does have a ``__loader__`` that is not
427+
``None``, then the loader's repr is used as part of the module's repr.
428428

429429
* Otherwise, just use the module's ``__name__`` in the repr.
430430

Doc/whatsnew/3.4.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,3 +231,8 @@ that may require changes to your code.
231231
:exc:`NotImplementedError` blindly. This will only affect code calling
232232
:func:`super` and falling through all the way to the ABCs. For compatibility,
233233
catch both :exc:`NotImplementedError` or the appropriate exception as needed.
234+
235+
* The module type now initializes the :attr:`__package__` and :attr:`__loader__`
236+
attributes to ``None`` by default. To determine if these attributes were set
237+
in a backwards-compatible fashion, use e.g.
238+
``getattr(module, '__loader__', None) is not None``.

Lib/ctypes/test/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ def requires(resource, msg=None):
3737

3838
def find_package_modules(package, mask):
3939
import fnmatch
40-
if (hasattr(package, "__loader__") and
40+
if (package.__loader__ is not None and
4141
hasattr(package.__loader__, '_files')):
4242
path = package.__name__.replace(".", os.path.sep)
4343
mask = os.path.join(path, mask)

Lib/doctest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ def _load_testfile(filename, package, module_relative, encoding):
215215
if module_relative:
216216
package = _normalize_module(package, 3)
217217
filename = _module_relative_path(package, filename)
218-
if hasattr(package, '__loader__'):
218+
if getattr(package, '__loader__', None) is not None:
219219
if hasattr(package.__loader__, 'get_data'):
220220
file_contents = package.__loader__.get_data(filename)
221221
file_contents = file_contents.decode(encoding)

Lib/importlib/_bootstrap.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1726,7 +1726,7 @@ def _setup(sys_module, _imp_module):
17261726
module_type = type(sys)
17271727
for name, module in sys.modules.items():
17281728
if isinstance(module, module_type):
1729-
if not hasattr(module, '__loader__'):
1729+
if getattr(module, '__loader__', None) is None:
17301730
if name in sys.builtin_module_names:
17311731
module.__loader__ = BuiltinImporter
17321732
elif _imp.is_frozen(name):

Lib/inspect.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -476,7 +476,7 @@ def getsourcefile(object):
476476
if os.path.exists(filename):
477477
return filename
478478
# only return a non-existent filename if the module has a PEP 302 loader
479-
if hasattr(getmodule(object, filename), '__loader__'):
479+
if getattr(getmodule(object, filename), '__loader__', None) is not None:
480480
return filename
481481
# or it is in the linecache
482482
if filename in linecache.cache:

Lib/test/test_descr.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2250,7 +2250,9 @@ class M(type(sys)):
22502250
minstance = M("m")
22512251
minstance.b = 2
22522252
minstance.a = 1
2253-
names = [x for x in dir(minstance) if x not in ["__name__", "__doc__"]]
2253+
default_attributes = ['__name__', '__doc__', '__package__',
2254+
'__loader__']
2255+
names = [x for x in dir(minstance) if x not in default_attributes]
22542256
self.assertEqual(names, ['a', 'b'])
22552257

22562258
class M2(M):

Lib/test/test_importlib/test_api.py

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -197,14 +197,12 @@ def test_everyone_has___loader__(self):
197197
# Issue #17098: all modules should have __loader__ defined.
198198
for name, module in sys.modules.items():
199199
if isinstance(module, types.ModuleType):
200-
if name in sys.builtin_module_names:
201-
self.assertIn(module.__loader__,
202-
(importlib.machinery.BuiltinImporter,
203-
importlib._bootstrap.BuiltinImporter))
204-
elif imp.is_frozen(name):
205-
self.assertIn(module.__loader__,
206-
(importlib.machinery.FrozenImporter,
207-
importlib._bootstrap.FrozenImporter))
200+
self.assertTrue(hasattr(module, '__loader__'),
201+
'{!r} lacks a __loader__ attribute'.format(name))
202+
if importlib.machinery.BuiltinImporter.find_module(name):
203+
self.assertIsNot(module.__loader__, None)
204+
elif importlib.machinery.FrozenImporter.find_module(name):
205+
self.assertIsNot(module.__loader__, None)
208206

209207

210208
if __name__ == '__main__':

0 commit comments

Comments
 (0)