[Jython-checkins] jython: Fix #2347, sensitivity to actual package in PEP 328 tests.

jeff.allen jython-checkins at python.org
Mon May 4 23:24:14 CEST 2015


https://hg.python.org/jython/rev/22368d55fbce
changeset:   7701:22368d55fbce
user:        Jeff Allen <ja.py at farowl.co.uk>
date:        Sat May 02 23:32:52 2015 +0100
summary:
  Fix #2347, sensitivity to actual package in PEP 328 tests.

The function TestImportFunction.top() is rewritten to return a dict
that will serve as a top-level module, irrespective of the actual
parentage of the test. Comments and optional diagnostic commentary have
been added, because module import is tricky to understand.

files:
  Lib/test/test_import_pep328.py |  169 ++++++++++++++------
  NEWS                           |    3 +
  2 files changed, 123 insertions(+), 49 deletions(-)


diff --git a/Lib/test/test_import_pep328.py b/Lib/test/test_import_pep328.py
--- a/Lib/test/test_import_pep328.py
+++ b/Lib/test/test_import_pep328.py
@@ -33,6 +33,16 @@
 import sys
 import types
 
+EXPLAIN = False # If True, produce commentary in TestImportFunction
+
+def dump_module(m):
+    "Print values of attributes relevant to import mechanism"
+    if isinstance(m, types.ModuleType):
+        m = m.__dict__
+    print "  Module name:     {}".format(m.get('__name__', ''))
+    for n in ['__package__', '__path__']:
+        print "    {:12s} = {}".format(n, m.get(n,''))
+
 origImport = __import__ 
 
 class TestImportStatementError(exceptions.ImportError):
@@ -179,45 +189,53 @@
         self.assertEqual(a.level, 0)
 
 
-class TestImportFunctionError(exceptions.ImportError):
+class TestImportFunctionSuccess(exceptions.ImportError):
     pass
 
 class TestImportFunction(unittest.TestCase):
     """Test the '__import__' function
 
-    This class tests, how the '__import__'-function
-    resolves module names. It uses the 'meta_path' hook,
-    to intercept the actual module loading. 
-
-    Module Structure:
+    This class tests, how the '__import__'-function resolves module names.
+    It uses the 'meta_path' hook, to intercept the actual module loading.
+    When consulted through find_module, it claims to have access to the
+    following module structure:
 
         Top
-          \---- X           package
-          |     \-- Y       package
-          |     |   \-- Z1  module
-          |     |   \-- Z2  module
-          |     \-- Y2      package
-          \---- X2          module
+          +---- X           package
+          |     +-- Y       package
+          |     |   +-- Z1  module
+          |     |   +-- Z2  module
+          |     +-- Y2      package
+          +---- X2          module
 
     """
 
     nameX = "TestImportFunctionX"
 
     def setUp(self):
-        self.modX = imp.new_module(self.nameX)
-        self.modX.__path__ = ['X']
-
-        self.modX2 = imp.new_module(self.nameX + "2")
-        self.modY = imp.new_module(self.nameX + ".Y")
-        self.modY.__path__ = ['X/Y']
-        self.modY2 = imp.new_module(self.nameX + ".Y2")
-        self.modY2.__path__ = ['X/Y']
-        self.modZ1 = imp.new_module(self.nameX + ".Y.Z1")
-        self.modZ2 = imp.new_module(self.nameX + ".Y.Z2")
-
+        self.modX = self._new_module(None, self.nameX, True)
+        self.modY = self._new_module(self.modX, "Y", True)
+        self.modZ1 = self._new_module(self.modY, "Z1")
+        self.modZ2 = self._new_module(self.modY, "Z2")
+        self.modY2 = self._new_module(self.modX, "Y2", True)
+        self.modX2 = self._new_module(None, self.nameX + "2")
         self.expected = "something_completely_different"
         sys.meta_path.insert(0, self)
 
