Commit de4ebfe5 authored by Brett Cannon's avatar Brett Cannon

When the globals argument to importlib.__import__() contained any value for

__package__, it was used. This was incorrect since it could be set to None to
represent the fact that a proper value was unknown. Now None will trigger the
calculation for __package__.

Discovered when running importlib against test_importhooks.
parent ce7d4cbc
...@@ -922,10 +922,10 @@ def __import__(name, globals={}, locals={}, fromlist=[], level=0): ...@@ -922,10 +922,10 @@ def __import__(name, globals={}, locals={}, fromlist=[], level=0):
if level == 0: if level == 0:
module = _gcd_import(name) module = _gcd_import(name)
else: else:
# __package__ is not guaranteed to be defined. # __package__ is not guaranteed to be defined or could be set to None
try: # to represent that it's proper value is unknown
package = globals['__package__'] package = globals.get('__package__')
except KeyError: if package is None:
package = globals['__name__'] package = globals['__name__']
if '__path__' not in globals: if '__path__' not in globals:
package = package.rpartition('.')[0] package = package.rpartition('.')[0]
......
...@@ -19,8 +19,9 @@ class Using__package__(unittest.TestCase): ...@@ -19,8 +19,9 @@ class Using__package__(unittest.TestCase):
base = package.rsplit('.', level)[0] base = package.rsplit('.', level)[0]
return '{0}.{1}'.format(base, name) return '{0}.{1}'.format(base, name)
But since there is no guarantee that __package__ has been set, there has to But since there is no guarantee that __package__ has been set (or not been
be a way to calculate the attribute's value [__name__]:: set to None [None]), there has to be a way to calculate the attribute's value
[__name__]::
def calc_package(caller_name, has___path__): def calc_package(caller_name, has___path__):
if has__path__: if has__path__:
...@@ -43,17 +44,22 @@ class Using__package__(unittest.TestCase): ...@@ -43,17 +44,22 @@ class Using__package__(unittest.TestCase):
fromlist=['attr'], level=2) fromlist=['attr'], level=2)
self.assertEquals(module.__name__, 'pkg') self.assertEquals(module.__name__, 'pkg')
def test_using___name__(self): def test_using___name__(self, package_as_None=False):
# [__name__] # [__name__]
globals_ = {'__name__': 'pkg.fake', '__path__': []}
if package_as_None:
globals_['__package__'] = None
with util.mock_modules('pkg.__init__', 'pkg.fake') as importer: with util.mock_modules('pkg.__init__', 'pkg.fake') as importer:
with util.import_state(meta_path=[importer]): with util.import_state(meta_path=[importer]):
import_util.import_('pkg.fake') import_util.import_('pkg.fake')
module = import_util.import_('', module = import_util.import_('', globals= globals_,
globals={'__name__': 'pkg.fake',
'__path__': []},
fromlist=['attr'], level=2) fromlist=['attr'], level=2)
self.assertEquals(module.__name__, 'pkg') self.assertEquals(module.__name__, 'pkg')
def test_None_as___package__(self):
# [None]
self.test_using___name__(package_as_None=True)
def test_bad__package__(self): def test_bad__package__(self):
globals = {'__package__': '<not real>'} globals = {'__package__': '<not real>'}
with self.assertRaises(SystemError): with self.assertRaises(SystemError):
......
...@@ -68,6 +68,9 @@ C-API ...@@ -68,6 +68,9 @@ C-API
Library Library
------- -------
- When the globals past to importlib.__import__() has __package__ set to None,
fall back to computing what __package__ should be instead of giving up.
- Raise a TypeError when the name of a module to be imported for - Raise a TypeError when the name of a module to be imported for
importlib.__import__ is not a string (was raising an importlib.__import__ is not a string (was raising an
AttributeError before). AttributeError before).
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment