[Python-Dev] Multi-line import implementation (was: 2.4a2, and @decorators)

Dima Dorfman dima at trit.org
Mon Aug 9 06:31:05 CEST 2004


Anthony Baxter <anthonyatinterlink.com.au> wrote:
> Guido van Rossum wrote:
> > Did the relative import syntax that was approved ever get checked in?
> > I thought it was on the list of things to land in 2.4?
> 
> Nope. The 2.4 release schedule PEP now lists this as lacking someone
> to drive it. I won't/can't spend time on it. So, if one of the people
> who wants it could step forward, that'd be excellent. Even if it's
> just to do the multi-line import syntax of
> 
>   from foo import ( bar, baz, bar2,
>                     baz2, bar3, baz3 )

I took a stab at implementing this (the easy part of PEP 328--parens
around import lists). Are the exact semantics of what's allowed
documented somewhere? PEP 328 mostly talks about relative and absolute
imports, and it doesn't specify the exact semantics of where
parentheses should be allowed. My patch (attached) accepts

  import (os, sys)
  from sys import (stdin, stdout, stderr)
  import (os)
  from sys import (*)

but rejects

  from (sys) import stdin
  import (os), (sys)
  import (os,)

Should any of those be allowed? Anything that I missed?

The patch is incomplete in other ways; the docs haven't been updated
and neither have the parser module and compile package. If it's
decided that it would be okay to include this separately from
relative/absolute import support (they're different features, really),
I'll complete the patch.

Dima.
-------------- next part --------------
Index: Python/compile.c
===================================================================
RCS file: /cvsroot/python/python/dist/src/Python/compile.c,v
retrieving revision 2.314
diff -u -c -r2.314 compile.c
*** Python/compile.c	6 Aug 2004 19:46:34 -0000	2.314
--- Python/compile.c	9 Aug 2004 04:01:38 -0000
***************
*** 3333,3374 ****
  static void
  com_import_stmt(struct compiling *c, node *n)
  {
  	int i;
  	REQ(n, import_stmt);
! 	/* 'import' dotted_name (',' dotted_name)* |
! 	   'from' dotted_name 'import' ('*' | NAME (',' NAME)*) */
! 	if (STR(CHILD(n, 0))[0] == 'f') {
  		PyObject *tup;
- 		/* 'from' dotted_name 'import' ... */
  		REQ(CHILD(n, 1), dotted_name);
! 		
! 		if (TYPE(CHILD(n, 3)) == STAR) {
  			tup = Py_BuildValue("(s)", "*");
! 		} else {
! 			tup = PyTuple_New((NCH(n) - 2)/2);
! 			for (i = 3; i < NCH(n); i += 2) {
! 				PyTuple_SET_ITEM(tup, (i-3)/2, 
  					PyString_FromString(STR(
! 						CHILD(CHILD(n, i), 0))));
  			}
  		}
  		com_addoparg(c, LOAD_CONST, com_addconst(c, tup));
  		Py_DECREF(tup);
  		com_push(c, 1);
  		com_addopname(c, IMPORT_NAME, CHILD(n, 1));
! 		if (TYPE(CHILD(n, 3)) == STAR) 
  			com_addbyte(c, IMPORT_STAR);
  		else {
! 			for (i = 3; i < NCH(n); i += 2) 
! 				com_from_import(c, CHILD(n, i));
  			com_addbyte(c, POP_TOP);
  		}
  		com_pop(c, 1);
  	}
  	else {
! 		/* 'import' ... */
! 		for (i = 1; i < NCH(n); i += 2) {
! 			node *subn = CHILD(n, i);
  			REQ(subn, dotted_as_name);
  			com_addoparg(c, LOAD_CONST, com_addconst(c, Py_None));
  			com_push(c, 1);
--- 3333,3379 ----
  static void
  com_import_stmt(struct compiling *c, node *n)
  {
+ 	node *nn;
  	int i;
  	REQ(n, import_stmt);
! 	n = CHILD(n, 0);
! 	/* import_stmt: just_import | from_import */
! 	if (TYPE(n) == from_import) {
! 		/* from_import: 'from' dotted_name 'import' ('*' | '(' '*' ')'
! 		     | '(' import_as_names ')' | import_as_names) */
  		PyObject *tup;
  		REQ(CHILD(n, 1), dotted_name);
! 		nn = CHILD(n, 3 + (TYPE(CHILD(n, 3)) == LPAR));
! 		if (TYPE(nn) == STAR)
  			tup = Py_BuildValue("(s)", "*");
! 		else {
! 			REQ(nn, import_as_names);
! 			tup = PyTuple_New((NCH(nn) + 1) / 2);
! 			for (i = 0; i < NCH(nn); i += 2) {
! 				PyTuple_SET_ITEM(tup, i / 2,
  					PyString_FromString(STR(
! 						CHILD(CHILD(nn, i), 0))));
  			}
  		}
  		com_addoparg(c, LOAD_CONST, com_addconst(c, tup));
  		Py_DECREF(tup);
  		com_push(c, 1);
  		com_addopname(c, IMPORT_NAME, CHILD(n, 1));
! 		if (TYPE(nn) == STAR)
  			com_addbyte(c, IMPORT_STAR);
  		else {
! 			for (i = 0; i < NCH(nn); i += 2)
! 				com_from_import(c, CHILD(nn, i));
  			com_addbyte(c, POP_TOP);
  		}
  		com_pop(c, 1);
  	}
  	else {
! 		/* 'import' ('(' dotted_as_names ')' | dotted_as_names)*/
! 		nn = CHILD(n, 1 + (TYPE(CHILD(n, 1)) == LPAR));
! 		REQ(nn, dotted_as_names);
! 		for (i = 0; i < NCH(nn); i += 2) {
! 			node *subn = CHILD(nn, i);
  			REQ(subn, dotted_as_name);
  			com_addoparg(c, LOAD_CONST, com_addconst(c, Py_None));
  			com_push(c, 1);
***************
*** 6131,6144 ****
  static void
  symtable_import(struct symtable *st, node *n)
  {
  	int i;
! 	/* import_stmt: 'import' dotted_as_name (',' dotted_as_name)* 
!               | 'from' dotted_name 'import' 
!                                 ('*' | import_as_name (',' import_as_name)*)
! 	   import_as_name: NAME [NAME NAME]
! 	*/
! 	if (STR(CHILD(n, 0))[0] == 'f') {  /* from */
  		node *dotname = CHILD(n, 1);
  		if (strcmp(STR(CHILD(dotname, 0)), "__future__") == 0) {
  			/* check for bogus imports */
  			if (n->n_lineno >= st->st_future->ff_last_lineno) {
--- 6136,6150 ----
  static void
  symtable_import(struct symtable *st, node *n)
  {
+ 	node *nn;
  	int i;
! 	/* import_stmt: just_import | from_import */
! 	n = CHILD(n, 0);
! 	if (TYPE(n) == from_import) {
! 		/* from_import: 'from' dotted_name 'import' ('*' | '(' '*' ')'
! 		     | '(' import_as_names ')' | import_as_names) */
  		node *dotname = CHILD(n, 1);
+ 		REQ(dotname, dotted_name);
  		if (strcmp(STR(CHILD(dotname, 0)), "__future__") == 0) {
  			/* check for bogus imports */
  			if (n->n_lineno >= st->st_future->ff_last_lineno) {
***************
*** 6148,6154 ****
  				return;
  			}
  		}
! 		if (TYPE(CHILD(n, 3)) == STAR) {
  			if (st->st_cur->ste_type != TYPE_MODULE) {
  				if (symtable_warn(st,
  				  "import * only allowed at module level") < 0)
--- 6154,6161 ----
  				return;
  			}
  		}
! 		nn = CHILD(n, 3 + (TYPE(CHILD(n, 3)) == LPAR));
! 		if (TYPE(nn) == STAR) {
  			if (st->st_cur->ste_type != TYPE_MODULE) {
  				if (symtable_warn(st,
  				  "import * only allowed at module level") < 0)
***************
*** 6157,6164 ****
  			st->st_cur->ste_optimized |= OPT_IMPORT_STAR;
  			st->st_cur->ste_opt_lineno = n->n_lineno;
  		} else {
! 			for (i = 3; i < NCH(n); i += 2) {
! 				node *c = CHILD(n, i);
  				if (NCH(c) > 1) /* import as */
  					symtable_assign(st, CHILD(c, 2),
  							DEF_IMPORT);
--- 6164,6172 ----
  			st->st_cur->ste_optimized |= OPT_IMPORT_STAR;
  			st->st_cur->ste_opt_lineno = n->n_lineno;
  		} else {
! 			REQ(nn, import_as_names);
! 			for (i = 0; i < NCH(nn); i += 2) {
! 				node *c = CHILD(nn, i);
  				if (NCH(c) > 1) /* import as */
  					symtable_assign(st, CHILD(c, 2),
  							DEF_IMPORT);
***************
*** 6167,6176 ****
  							DEF_IMPORT);
  			}
  		}
! 	} else { 
! 		for (i = 1; i < NCH(n); i += 2) {
! 			symtable_assign(st, CHILD(n, i), DEF_IMPORT);
! 		}
  	}
  }
  
--- 6175,6186 ----
  							DEF_IMPORT);
  			}
  		}
! 	} else {
! 		/* 'import' ('(' dotted_as_names ')' | dotted_as_names)*/
! 		nn = CHILD(n, 1 + (TYPE(CHILD(n, 1)) == LPAR));
! 		REQ(nn, dotted_as_names);
! 		for (i = 0; i < NCH(nn); i += 2)
! 			symtable_assign(st, CHILD(nn, i), DEF_IMPORT);
  	}
  }
  
Index: Python/future.c
===================================================================
RCS file: /cvsroot/python/python/dist/src/Python/future.c,v
retrieving revision 2.13
diff -u -c -r2.13 future.c
*** Python/future.c	11 Dec 2002 14:04:59 -0000	2.13
--- Python/future.c	9 Aug 2004 04:01:38 -0000
***************
*** 18,35 ****
  {
  	int i;
  	char *feature;
! 	node *ch;
  
! 	REQ(n, import_stmt); /* must by from __future__ import ... */
! 
! 	for (i = 3; i < NCH(n); i += 2) {
! 		ch = CHILD(n, i);
! 		if (TYPE(ch) == STAR) {
! 			PyErr_SetString(PyExc_SyntaxError,
! 					FUTURE_IMPORT_STAR);
! 			PyErr_SyntaxLocation(filename, ch->n_lineno);
! 			return -1;
! 		}
  		REQ(ch, import_as_name);
  		feature = STR(CHILD(ch, 0));
  		if (strcmp(feature, FUTURE_NESTED_SCOPES) == 0) {
--- 18,35 ----
  {
  	int i;
  	char *feature;
! 	node *ch, *nn;
  
! 	REQ(n, from_import);
! 	nn = CHILD(n, 3 + (TYPE(CHILD(n, 3)) == LPAR));
! 	if (TYPE(nn) == STAR) {
! 		PyErr_SetString(PyExc_SyntaxError, FUTURE_IMPORT_STAR);
! 		PyErr_SyntaxLocation(filename, nn->n_lineno);
! 		return -1;
! 	}
! 	REQ(nn, import_as_names);
! 	for (i = 0; i < NCH(nn); i += 2) {
! 		ch = CHILD(nn, i);
  		REQ(ch, import_as_name);
  		feature = STR(CHILD(ch, 0));
  		if (strcmp(feature, FUTURE_NESTED_SCOPES) == 0) {
***************
*** 188,194 ****
  	case import_stmt: {
  		node *name;
  
! 		if (STR(CHILD(n, 0))[0] != 'f') { /* from */
  			ff->ff_last_lineno = n->n_lineno;
  			return 0;
  		}
--- 188,195 ----
  	case import_stmt: {
  		node *name;
  
! 		n = CHILD(n, 0);
! 		if (TYPE(n) != from_import) {
  			ff->ff_last_lineno = n->n_lineno;
  			return 0;
  		}
Index: Grammar/Grammar
===================================================================
RCS file: /cvsroot/python/python/dist/src/Grammar/Grammar,v
retrieving revision 1.50
diff -u -c -r1.50 Grammar
*** Grammar/Grammar	2 Aug 2004 06:09:53 -0000	1.50
--- Grammar/Grammar	9 Aug 2004 04:01:38 -0000
***************
*** 51,59 ****
  return_stmt: 'return' [testlist]
  yield_stmt: 'yield' testlist
  raise_stmt: 'raise' [test [',' test [',' test]]]
! import_stmt: 'import' dotted_as_name (',' dotted_as_name)* | 'from' dotted_name 'import' ('*' | import_as_name (',' import_as_name)*)
  import_as_name: NAME [NAME NAME]
  dotted_as_name: dotted_name [NAME NAME]
  dotted_name: NAME ('.' NAME)*
  global_stmt: 'global' NAME (',' NAME)*
  exec_stmt: 'exec' expr ['in' test [',' test]]
--- 51,63 ----
  return_stmt: 'return' [testlist]
  yield_stmt: 'yield' testlist
  raise_stmt: 'raise' [test [',' test [',' test]]]
! import_stmt: just_import | from_import
! just_import: 'import' ('(' dotted_as_names ')' | dotted_as_names)
! from_import: 'from' dotted_name 'import' ('*' | '(' '*' ')' | '(' import_as_names ')' | import_as_names)
  import_as_name: NAME [NAME NAME]
  dotted_as_name: dotted_name [NAME NAME]
+ import_as_names: import_as_name (',' import_as_name)*
+ dotted_as_names: dotted_as_name (',' dotted_as_name)*
  dotted_name: NAME ('.' NAME)*
  global_stmt: 'global' NAME (',' NAME)*
  exec_stmt: 'exec' expr ['in' test [',' test]]
Index: Lib/test/test_compile.py
===================================================================
RCS file: /cvsroot/python/python/dist/src/Lib/test/test_compile.py,v
retrieving revision 1.22
diff -u -c -r1.22 test_compile.py
*** Lib/test/test_compile.py	2 Aug 2004 08:30:07 -0000	1.22
--- Lib/test/test_compile.py	9 Aug 2004 04:01:39 -0000
***************
*** 211,216 ****
--- 211,263 ----
              self.assertRaises(SyntaxError, compile, stmt, 'tmp', 'single')
              self.assertRaises(SyntaxError, compile, stmt, 'tmp', 'exec')
  
+     def test_import(self):
+         # These should pass
+         exec '''
+ from __future__ import (nested_scopes, generators)
+ import sys
+ import (os,
+   sys)
+ from sys import (stdin, stderr,
+   stdout)
+ from keyword import (*)'''
+         # These should fail
+         try:
+             exec 'from (sys) import stdin'
+             self.fail("parens not allowed around single dotted_name")
+         except SyntaxError:
+             pass
+         try:
+             exec 'import (os), (sys)'
+             self.fail("entire name list must be enclosed in parens")
+         except SyntaxError:
+             pass
+         try:
+             exec 'import ((os), (sys))'
+             self.fail("subparens not allowed")
+         except SyntaxError:
+             pass
+         try:
+             exec 'import sys)'
+             self.fail("mismatched parens")
+         except SyntaxError:
+             pass
+         try:
+             exec 'from sys import stdin)'
+             self.fail("mismatched parens")
+         except SyntaxError:
+             pass
+         try:
+             exec 'import (os,)'
+             self.fail("not a tuple")
+         except SyntaxError:
+             pass
+         try:
+             exec 'from __future__ import nested_scopes)'
+             self.fail("mismatched parens in __future__");
+         except SyntaxError:
+             pass
+ 
  def test_main():
      test_support.run_unittest(TestSpecifics)
  
Index: Lib/test/test_grammar.py
===================================================================
RCS file: /cvsroot/python/python/dist/src/Lib/test/test_grammar.py,v
retrieving revision 1.50
diff -u -c -r1.50 test_grammar.py
*** Lib/test/test_grammar.py	19 May 2004 08:20:09 -0000	1.50
--- Lib/test/test_grammar.py	9 Aug 2004 04:01:42 -0000
***************
*** 417,428 ****
  try: raise KeyboardInterrupt
  except KeyboardInterrupt: pass
  
! print 'import_stmt' # 'import' NAME (',' NAME)* | 'from' NAME 'import' ('*' | NAME (',' NAME)*)
  import sys
  import time, sys
  from time import time
  from sys import *
  from sys import path, argv
  
  print 'global_stmt' # 'global' NAME (',' NAME)*
  def f():
--- 417,434 ----
  try: raise KeyboardInterrupt
  except KeyboardInterrupt: pass
  
! print 'just_import' # 'import' ('(' dotted_as_names ')' | dotted_as_names)
  import sys
+ import (sys)
  import time, sys
+ import (time, sys)
+ print 'from_import' # 'from' dotted_name 'import' ('*' | '(' '*' ')' | '(' import_as_names ')' | import_as_names)
  from time import time
+ from time import (time)
  from sys import *
+ from sys import (*)
  from sys import path, argv
+ from sys import (path, argv)
  
  print 'global_stmt' # 'global' NAME (',' NAME)*
  def f():
Index: Lib/test/output/test_grammar
===================================================================
RCS file: /cvsroot/python/python/dist/src/Lib/test/output/test_grammar,v
retrieving revision 1.20
diff -u -c -r1.20 test_grammar
*** Lib/test/output/test_grammar	21 May 2003 17:34:49 -0000	1.20
--- Lib/test/output/test_grammar	9 Aug 2004 04:01:42 -0000
***************
*** 35,41 ****
  testing continue and break in try/except in loop
  return_stmt
  raise_stmt
! import_stmt
  global_stmt
  exec_stmt
  assert_stmt
--- 35,42 ----
  testing continue and break in try/except in loop
  return_stmt
  raise_stmt
! just_import
! from_import
  global_stmt
  exec_stmt
  assert_stmt


More information about the Python-Dev mailing list