T542 relative import
authorHaoyu Bai <baihaoyu@gmail.com>
Fri, 1 Apr 2011 08:05:31 +0000 (16:05 +0800)
committerRobert Bradshaw <robertwb@math.washington.edu>
Sat, 16 Apr 2011 07:36:29 +0000 (00:36 -0700)
Cython/Compiler/ExprNodes.py
Cython/Compiler/Parsing.py
runtests.py
tests/compile/fromimport.pyx
tests/compile/fromimport_star.pyx [new file with mode: 0644]
tests/run/importfrom.pyx
tests/run/relativeimport_T542.pyx [new file with mode: 0644]
tests/run/relativeimport_star_T542.pyx [new file with mode: 0644]

index c2baa8d15f26dedf0643ea5aa31db47abebfda9c..1a1c11e24606bc1e7b69bb642cb6dda7188ea5af 100755 (executable)
@@ -1735,14 +1735,20 @@ class BackquoteNode(ExprNode):
         code.put_gotref(self.py_result())
 
 
-
 class ImportNode(ExprNode):
     #  Used as part of import statement implementation.
     #  Implements result =
-    #    __import__(module_name, globals(), None, name_list)
+    #    __import__(module_name, globals(), None, name_list, level)
     #
-    #  module_name   StringNode            dotted name of module
+    #  module_name   StringNode            dotted name of module. Empty module
+    #                       name means importing the parent package accourding
+    #                       to level
     #  name_list     ListNode or None      list of names to be imported
+    #  level         int                   relative import level:
+    #                       -1: attempt both relative import and absolute import;
+    #                        0: absolute import;
+    #                       >0: the number of parent directories to search
+    #                           relative to the current module.
 
     type = py_object_type
 
