[Python-checkins] r72912 - in python/trunk: Doc/library/dis.rst Doc/reference/compound_stmts.rst Include/opcode.h Lib/opcode.py Lib/test/test_descr.py Misc/NEWS Python/ceval.c Python/compile.c Python/import.c

benjamin.peterson python-checkins at python.org
Mon May 25 15:13:45 CEST 2009


Author: benjamin.peterson
Date: Mon May 25 15:13:44 2009
New Revision: 72912
Log:
add a SETUP_WITH opcode
It speeds up the with statement and correctly looks up the special
methods involved.
Modified:
 python/trunk/Doc/library/dis.rst
 python/trunk/Doc/reference/compound_stmts.rst
 python/trunk/Include/opcode.h
 python/trunk/Lib/opcode.py
 python/trunk/Lib/test/test_descr.py
 python/trunk/Misc/NEWS
 python/trunk/Python/ceval.c
 python/trunk/Python/compile.c
 python/trunk/Python/import.c
Modified: python/trunk/Doc/library/dis.rst
==============================================================================
--- python/trunk/Doc/library/dis.rst	(original)
+++ python/trunk/Doc/library/dis.rst	Mon May 25 15:13:44 2009
@@ -532,6 +532,18 @@
 the names of the base classes, and TOS2 the class name.
 
 
+.. opcode:: SETUP_WITH (delta)
+
+ This opcode performs several operations before a with block starts. First,
+ it loads :meth:`~object.__exit__` from the context manager and pushes it onto
+ the stack for later use by :opcode:`WITH_CLEANUP`. Then,
+ :meth:`~object.__enter__` is called, and a finally block pointing to *delta*
+ is pushed. Finally, the result of calling the enter method is pushed onto
+ the stack. The next opcode will either ignore it (:opcode:`POP_TOP`), or
+ store it in (a) variable(s) (:opcode:`STORE_FAST`, :opcode:`STORE_NAME`, or
+ :opcode:`UNPACK_SEQUENCE`).
+
+
 .. opcode:: WITH_CLEANUP ()
 
 Cleans up the stack when a :keyword:`with` statement block exits. On top of
Modified: python/trunk/Doc/reference/compound_stmts.rst
==============================================================================
--- python/trunk/Doc/reference/compound_stmts.rst	(original)
+++ python/trunk/Doc/reference/compound_stmts.rst	Mon May 25 15:13:44 2009
@@ -339,6 +339,8 @@
 
 #. The context expression is evaluated to obtain a context manager.
 
+#. The context manager's :meth:`__exit__` is loaded for later use.
+
 #. The context manager's :meth:`__enter__` method is invoked.
 
 #. If a target was included in the :keyword:`with` statement, the return value
@@ -349,7 +351,7 @@
 The :keyword:`with` statement guarantees that if the :meth:`__enter__` method
 returns without an error, then :meth:`__exit__` will always be called. Thus, if
 an error occurs during the assignment to the target list, it will be treated the
- same as an error occurring within the suite would be. See step 5 below.
+ same as an error occurring within the suite would be. See step 6 below.
 
 #. The suite is executed.
 
Modified: python/trunk/Include/opcode.h
==============================================================================
--- python/trunk/Include/opcode.h	(original)
+++ python/trunk/Include/opcode.h	Mon May 25 15:13:44 2009
@@ -141,8 +141,10 @@
 #define CALL_FUNCTION_KW 141	/* #args + (#kwargs<<8) */
 #define CALL_FUNCTION_VAR_KW 142	/* #args + (#kwargs<<8) */
 
+#define SETUP_WITH 143
+
 /* Support for opargs more than 16 bits long */