+    @staticmethod
+    def _new_module(in_package, name, is_package=False):
+        if not in_package:
+            m = imp.new_module(name)
+        else:
+            m = imp.new_module(in_package.__name__ + '.' + name)
+            if is_package:
+                m.__package__ = m.__name__ # surprisingly not the parent name
+            else:
+                m.__package__ = in_package.__name__
+        if is_package:
+            m.__path__ = [m.__name__.replace('.', '/')]
+        return m
+
     def tearDown(self):
         try:
             sys.meta_path.remove(self)
@@ -248,164 +266,217 @@
         sys.modules[self.modZ1.__name__] = self.modZ1
         self.modY.Z1 = self.modZ1
 
-    def top(self):
-        if sys.modules.has_key("__main__"):
-            return sys.modules["__main__"].__dict__
-        return globals()
+    @staticmethod
+    def top():
+        "Return the __dict__ of a non-package, top-level module"
+        # When this program runs as python -m test.test_import_pep328, it is
+        # called __main__, but is inside package test, so we must fake it.
+        myName = TestImportFunction.nameX[:-1] + "Top"
+        return {'__name__': myName, '__package__': None, '__file__': None}
 
     def find_module(self, fullname, path=None):
+        # Simulate the operation of a module finder object on the sys.meta_path
+        if EXPLAIN:
+            print "find_module:"
+            print "    fullname   =", fullname
+            print "    path       =", path
         if self.expected and self.expected != fullname:
+            # Equivalent of "import name" was called and the import mechanism is
+            # trying something other than the expected full name. For example, X
+            # called "import X2", and something other than X2 is tried (first).
             return None
         self.fullname = fullname
         self.path = path
         return self
 
     def load_module(self, fullname):
+        # Masquerade as the loader matching fullname
         self.assertEqual(fullname, self.fullname)
-        raise TestImportFunctionError()
+        # Signal success, disguised as an ImportError
+        raise TestImportFunctionSuccess()
 
     def runImport(self, expected, name, globals, fromlist=None, level=None):
         self.expected = expected
         if isinstance(globals, types.ModuleType):
             globals = globals.__dict__
+        if EXPLAIN:
+            print "\nrunImport:"
+            dotname = ('.'*level if level>0 else '') + name
+            callername = globals['__name__']
+            callerpkg = globals.get('__package__', None)
+            if fromlist:
+                print "    from {} import {} # in {} in package {}".format(
+                            dotname, fromlist, callername, callerpkg)
+            else:
+                print "    import {} # in {} in package {}".format(
+                            dotname, callername, callerpkg)
         try:
             if level is not None:
                 __import__(name, globals, None, fromlist, level)
             else:
                 __import__(name, globals, None, fromlist)
-        except TestImportFunctionError:
+        except TestImportFunctionSuccess:
             return
-        self.fail("Expected a TestImportFunctionError")
+        self.fail("Expected a TestImportFunctionSuccess")
 
     def testRelativeOrAbsolute_top_X2_1(self):
+        # In context of a non-package, top-level module, find X2.
+        # The finder should only be consulted with the absolute name.
         self.runImport(None, self.modX2.__name__, self.top())
         self.assertEqual(self.fullname, self.modX2.__name__)
         self.assertEqual(self.path, None)
 
     def testRelativeOrAbsolute_top_X2_2(self):
+        # In context of a non-package, top-level module, find X2.
+        # The finder should only be consulted with the absolute name.
         self.runImport(None, self.modX2.__name__, self.top(), None, -1)
         self.assertEqual(self.fullname, self.modX2.__name__)
         self.assertEqual(self.path, None)
 
     def testRelativeOrAbsolute_top_Y_1(self):
+        # In context of a non-package, top-level module, find X.Y.
+        # The finder should only be consulted with the absolute name.
         self.importX()
         self.runImport(None, self.modY.__name__, self.top())
         self.assertEqual(self.fullname, self.modY.__name__)
-        self.assertEqual(self.path, ['X'])
+        self.assertEqual(self.path, [self.nameX])
 
     def testRelativeOrAbsolute_top_Y_2(self):
