[Jython-checkins] jython: Correct level in relative * imports. Fixes #2158 and #2259.

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


https://hg.python.org/jython/rev/9483b3252bd5
changeset:   7702:9483b3252bd5
user:        Jeff Allen <ja.py at farowl.co.uk>
date:        Sun May 03 21:24:54 2015 +0100
summary:
  Correct level in relative * imports. Fixes #2158 and #2259.

A test is added for "from ...X.Y import *" and failing code is
corrected in the code-generation phase of compilation, so that the
proper value of "level" is obtained. Code is simplified (and should
compile to nothing when imp.DEFAULT_LEVEL == 0 in 3.x).

files:
  Lib/test/test_import_pep328.py            |  75 ++++++----
  NEWS                                      |   1 +
  src/org/python/compiler/CodeCompiler.java |  26 +--
  3 files changed, 58 insertions(+), 44 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
@@ -34,6 +34,7 @@
 import types
 
 EXPLAIN = False # If True, produce commentary in TestImportFunction
+MAXDOTS = 10 # adjust enthusiasm of dotted tests
 
 def dump_module(m):
     "Print values of attributes relevant to import mechanism"
@@ -45,13 +46,19 @@
 
 origImport = __import__ 
 
-class TestImportStatementError(exceptions.ImportError):
+class TestImportStatementTell(exceptions.ImportError):
+    # Raised by TestImportStatement.importFunction to tell us how it was called
     def __init__(self, args):
+        # Smuggle the arguments of importFunction() through the call stack
+        if EXPLAIN: print "\nimport:"
         names = ['name', 'globals', 'locals', 'fromlist', 'level']
         self.len = len(args)
         for a in args:
             n = names.pop(0)
             setattr(self, n, a)
+            if EXPLAIN:
+                too_long = not isinstance(a, (int, tuple, str, unicode))
+                print "    {:12s}= {}".format(n, a if not too_long else type(a))
         for n in names:
             setattr(self, n, None)
 
@@ -69,7 +76,7 @@
     def importFunction(*args):
         if args[0] == '__future__':
             return origImport(*args)
-        raise TestImportStatementError(args)
+        raise TestImportStatementTell(args)
     importFunction = staticmethod(importFunction)
 
     def setUp(self):
@@ -83,73 +90,79 @@
         g = {}
         try:
             exec statement in g, l 
-        except TestImportStatementError, e:
+        except TestImportStatementTell, e:
             self.assert_(e.globals is g, "globals is changed")
             self.assert_(e.locals is l, "locals is changed")
             return e
-        self.fail("Expected a TestImportStatementError")
+        self.fail("Expected a TestImportStatementTell")
+
+    @staticmethod
+    def dotrange(n=MAXDOTS):
+        "Return a longer sequence of dots in each iteration"
+        for i in range(1, n):
+            yield '.'*i
 
     def testFromDotsOnly(self):
-        dots = ''
-        for i in range(1, 10):
-            dots += '.'
+        for dots in self.dotrange():
             a = self.runImport("from %s import (A,B)" % (dots,))
             self.assertEqual(a.len, 5)
             self.assertEqual(a.name, "")
-            self.assertEqual(a.level, i)
+            self.assertEqual(a.level, len(dots))
             self.assertEqual(a.fromlist, ('A', 'B'))
 
     def testFromDotsOnlyAs(self):
-        dots = ''
-        for i in range(1, 10):
-            dots += '.'
+        for dots in self.dotrange():
             a = self.runImport("from %s import A as B" % (dots,))
             self.assertEqual(a.len, 5)
             self.assertEqual(a.name, "")
             self.assertEqual(a.fromlist, ('A',))
-            self.assertEqual(a.level, i)
+            self.assertEqual(a.level, len(dots))
 
     def testFromDotsAndName(self):
-        dots = ''
-        for i in range(1, 10):
-            dots += '.'
+        for dots in self.dotrange():
             a = self.runImport("from %sX import A" % (dots,))
             self.assertEqual(a.len, 5)
             self.assertEqual(a.name, "X")
             self.assertEqual(a.fromlist, ('A',))
-            self.assertEqual(a.level, i)
+            self.assertEqual(a.level, len(dots))
 
-    def testFromDotsAndDotedName(self):
-        dots = ''
-        for i in range(1, 10):
-            dots += '.'
+    def testFromDotsAndDottedName(self):
+        for dots in self.dotrange():
             a = self.runImport("from %sX.Y import A" % (dots,))
             self.assertEqual(a.len, 5)
             self.assertEqual(a.name, "X.Y")
             self.assertEqual(a.fromlist, ('A',))
-            self.assertEqual(a.level, i)
+            self.assertEqual(a.level, len(dots))
 
-    def testAbsoluteFromDotedNameAs(self):
+    def testFromDotsAndDottedNameAll(self):
+        for dots in self.dotrange():
+            a = self.runImport("from %sX.Y import *" % (dots,))
+            self.assertEqual(a.len, 5, "level argument elided") # Issue 2158
+            self.assertEqual(a.name, "X.Y")
+            self.assertEqual(a.fromlist, ('*',))
+            self.assertEqual(a.level, len(dots))
+
+    def testAbsoluteFromDottedNameAs(self):
         a = self.runImport(self.AI + "from X.Y import A as B")
         self.assertEqual(a.len, 5)
         self.assertEqual(a.name, "X.Y")
         self.assertEqual(a.fromlist, ('A',))
         self.assertEqual(a.level, 0)
 
