Skip to content

Commit 0c89bf0

Browse files
author
thomas.wouters
committed
SF patch #1438387, PEP 328: relative and absolute imports. - IMPORT_NAME takes an extra argument from the stack: the relativeness of the import. Only passed to __import__ when it's not -1. - __import__() takes an optional 5th argument for the same thing; it __defaults to -1 (old semantics: try relative, then absolute) - 'from . import name' imports name (be it module or regular attribute) from the current module's *package*. Likewise, 'from .module import name' will import name from a sibling to the current module. - Importing from outside a package is not allowed; 'from . import sys' in a toplevel module will not work, nor will 'from .. import sys' in a (single-level) package. - 'from __future__ import absolute_import' will turn on the new semantics for import and from-import: imports will be absolute, except for from-import with dots. Includes tests for regular imports and importhooks, parser changes and a NEWS item, but no compiler-package changes or documentation changes. git-svn-id: http://svn.python.org/projects/python/trunk@42649 6015fed2-1504-0410-9fe1-9d1591cc4771
1 parent 84c6540 commit 0c89bf0

20 files changed

Lines changed: 271 additions & 74 deletions

Grammar/Grammar

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ yield_stmt: yield_expr
5959
raise_stmt: 'raise' [test [',' test [',' test]]]
6060
import_stmt: import_name | import_from
6161
import_name: 'import' dotted_as_names
62-
import_from: ('from' ('.')* dotted_name
62+
import_from: ('from' ('.'* dotted_name | '.')
6363
'import' ('*' | '(' import_as_names ')' | import_as_names))
6464
import_as_name: NAME [NAME NAME]
6565
dotted_as_name: dotted_name [NAME NAME]

Include/Python-ast.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ struct _stmt {
159159
struct {
160160
identifier module;
161161
asdl_seq *names;
162+
int level;
162163
} ImportFrom;
163164

164165
struct {
@@ -371,8 +372,8 @@ stmt_ty TryFinally(asdl_seq * body, asdl_seq * finalbody, int lineno, PyArena
371372
*arena);
372373
stmt_ty Assert(expr_ty test, expr_ty msg, int lineno, PyArena *arena);
373374
stmt_ty Import(asdl_seq * names, int lineno, PyArena *arena);
374-
stmt_ty ImportFrom(identifier module, asdl_seq * names, int lineno, PyArena
375-
*arena);
375+
stmt_ty ImportFrom(identifier module, asdl_seq * names, int level, int lineno,
376+
PyArena *arena);
376377
stmt_ty Exec(expr_ty body, expr_ty globals, expr_ty locals, int lineno, PyArena
377378
*arena);
378379
stmt_ty Global(asdl_seq * names, int lineno, PyArena *arena);

Include/code.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ typedef struct {
4545
#define CO_GENERATOR_ALLOWED 0x1000
4646
#endif
4747
#define CO_FUTURE_DIVISION 0x2000
48+
#define CO_FUTURE_ABSIMPORT 0x4000 /* absolute import by default */
4849

4950
#define CO_MAXBLOCKS 20 /* Max static block nesting within a function */
5051

Include/compile.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ typedef struct {
2222
#define FUTURE_NESTED_SCOPES "nested_scopes"
2323
#define FUTURE_GENERATORS "generators"
2424
#define FUTURE_DIVISION "division"
25+
#define FUTURE_ABSIMPORT "absolute_import"
2526

2627
struct _mod; /* Declare the existence of this type */
2728
PyAPI_FUNC(PyCodeObject *) PyAST_Compile(struct _mod *, const char *,

Include/import.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,16 @@ PyAPI_FUNC(PyObject *) PyImport_ExecCodeModuleEx(
1414
PyAPI_FUNC(PyObject *) PyImport_GetModuleDict(void);
1515
PyAPI_FUNC(PyObject *) PyImport_AddModule(const char *name);
1616
PyAPI_FUNC(PyObject *) PyImport_ImportModule(const char *name);
17+
PyAPI_FUNC(PyObject *) PyImport_ImportModuleLevel(char *name,
18+
PyObject *globals, PyObject *locals, PyObject *fromlist, int level);
19+
20+
/* For DLL compatibility */
21+
#undef PyImport_ImportModuleEx
1722
PyAPI_FUNC(PyObject *) PyImport_ImportModuleEx(
1823
char *name, PyObject *globals, PyObject *locals, PyObject *fromlist);
24+
#define PyImport_ImportModuleEx(n, g, l, f) \
25+
PyImport_ImportModuleLevel(n, g, l, f, -1);
26+
1927
PyAPI_FUNC(PyObject *) PyImport_Import(PyObject *name);
2028
PyAPI_FUNC(PyObject *) PyImport_ReloadModule(PyObject *m);
2129
PyAPI_FUNC(void) PyImport_Cleanup(void);

Include/pythonrun.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
extern "C" {
88
#endif
99

10-
#define PyCF_MASK (CO_FUTURE_DIVISION)
10+
#define PyCF_MASK (CO_FUTURE_DIVISION | CO_FUTURE_ABSIMPORT)
1111
#define PyCF_MASK_OBSOLETE (CO_NESTED)
1212
#define PyCF_SOURCE_IS_UTF8 0x0100
1313
#define PyCF_DONT_IMPLY_DEDENT 0x0200

Lib/__future__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
"nested_scopes",
5252
"generators",
5353
"division",
54+
"absolute_import",
5455
]
5556

5657
__all__ = ["all_feature_names"] + all_feature_names
@@ -62,6 +63,7 @@
6263
CO_NESTED = 0x0010 # nested_scopes
6364
CO_GENERATOR_ALLOWED = 0 # generators (obsolete, was 0x1000)
6465
CO_FUTURE_DIVISION = 0x2000 # division
66+
CO_FUTURE_ABSIMPORT = 0x4000 # absolute_import
6567

6668
class _Feature:
6769
def __init__(self, optionalRelease, mandatoryRelease, compiler_flag):
@@ -102,3 +104,7 @@ def __repr__(self):
102104
division = _Feature((2, 2, 0, "alpha", 2),
103105
(3, 0, 0, "alpha", 0),
104106
CO_FUTURE_DIVISION)
107+
108+
absolute_import = _Feature((2, 5, 0, "alpha", 1),
109+
(2, 7, 0, "alpha", 0),
110+
CO_FUTURE_ABSIMPORT)

Lib/test/test_ast.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ def run_tests():
145145
('Module', [('TryFinally', [('Pass',)], [('Pass',)])]),
146146
('Module', [('Assert', ('Name', 'v', ('Load',)), None)]),
147147
('Module', [('Import', [('alias', 'sys', None)])]),
148-
('Module', [('ImportFrom', 'sys', [('alias', 'v', None)])]),
148+
('Module', [('ImportFrom', 'sys', [('alias', 'v', None)], 0)]),
149149
('Module', [('Exec', ('Str', 'v'), None, None)]),
150150
('Module', [('Global', ['v'])]),
151151
('Module', [('Expr', ('Num', 1))]),

Lib/test/test_importhooks.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,22 @@ def get_file():
1212
return __file__
1313
"""
1414

15+
absimp = "import sub\n"
16+
relimp = "from . import sub\n"
17+
futimp = "from __future__ import absolute_import\n"
18+
1519
reload_src = test_src+"""\
1620
reloaded = True
1721
"""
1822

1923
test_co = compile(test_src, "<???>", "exec")
2024
reload_co = compile(reload_src, "<???>", "exec")
2125

26+
test2_oldabs_co = compile(absimp + test_src, "<???>", "exec")
27+
test2_newabs_co = compile(futimp + absimp + test_src, "<???>", "exec")
28+
test2_newrel_co = compile(relimp + test_src, "<???>", "exec")
29+
test2_futrel_co = compile(futimp + relimp + test_src, "<???>", "exec")
30+
2231
test_path = "!!!_test_!!!"
2332

2433

@@ -38,6 +47,11 @@ class TestImporter:
3847
"hooktestpackage": (True, test_co),
3948
"hooktestpackage.sub": (True, test_co),
4049
"hooktestpackage.sub.subber": (False, test_co),
50+
"hooktestpackage.oldabs": (False, test2_oldabs_co),
51+
"hooktestpackage.newabs": (False, test2_newabs_co),
52+
"hooktestpackage.newrel": (False, test2_newrel_co),
53+
"hooktestpackage.futrel": (False, test2_futrel_co),
54+
"sub": (False, test_co),
4155
"reloadmodule": (False, test_co),
4256
}
4357

@@ -176,6 +190,32 @@ def doTestImports(self, importer=None):
176190
TestImporter.modules['reloadmodule'] = (False, reload_co)
177191
reload(reloadmodule)
178192
self.failUnless(hasattr(reloadmodule,'reloaded'))
193+
194+
import hooktestpackage.oldabs
195+
self.assertEqual(hooktestpackage.oldabs.get_name(),
196+
"hooktestpackage.oldabs")
197+
self.assertEqual(hooktestpackage.oldabs.sub,
198+
hooktestpackage.sub)
199+
200+
import hooktestpackage.newrel
201+
self.assertEqual(hooktestpackage.newrel.get_name(),
202+
"hooktestpackage.newrel")
203+
self.assertEqual(hooktestpackage.newrel.sub,
204+
hooktestpackage.sub)
205+
206+
import hooktestpackage.futrel
207+
self.assertEqual(hooktestpackage.futrel.get_name(),
208+
"hooktestpackage.futrel")
209+
self.assertEqual(hooktestpackage.futrel.sub,
210+
hooktestpackage.sub)
211+
212+
import sub
213+
self.assertEqual(sub.get_name(), "sub")
214+
215+
import hooktestpackage.newabs
216+
self.assertEqual(hooktestpackage.newabs.get_name(),
217+
"hooktestpackage.newabs")
218+
self.assertEqual(hooktestpackage.newabs.sub, sub)
179219

180220
def testMetaPath(self):
181221
i = MetaImporter()

Modules/parsermodule.c

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1792,27 +1792,42 @@ validate_import_name(node *tree)
17921792
&& validate_dotted_as_names(CHILD(tree, 1)));
17931793
}
17941794

1795+
/* Helper function to count the number of leading dots in
1796+
* 'from ...module import name'
1797+
*/
1798+
static int
1799+
count_from_dots(node *tree)
1800+
{
1801+
int i;
1802+
for (i = 0; i < NCH(tree); i++)
1803+
if (TYPE(CHILD(tree, i)) != DOT)
1804+
break;
1805+
return i;
1806+
}
17951807

1796-
/* 'from' dotted_name 'import' ('*' | '(' import_as_names ')' |
1808+
/* 'from' ('.'* dotted_name | '.') 'import' ('*' | '(' import_as_names ')' |
17971809
* import_as_names
17981810
*/
17991811
static int
18001812
validate_import_from(node *tree)
18011813
{
18021814
int nch = NCH(tree);
1815+
int ndots = count_from_dots(tree);
1816+
int havename = (TYPE(CHILD(tree, ndots + 1)) == dotted_name);
1817+
int offset = ndots + havename;
18031818
int res = validate_ntype(tree, import_from)
1804-
&& (nch >= 4)
1805-
&& validate_name(CHILD(tree, 0), "from")
1806-
&& validate_dotted_name(CHILD(tree, 1))
1807-
&& validate_name(CHILD(tree, 2), "import");
1808-
1809-
if (res && TYPE(CHILD(tree, 3)) == LPAR)
1810-
res = ((nch == 6)
1811-
&& validate_lparen(CHILD(tree, 3))
1812-
&& validate_import_as_names(CHILD(tree, 4))
1813-
&& validate_rparen(CHILD(tree, 5)));
1814-
else if (res && TYPE(CHILD(tree, 3)) != STAR)
1815-
res = validate_import_as_names(CHILD(tree, 3));
1819+
&& (nch >= 4 + ndots)
1820+
&& validate_name(CHILD(tree, 0), "from")
1821+
&& (!havename || validate_dotted_name(CHILD(tree, ndots + 1)))
1822+
&& validate_name(CHILD(tree, offset + 1), "import");
1823+
1824+
if (res && TYPE(CHILD(tree, offset + 2)) == LPAR)
1825+
res = ((nch == offset + 5)
1826+
&& validate_lparen(CHILD(tree, offset + 2))
1827+
&& validate_import_as_names(CHILD(tree, offset + 3))
1828+
&& validate_rparen(CHILD(tree, offset + 4)));
1829+
else if (res && TYPE(CHILD(tree, offset + 2)) != STAR)
1830+
res = validate_import_as_names(CHILD(tree, offset + 2));
18161831
return (res);
18171832
}
18181833

0 commit comments

Comments
 (0)