-#define EXTENDED_ARG 143
+#define EXTENDED_ARG 145
 
 
 enum cmp_op {PyCmp_LT=Py_LT, PyCmp_LE=Py_LE, PyCmp_EQ=Py_EQ, PyCmp_NE=Py_NE, PyCmp_GT=Py_GT, PyCmp_GE=Py_GE,
Modified: python/trunk/Lib/opcode.py
==============================================================================
--- python/trunk/Lib/opcode.py	(original)
+++ python/trunk/Lib/opcode.py	Mon May 25 15:13:44 2009
@@ -181,7 +181,10 @@
 def_op('CALL_FUNCTION_VAR', 140) # #args + (#kwargs << 8)
 def_op('CALL_FUNCTION_KW', 141) # #args + (#kwargs << 8)
 def_op('CALL_FUNCTION_VAR_KW', 142) # #args + (#kwargs << 8)
-def_op('EXTENDED_ARG', 143)
-EXTENDED_ARG = 143
+
+jrel_op('SETUP_WITH', 143)
+
+def_op('EXTENDED_ARG', 145)
+EXTENDED_ARG = 145
 
 del def_op, name_op, jrel_op, jabs_op
Modified: python/trunk/Lib/test/test_descr.py
==============================================================================
--- python/trunk/Lib/test/test_descr.py	(original)
+++ python/trunk/Lib/test/test_descr.py	Mon May 25 15:13:44 2009
@@ -1689,6 +1689,7 @@
 return isinstance(int, obj)
 def do_issubclass(obj):
 return issubclass(int, obj)
+ def swallow(*args): pass
 
 # It would be nice to have every special method tested here, but I'm
 # only listing the ones I can remember outside of typeobject.c, since it
@@ -1702,11 +1703,8 @@
 ("__instancecheck__", do_isinstance, return_true, set(), {}),
 ("__subclasscheck__", do_issubclass, return_true,
 set(("__bases__",)), {}),
- # These two fail because the compiler generates LOAD_ATTR to look
- # them up. We'd have to add a new opcode to fix this, and it's
- # probably not worth it.
- # ("__enter__", run_context, iden),
- # ("__exit__", run_context, iden),
+ ("__enter__", run_context, iden, set(), {"__exit__" : swallow}),
+ ("__exit__", run_context, swallow, set(), {"__enter__" : iden}),
 ]
 
 class Checker(object):
Modified: python/trunk/Misc/NEWS
==============================================================================
--- python/trunk/Misc/NEWS	(original)
+++ python/trunk/Misc/NEWS	Mon May 25 15:13:44 2009
@@ -12,6 +12,9 @@
 Core and Builtins
 -----------------
 
+- Issue #6101: A new opcode, SETUP_WITH, has been added to speed up the with
+ statement and correctly lookup the __enter__ and __exit__ special methods.
+
 - Issue #5829: complex("1e500") no longer raises OverflowError. This
 makes it consistent with float("1e500") and interpretation of real
 and imaginary literals.
Modified: python/trunk/Python/ceval.c
==============================================================================
--- python/trunk/Python/ceval.c	(original)
+++ python/trunk/Python/ceval.c	Mon May 25 15:13:44 2009
@@ -128,6 +128,7 @@
 static PyObject * string_concatenate(PyObject *, PyObject *,
 				 PyFrameObject *, unsigned char *);
 static PyObject * kwd_as_string(PyObject *);
+static PyObject * special_lookup(PyObject *, char *, PyObject **);
 
 #define NAME_ERROR_MSG \
 	"name '%.200s' is not defined"
@@ -2467,6 +2468,33 @@
 					 STACK_LEVEL());
 			continue;
 