+        # In context of a non-package, top-level module, find X.Y.
+        # The finder should only be consulted with the absolute name.
         self.importX()
         self.runImport(None, self.modY.__name__, self.top(), None, -1)
         self.assertEqual(self.fullname, self.modY.__name__)
-        self.assertEqual(self.path, ['X'])
+        self.assertEqual(self.path, [self.nameX])
 
     def testAbsolute_top_X2(self):
+        # In context of a non-package, top-level module, find X2 absolutely.
         self.runImport(None, self.modX2.__name__, globals(), None, 0)
         self.assertEqual(self.fullname, self.modX2.__name__)
         self.assertEqual(self.path, None)
 
     def testAbsolute_top_Y(self):
+        # In context of a non-package, top-level module, find X.Y absolutely.
         self.importX()
         self.runImport(None, self.modY.__name__, globals(), None, 0)
         self.assertEqual(self.fullname, self.modY.__name__)
-        self.assertEqual(self.path, ['X'])
+        self.assertEqual(self.path, [self.nameX])
 
     # Relative case
     def testRelativeOrAbsolute_X_X2_rel1(self):
+        # In context of package X, look for X2 at X.X2 (where actually it isn't).
         self.importX()
         self.runImport(None, self.modX2.__name__, self.modX)
         self.assertEqual(self.fullname, self.nameX + "." + self.modX2.__name__)
-        self.assertEqual(self.path, ['X'])
+        self.assertEqual(self.path, [self.nameX])
 
     def testRelativeOrAbsolute_X_X2_rel2(self):
+        # In context of package X, look for X2 at X.X2 (where actually it isn't).
         self.importX()
         self.runImport(None, self.modX2.__name__, self.modX, None, -1)
-        self.assertEqual(self.path, ['X'])
+        self.assertEqual(self.path, [self.nameX])
         self.assertEqual(self.fullname, self.nameX + "." + self.modX2.__name__)
 
     # Absolute case
     def testRelativeOrAbsolute_X_X2_abs1(self):
+        # In context of package X, find X2 at absolute X2 (on second attempt).
         self.importX()
         self.runImport(self.modX2.__name__, self.modX2.__name__, self.modX)
         self.assertEqual(self.fullname, self.modX2.__name__)
         self.assertEqual(self.path, None)
 
     def testRelativeOrAbsolute_X_X2_abs2(self):
+        # In context of package X, find X2 at absolute X2 (on second attempt).
         self.importX()
         self.runImport(self.modX2.__name__, self.modX2.__name__, self.modX, None, -1)
         self.assertEqual(self.path, None)
         self.assertEqual(self.fullname, self.modX2.__name__)
 
     def testAbsolute_X_X2(self):
+        # In context of package X, find X2 at explicitly absolute X2.
         self.importX()
         self.runImport(None, self.modX2.__name__, self.modX, None, 0)
         self.assertEqual(self.fullname, self.modX2.__name__)
         self.assertEqual(self.path, None)
 
     def testAbsolute_X_Y(self):
+        # In context of package X, find Y at explicitly absolute X.Y.
         self.importX()
         self.runImport(None, self.modY.__name__, self.modX, None, 0)
         self.assertEqual(self.fullname, self.modY.__name__)
-        self.assertEqual(self.path, ['X'])
+        self.assertEqual(self.path, [self.nameX])
 
     def testRelative_Z1_Z2(self):
+        # In context of module Z1, from . import Z2.
         self.importZ1()
         self.runImport(None, "", self.modZ1, ['Z2'], 1)
         self.assertEqual(self.fullname, self.modZ2.__name__)
-        self.assertEqual(self.path, ['X/Y'])
+        self.assertEqual(self.path, [self.nameX + '/Y'])
 
     def testRelative_Z1_Y2(self):
+        # In context of module Z1, from .. import Y2.
         self.importZ1()
         self.runImport(None, "", self.modZ1, ["Y2"], 2)
         self.assertEqual(self.fullname, self.modX.__name__ + ".Y2")