-    def testRelativeOrAbsoluteFromDotedNameAs(self):
+    def testRelativeOrAbsoluteFromDottedNameAs(self):
         a = self.runImport("from X.Y import A as B")
         self.assertEqual(a.name, "X.Y")
         self.assertEqual(a.fromlist, ('A',))
         self.assertEqual(a.len, 4)
 
-    def testAbsoluteFromDotedNameAll(self):
+    def testAbsoluteFromDottedNameAll(self):
         a = self.runImport(self.AI + "from X.Y import *")
         self.assertEqual(a.len, 5)
         self.assertEqual(a.name, "X.Y")
         self.assertEqual(a.fromlist, ('*',))
         self.assertEqual(a.level, 0)
 
-    def testRelativeOrAbsoluteFromDotedNameAll(self):
+    def testRelativeOrAbsoluteFromDottedNameAll(self):
         a = self.runImport("from X.Y import *")
         self.assertEqual(a.name, "X.Y")
         self.assertEqual(a.fromlist, ('*',))
@@ -162,7 +175,7 @@
         self.assertEqual(a.fromlist, None)
         self.assertEqual(a.level, 0)
 
-    def testAbsoluteImportDotedName(self):
+    def testAbsoluteImportDottedName(self):
         a = self.runImport(self.AI + "import X.Y")
         self.assertEqual(a.len, 5)
         self.assertEqual(a.name, "X.Y")
@@ -175,13 +188,13 @@
         self.assertEqual(a.fromlist, None)
         self.assertEqual(a.len, 4)
 
-    def testRelativeOrAbsoluteImportDotedName(self):
+    def testRelativeOrAbsoluteImportDottedName(self):
         a = self.runImport("import X.Y")
         self.assertEqual(a.name, "X.Y")
         self.assertEqual(a.fromlist, None)
         self.assertEqual(a.len, 4)
 
-    def testAbsoluteImportDotedNameAs(self):
+    def testAbsoluteImportDottedNameAs(self):
         a = self.runImport(self.AI + "import X.Y as Z")
         self.assertEqual(a.len, 5)
         self.assertEqual(a.name, "X.Y")
@@ -484,8 +497,10 @@
     test_main = unittest.main
 else:
     def test_main():
-        test_support.run_unittest(TestImportStatement,
-                              TestImportFunction)
+        test_support.run_unittest(
+                TestImportStatement,
+                TestImportFunction,
+                )
 
 if __name__ == '__main__':
     test_main()
diff --git a/NEWS b/NEWS
--- a/NEWS
+++ b/NEWS
@@ -4,6 +4,7 @@
 
   Bugs fixed
    - [ 2347 ] failures in test_import_pep328 when run with -m
+   - [ 2158, 2259 ] Fixed behaviour of relative from ... import *
 
 Jython 2.7rc3
   Bugs fixed
diff --git a/src/org/python/compiler/CodeCompiler.java b/src/org/python/compiler/CodeCompiler.java
--- a/src/org/python/compiler/CodeCompiler.java
+++ b/src/org/python/compiler/CodeCompiler.java
@@ -921,14 +921,16 @@
     }
 
     /**
-     * Push the import level <code>0</code> or <code>-1</code>.
+     * Return the implied import level, which is different from the argument only if the argument is
+     * zero (no leading dots) meaning try relative then absolute (in Python 2), signified by
+     * returning level <code>-1</code>.
      */
-    private void defaultImportLevel() {
+    private int impliedImportLevel(int level) {
         // already prepared for a future change of DEFAULT_LEVEL
-        if (module.getFutures().isAbsoluteImportOn() || imp.DEFAULT_LEVEL == 0) {
-            code.iconst_0();
+        if (imp.DEFAULT_LEVEL == 0 || level != 0 || module.getFutures().isAbsoluteImportOn()) {
+            return level;
         } else {
-            code.iconst_m1();
+            return imp.DEFAULT_LEVEL;
         }
     }
 
@@ -942,7 +944,7 @@
                 asname = a.getInternalAsname();
                 code.ldc(name);
                 loadFrame();
-                defaultImportLevel();
+                code.iconst(impliedImportLevel(0));
                 code.invokestatic(p(imp.class), "importOneAs",
                         sig(PyObject.class, String.class, PyFrame.class, Integer.TYPE));
             } else {
@@ -953,7 +955,7 @@
                 }
                 code.ldc(name);
                 loadFrame();
-                defaultImportLevel();
+                code.iconst(impliedImportLevel(0));
                 code.invokestatic(p(imp.class), "importOne",
                         sig(PyObject.class, String.class, PyFrame.class, Integer.TYPE));
             }
@@ -986,9 +988,10 @@
             }
 
             loadFrame();
-            defaultImportLevel();
+            code.iconst(impliedImportLevel(node.getInternalLevel()));
             code.invokestatic(p(imp.class), "importAll",
                     sig(Void.TYPE, String.class, PyFrame.class, Integer.TYPE));
+
         } else {
             java.util.List<String> fromNames = new ArrayList<String>(); // [names.size()];
             java.util.List<String> asnames = new ArrayList<String>(); // [names.size()];
@@ -1004,12 +1007,7 @@
             code.freeLocal(strArray);
 
             loadFrame();
-
-            if (node.getInternalLevel() == 0) {
-                defaultImportLevel();
-            } else {
-                code.iconst(node.getInternalLevel());
-            }
+            code.iconst(impliedImportLevel(node.getInternalLevel()));
             code.invokestatic(
                     p(imp.class),
                     "importFrom",

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


More information about the Jython-checkins mailing list