@@ -1765,10 +1771,11 @@ class ImportNode(ExprNode):
         else:
             name_list_code = "0"
         code.putln(
-            "%s = __Pyx_Import(%s, %s); %s" % (
+            "%s = __Pyx_Import(%s, %s, %d); %s" % (
                 self.result(),
                 self.module_name.py_result(),
                 name_list_code,
+                self.level,
                 code.error_goto_if_null(self.result(), self.pos)))
         code.put_gotref(self.py_result())
 
@@ -7659,10 +7666,10 @@ static PyObject *__Pyx_GetName(PyObject *dict, PyObject *name) {
 
 import_utility_code = UtilityCode(
 proto = """
-static PyObject *__Pyx_Import(PyObject *name, PyObject *from_list); /*proto*/
+static PyObject *__Pyx_Import(PyObject *name, PyObject *from_list, long level); /*proto*/
 """,
 impl = """
-static PyObject *__Pyx_Import(PyObject *name, PyObject *from_list) {
+static PyObject *__Pyx_Import(PyObject *name, PyObject *from_list, long level) {
     PyObject *py_import = 0;
     PyObject *empty_list = 0;
     PyObject *module = 0;
@@ -7686,8 +7693,23 @@ static PyObject *__Pyx_Import(PyObject *name, PyObject *from_list) {
     empty_dict = PyDict_New();
     if (!empty_dict)
         goto bad;
+    #if PY_VERSION_HEX >= 0x02050000
+    {
+        PyObject *py_level = PyInt_FromLong(level);
+        if (!py_level)
+            goto bad;
+        module = PyObject_CallFunctionObjArgs(py_import,
+            name, global_dict, empty_dict, list, py_level, NULL);
+        Py_DECREF(py_level);
+    }
+    #else
+    if (level>0) {
+        PyErr_SetString(PyExc_RuntimeError, "Relative import is not supported for Python <=2.4.");
+        goto bad;
+    }
     module = PyObject_CallFunctionObjArgs(py_import,
         name, global_dict, empty_dict, list, NULL);
+    #endif
 bad:
     Py_XDECREF(empty_list);
     Py_XDECREF(py_import);
index a2d38fe8c79eedb07157afbcb3e8e53d6d4b1716..d72aeaa11fc53acff7f6b79ef54492624b465c01 100644 (file)
@@ -1189,6 +1189,8 @@ def p_raise_statement(s):
         return Nodes.ReraiseStatNode(pos)
 
 def p_import_statement(s):
+    # will do absolute import in Py3 and try both relative and absolute in Py2.
+    level = 0 if s.context.language_level >= 3 else -1
     # s.sy in ('import', 'cimport')
     pos = s.position()
     kind = s.sy
@@ -1216,6 +1218,7 @@ def p_import_statement(s):
                 rhs = ExprNodes.ImportNode(pos,
                     module_name = ExprNodes.IdentifierStringNode(
                         pos, value = dotted_name),
+                    level = level,
                     name_list = name_list))
         stats.append(stat)
     return Nodes.StatListNode(pos, stats = stats)
@@ -1224,13 +1227,30 @@ def p_from_import_statement(s, first_statement = 0):
     # s.sy == 'from'
     pos = s.position()
     s.next()
-    (dotted_name_pos, _, dotted_name, _) = \
-        p_dotted_name(s, as_allowed = 0)
+    if s.sy == '.':
+        # count relative import level
+        level = 0
+        while s.sy == '.':
+            level += 1
+            s.next()
+    else:
+        # will do absolute import in Py3 and try both relative and absolute in Py2.
+        level = 0 if s.context.language_level >= 3 else -1
+
+    if level > 0 and s.sy == 'cimport':
+        s.error("Relative cimport is not supported yet")
+    if level > 0 and s.sy == 'import':
+        # we are dealing with "from .. import foo, bar"
+        dotted_name_pos, dotted_name = s.position(), ''
+    else:
+        (dotted_name_pos, _, dotted_name, _) = \
+            p_dotted_name(s, as_allowed = 0)
     if s.sy in ('import', 'cimport'):
         kind = s.sy
         s.next()
     else:
         s.error("Expected 'import' or 'cimport'")
+
     is_cimport = kind == 'cimport'
     is_parenthesized = False
     if s.sy == '*':
@@ -1252,6 +1272,8 @@ def p_from_import_statement(s, first_statement = 0):
     if dotted_name == '__future__':
         if not first_statement:
             s.error("from __future__ imports must occur at the beginning of the file")
+        elif level is not None:
+            s.error("invalid syntax")
         else:
             for (name_pos, name, as_name, kind) in imported_names:
                 if name == "braces":
@@ -1285,6 +1307,7 @@ def p_from_import_statement(s, first_statement = 0):
         return Nodes.FromImportStatNode(pos,
             module = ExprNodes.ImportNode(dotted_name_pos,
                 module_name = ExprNodes.IdentifierStringNode(pos, value = dotted_name),
+                level = level,
                 name_list = import_list),
             items = items)
 
index 8b3ffa1d5118cea455c52aa181e067f690683baf..c68958f6eb707413d68f681df2ad5b2b98518fc7 100644 (file)
@@ -93,6 +93,7 @@ VER_DEP_MODULES = {
                                           ]),
     (2,5) : (operator.lt, lambda x: x in ['run.any',
                                           'run.all',
+                                          'run.relativeimport_T542',
                                           ]),
     (2,6) : (operator.lt, lambda x: x in ['run.print_function',
                                           'run.cython3',
index 142999deede70d1603078f2ed97c005c8f07ec3e..46f7b54420534e79275ee355d31c05ef54864180 100644 (file)
@@ -4,4 +4,12 @@ def f():
     from spam import eggs
     from spam.morespam import bacon, eggs, ham
     from spam import eggs as ova
-
+    from . import spam
+    from ... import spam
+    from .. import spam, foo
+    from ... import spam, foobar
+    from .spam import foo
+    from ...spam import foo, bar
+    from ...spam.foo import bar
+    from ...spam.foo import foo, bar
+    from ...spam.foo import (foo, bar)
diff --git a/tests/compile/fromimport_star.pyx b/tests/compile/fromimport_star.pyx
new file mode 100644 (file)
index 0000000..b1daad9
--- /dev/null
@@ -0,0 +1,4 @@
+from spam import *
+from ...spam.foo import *
+from . import *
+from ... import *
index 93d4e5eb0ac2a661828cfcc8d574a6e44a7a419d..52bc676113de36bc893af3fbd76202618d68f26a 100644 (file)
@@ -61,16 +61,16 @@ def typed_imports():
     cdef type t
 
     from sys import maxunicode
-    print maxunicode == sys.maxunicode
+    print(maxunicode == sys.maxunicode)
     from types import ModuleType as t
-    print t is types.ModuleType
+    print(t is types.ModuleType)
 
     try:
         from sys import version_info as maxunicode
     except TypeError, e:
-        print e
+        print(e)
 
     try:
         from sys import maxunicode as t
     except TypeError, e:
-        print e
+        print(e)
diff --git a/tests/run/relativeimport_T542.pyx b/tests/run/relativeimport_T542.pyx
new file mode 100644 (file)
index 0000000..2994cc4
--- /dev/null
@@ -0,0 +1,29 @@
+# cython: language_level=3
+__name__='distutils.baregg' # fool Python we are in distutils
+from distutils import cmd, core, version
+
+from .core import *
+def test_relative():
+    """
+    >>> test_relative() == (cmd, core, 'distutils.version')
+    True
+    """
+    from . import cmd, core
+    from . import (version, core)
+    from .version import __name__
+    return cmd, core, __name__
+
+def test_absolute():
+    """
+    >>> test_absolute()
+    Traceback (most recent call last):
+    ...
+    ImportError: No module named debug
+    """
+    import debug
+    return
+
+__doc__ = """
+>>> setup == core.setup
+True
+"""
diff --git a/tests/run/relativeimport_star_T542.pyx b/tests/run/relativeimport_star_T542.pyx
new file mode 100644 (file)
index 0000000..39ceeaa
--- /dev/null
@@ -0,0 +1,8 @@
+from distutils import core, version
+__package__ = 'distutils.core' # fool Python we are in distutils
+from . import *
+
+__doc__ = """
+>>> core.setup == setup
+True
+"""