+		case SETUP_WITH:
+ {
+			static PyObject *exit, *enter;
+			w = TOP();
+			x = special_lookup(w, "__exit__", &exit);
+			if (!x)
+				break;
+			SET_TOP(x);
+		 u = special_lookup(w, "__enter__", &enter);
+			Py_DECREF(w);
+			if (!u) {
+				x = NULL;
+				break;
+			}
+			x = PyObject_CallFunctionObjArgs(u, NULL);
+			Py_DECREF(u);
+			if (!x)
+				break;
+			/* Setup the finally block before pushing the result
+			 of __enter__ on the stack. */
+			PyFrame_BlockSetup(f, SETUP_FINALLY, INSTR_OFFSET() + oparg,
+					 STACK_LEVEL());
+
+			PUSH(x);
+			continue;
+		}
+
 		case WITH_CLEANUP:
 		{
 			/* At the top of the stack are 1-3 values indicating
@@ -3171,6 +3199,24 @@
 }
 
 
+static PyObject *
+special_lookup(PyObject *o, char *meth, PyObject **cache)
+{
+	PyObject *res;
+	if (PyInstance_Check(o)) {
+		if (!*cache)
+			return PyObject_GetAttrString(o, meth);
+		else
+			return PyObject_GetAttr(o, *cache);
+	}
+	res = _PyObject_LookupSpecial(o, meth, cache);
+	if (res == NULL && !PyErr_Occurred()) {
+		PyErr_SetObject(PyExc_AttributeError, *cache);
+		return NULL;
+	}
+	return res;
+}
+
 
 static PyObject *
 kwd_as_string(PyObject *kwd) {
Modified: python/trunk/Python/compile.c
==============================================================================
--- python/trunk/Python/compile.c	(original)
+++ python/trunk/Python/compile.c	Mon May 25 15:13:44 2009
@@ -778,6 +778,8 @@
 			return -1;
 		case BREAK_LOOP:
 			return 0;
+	 case SETUP_WITH:
+			return 1;
 		case WITH_CLEANUP:
 			return -1; /* XXX Sometimes more */
 		case LOAD_LOCALS:
@@ -2821,80 +2823,31 @@
 static int
 compiler_with(struct compiler *c, stmt_ty s)
 {
- static identifier enter_attr, exit_attr;
 basicblock *block, *finally;
- identifier tmpvalue = NULL;
 
 assert(s->kind == With_kind);
 
- if (!enter_attr) {
-	enter_attr = PyString_InternFromString("__enter__");
-	if (!enter_attr)
-	 return 0;
- }
- if (!exit_attr) {
-	exit_attr = PyString_InternFromString("__exit__");
-	if (!exit_attr)
-	 return 0;
- }
-
 block = compiler_new_block(c);
 finally = compiler_new_block(c);
 if (!block || !finally)
 	return 0;
 
- if (s->v.With.optional_vars) {
-	/* Create a temporary variable to hold context.__enter__().
-	 We need to do this rather than preserving it on the stack
-	 because SETUP_FINALLY remembers the stack level.
-	 We need to do the assignment *inside* the try/finally
-	 so that context.__exit__() is called when the assignment
-	 fails. But we need to call context.__enter__() *before*
-	 the try/finally so that if it fails we won't call
-	 context.__exit__().
-	*/
-	tmpvalue = compiler_new_tmpname(c);
-	if (tmpvalue == NULL)
-	 return 0;
-	PyArena_AddPyObject(c->c_arena, tmpvalue);
- }
-
 /* Evaluate EXPR */
 VISIT(c, expr, s->v.With.context_expr);
+ ADDOP_JREL(c, SETUP_WITH, finally);
 
- /* Squirrel away context.__exit__ by stuffing it under context */
- ADDOP(c, DUP_TOP);
- ADDOP_O(c, LOAD_ATTR, exit_attr, names);
- ADDOP(c, ROT_TWO);
-
- /* Call context.__enter__() */
- ADDOP_O(c, LOAD_ATTR, enter_attr, names);
- ADDOP_I(c, CALL_FUNCTION, 0);
-
- if (s->v.With.optional_vars) {
-	/* Store it in tmpvalue */
-	if (!compiler_nameop(c, tmpvalue, Store))
-	 return 0;
- }
- else {
-	/* Discard result from context.__enter__() */
-	ADDOP(c, POP_TOP);
- }
-
- /* Start the try block */
- ADDOP_JREL(c, SETUP_FINALLY, finally);
-
+ /* SETUP_WITH pushes a finally block. */
 compiler_use_next_block(c, block);
 if (!compiler_push_fblock(c, FINALLY_TRY, block)) {
 	return 0;
 }
 
 if (s->v.With.optional_vars) {
-	/* Bind saved result of context.__enter__() to VAR */
-	if (!compiler_nameop(c, tmpvalue, Load) ||
-	 !compiler_nameop(c, tmpvalue, Del))
-	 return 0;
-	VISIT(c, expr, s->v.With.optional_vars);
+ VISIT(c, expr, s->v.With.optional_vars);
+ }
+ else {
+ /* Discard result from context.__enter__() */
+ ADDOP(c, POP_TOP);
 }
 
 /* BLOCK code */
Modified: python/trunk/Python/import.c
==============================================================================
--- python/trunk/Python/import.c	(original)
+++ python/trunk/Python/import.c	Mon May 25 15:13:44 2009
@@ -74,9 +74,10 @@
 Python 2.7a0: 62171 (optimize list comprehensions/change LIST_APPEND)
 Python 2.7a0: 62181 (optimize conditional branches:
 			 introduce POP_JUMP_IF_FALSE and POP_JUMP_IF_TRUE)
+ Python 2.7a0 62191 (introduce SETUP_WITH)
 .
 */
-#define MAGIC (62181 | ((long)'\r'<<16) | ((long)'\n'<<24))
+#define MAGIC (62191 | ((long)'\r'<<16) | ((long)'\n'<<24))
 
 /* Magic word as global; note that _PyImport_Init() can change the
 value of this global to accommodate for alterations of how the


More information about the Python-checkins mailing list

AltStyle によって変換されたページ (->オリジナル) /