-        self.assertEqual(self.path, ['X'])
+        self.assertEqual(self.path, [self.nameX])
 
     def testRelative_Z1_X2(self):
-        # """beyond top level"""
+        # In context of module Z1, from ... import X2 (incorrectly beyond top level).
         self.importZ1()
-        self.assertRaises(ValueError, self.runImport, None, "", self.modZ1, [self.modX2.__name__], 3)
+        with self.assertRaises(ValueError):
+            self.runImport(None, "", self.modZ1, [self.modX2.__name__], 3)
 
     def testRelative_X2_X(self):
-        # """not a package"""
+        # In context of module X2, from . import X (incorrectly)
+        # This is incorrect as X2 is not in a package (is a top-level module).
         self.importX2()
-        self.assertRaises(ValueError, self.runImport, None, "", self.modX2, [self.modX.__name__], 1)
+        with self.assertRaises(ValueError):
+            self.runImport(None, "", self.modX2, [self.modX.__name__], 1)
 
     def testRelative_X2_Y(self):
-        # """not a package"""
+        # In context of module X2, from .X import Y (incorrectly).
+        # This is incorrect as X2 is not in a package (is a top-level module).
         self.importX2()
         self.importX()
-        self.assertRaises(ValueError, self.runImport, None, self.modX.__name__, self.modX2, ["Y"], 1)
+        with self.assertRaises(ValueError):
+            self.runImport(None, self.modX.__name__, self.modX2, ["Y"], 1)
 
     def testRelative_X_Z1_1(self):
+        # In context of package X, from .Y import Z1.
         self.importX()
         self.runImport(None, "Y", self.modX, ['Z1'], 1)
         self.assertEqual(self.fullname, self.modY.__name__)
-        self.assertEqual(self.path, ['X'])
+        self.assertEqual(self.path, [self.nameX])
 
     def testRelative_X_Z1_2(self):
+        # In context of package X, from .Y import Z1.
         self.importY()
         self.runImport(None, "Y", self.modX, ['Z1'], 1)
         self.assertEqual(self.fullname, self.modZ1.__name__)
-        self.assertEqual(self.path, ['X/Y'])
+        self.assertEqual(self.path, [self.nameX + '/Y'])
 
     def testRelative_Y_Z1(self):
+        # In context of package Y: from .Z1 import A, B.
         self.importY()
         self.runImport(None, "Z1", self.modY, ['A', 'B'], 1)
         self.assertEqual(self.fullname, self.modZ1.__name__)
-        self.assertEqual(self.path, ['X/Y'])
+        self.assertEqual(self.path, [self.nameX + '/Y'])
 
     def testRelative_Y2_Z1_1(self):
+        # In context of package Y2, from ..Y import Z1.
         self.importY2()
         self.runImport(None, "Y", self.modY2, ['Z1'], 2)
         self.assertEqual(self.fullname, self.modY.__name__)
-        self.assertEqual(self.path, ['X'])
+        self.assertEqual(self.path, [self.nameX])
 
     def testRelative_Y2_Z1_2(self):
+        # In context of package Y2, from ..Y import Z1.
         self.importY2()
         self.importY()
         self.runImport(None, "Y", self.modY2, ['Z1'], 2)
         self.assertEqual(self.fullname, self.modZ1.__name__)
-        self.assertEqual(self.path, ['X/Y'])
+        self.assertEqual(self.path, [self.nameX + '/Y'])
+
 
 try:
     from test import test_support
diff --git a/NEWS b/NEWS
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,9 @@
 
 For more details, please see https://hg.python.org/jython
 
+  Bugs fixed
+   - [ 2347 ] failures in test_import_pep328 when run with -m
+
 Jython 2.7rc3
   Bugs fixed
    - [ 2326 ] Java's weakly consistent iteration of ConcurrentMap is compatible with mutation

-- 
Repository URL: https://hg.python.org/jython


More information about the Jython-checkins mailing list