[Python-checkins] r51958 - in python/branches/bcannon-objcap: Lib/test/test_interpreter.py Modules/interpretermodule.c securing_python.txt

brett.cannon python-checkins at python.org
Thu Sep 21 23:47:57 CEST 2006


Author: brett.cannon
Date: Thu Sep 21 23:47:56 2006
New Revision: 51958

Modified:
   python/branches/bcannon-objcap/Lib/test/test_interpreter.py
   python/branches/bcannon-objcap/Modules/interpretermodule.c
   python/branches/bcannon-objcap/securing_python.txt
Log:
Fix up handling exceptions.  Added exc_matches() method to allow comparing
against whether the raised exception matches the one passed in.

execute() also now clears any exceptions before beginning execution.
RuntimeError raised by the method also now has its message contain the name of
the exception raised.


Modified: python/branches/bcannon-objcap/Lib/test/test_interpreter.py
==============================================================================
--- python/branches/bcannon-objcap/Lib/test/test_interpreter.py	(original)
+++ python/branches/bcannon-objcap/Lib/test/test_interpreter.py	Thu Sep 21 23:47:56 2006
@@ -190,9 +190,10 @@
         # Make sure removing a value raises the proper exception when accessing
         # through the 'sys' module.
         del self.interp.sys_dict()['version']
-        # XXX requires exceptions
-        # XXX self.failUnlessRaises(XXX, self.interp.execute,
-        #                           'import sys; sys.version')
+        stdout, stderr = self.interp.redirect_output()
+        self.failUnlessRaises(RuntimeError, self.interp.execute,
+                              'import sys; sys.version')
+        self.failUnless(self.interp.exc_matches(AttributeError))
 
     def test_copied(self):
         # sys_dict should be unique per interpreter (including mutable data
@@ -217,8 +218,8 @@
         self.interp.execute("print 'test'")
         self.failUnlessEqual("test\n", stdout.getvalue())
         self.failUnless(not stderr.getvalue())
-        self.failUnlessRaises(RuntimeError, self.interp.execute, "+")
-        self.failUnless(stderr.getvalue())
+        self.interp.execute(r"import sys; sys.stderr.write('test\n')")
+        self.failUnlessEqual('test\n', stderr.getvalue())
         
     def test_redirect_output_arguments(self):
         # Test passing in arguments to redirect_output().
@@ -247,8 +248,29 @@
         # Test exc_matches().
         stdout, stderr = self.interp.redirect_output()
         self.failUnlessRaises(RuntimeError, self.interp.execute, '=')
-        #self.failUnless(self.interp.exc_matches(SyntaxError))
-
+        self.failUnless(self.interp.exc_matches(SyntaxError))
+        self.failUnless(not self.interp.exc_matches(TypeError))
+        
+    def test_exception_cleared(self):
+        # No exception should be set after a successful execution.
+        stdout, stderr = self.interp.redirect_output()
+        self.failUnlessRaises(RuntimeError, self.interp.execute, '=')
+        self.interp.execute('2 + 3')
+        self.failUnlessRaises(LookupError, self.interp.exc_matches, Exception)
+        
+    def test_multiple_exc_checks(self):
+        # Be able to check the exception multiple times.
+        stdout, stderr = self.interp.redirect_output()
+        self.failUnlessRaises(RuntimeError, self.interp.execute, '=')
+        for x in range(2):
+            self.failUnless(self.interp.exc_matches(SyntaxError))
+            
+    def test_SystemExit_safe(self):
+        # Raising SystemExit should not cause the process to exit.
+        self.failUnlessRaises(RuntimeError, self.interp.execute,
+                                "raise SystemExit")
+        self.failUnless(self.interp.exc_matches(SystemExit))
+        
 
 def test_main():
     test_support.run_unittest(

Modified: python/branches/bcannon-objcap/Modules/interpretermodule.c
==============================================================================
--- python/branches/bcannon-objcap/Modules/interpretermodule.c	(original)
+++ python/branches/bcannon-objcap/Modules/interpretermodule.c	Thu Sep 21 23:47:56 2006
@@ -97,12 +97,14 @@
    Execute Python source code in the interpreter.
 */
 static PyObject *
-interpreter_exec(PyObject *self, PyObject *arg)
+interpreter_exec(PyInterpreterObject *self, PyObject *arg)
 {
-    PyInterpreterObject *interp_self = (PyInterpreterObject *)self;
     const char *str_arg = NULL;
     PyThreadState* cur_tstate = NULL;
-    int result = 0;
+    PyObject *main_module = NULL;
+    PyObject *main_dict = NULL;
+    PyObject *result = NULL;
+    const char *exc_name = NULL;
 
     if (!PyString_Check(arg)) {
 	PyErr_SetString(PyExc_TypeError, "argument must be a string");
@@ -113,21 +115,40 @@
     if (!str_arg)
 	return NULL;
 
-    cur_tstate = PyThreadState_Swap(interp_self->tstate);
+    /* Execute in 'self'. */
+    cur_tstate = PyThreadState_Swap(self->tstate);
+    
+    /* If a previous exception was present, clear it out. */
+    if (PyErr_Occurred())
+        PyErr_Clear();
+    
+    /* Code borrowed from PyRun_SimpleStringFlags(). */
+    main_module = PyImport_AddModule("__main__");
+    if (!main_module) {
+        goto back_to_caller;
+    }
+    
+    main_dict = PyModule_GetDict(main_module);
 
-    result = PyRun_SimpleString(str_arg);
-    if (result < 0) {
-	PyErr_Clear();
+    result = PyRun_String(str_arg, Py_file_input, main_dict, main_dict);
+    
+    if (result) {
+        Py_DECREF(result);
+    }
+    else {
+        exc_name = ((PyTypeObject *)PyErr_Occurred())->tp_name;
     }
 
+ back_to_caller:
+    /* Execute in calling interpreter. */
     PyThreadState_Swap(cur_tstate);
 
-    if (result < 0) {
-	PyErr_SetString(PyExc_RuntimeError,
-			"exception during execution");
+    if (!result) {
+	PyErr_Format(PyExc_RuntimeError,
+			"execution raised during execution (%s)", exc_name);
 	return NULL;
     }
-
+    
     Py_RETURN_NONE;
 }
 
@@ -143,14 +164,14 @@
     
     if (!py_stdout) {
         /* Argument for NewOutput() copied from PycStringIO->NewInput(). */
-        py_stdout = (PycStringIO->NewOutput)(128);
+        py_stdout = (PycStringIO->NewOutput)(512);
         if (!py_stdout)
             return NULL;
     }
     
     if (!py_stderr) {
         /* Argument for NewOutput() copied from PycStringIO->NewInput(). */
-        py_stderr = (PycStringIO->NewOutput)(128);
+        py_stderr = (PycStringIO->NewOutput)(512);
         if (!py_stderr)
             return NULL;
     }
@@ -174,15 +195,55 @@
     return used_stdout_stderr;
 }
 
+static PyObject *
+exc_matches(PyInterpreterObject *self, PyObject *arg)
+{
+    PyThreadState *starting_tstate = NULL;
+    PyObject *raised_exc = NULL;
+    int result = 0;
+    
+    /* Can only compare against exception classes or instances. */
+    if (!(PyExceptionClass_Check(arg) || PyExceptionInstance_Check(arg))) {
+        PyErr_SetString(PyExc_TypeError,
+                        "argument must be an exception class or instance");
+        return NULL;
+    }
+    
+    /* Now executing under 'self'. */
+    starting_tstate = PyThreadState_Swap(self->tstate);
+    
+    raised_exc = PyErr_Occurred();
+    
+    if (!raised_exc) {
+        /* Executing under calling interpreter. */
+        PyThreadState_Swap(starting_tstate);
+        PyErr_SetString(PyExc_LookupError, "no exception set");
+        return NULL;
+    }
+    
+    if (PyErr_GivenExceptionMatches(raised_exc, arg))
+        result = 1;
+    
+    /* Execute under calling interpreter. */
+    PyThreadState_Swap(starting_tstate);
+    
+    if (result)
+        Py_RETURN_TRUE;
+    else
+        Py_RETURN_FALSE;
+}
+
 static PyMethodDef interpreter_methods[] = {
     {"builtins", (PyCFunction)interpreter_builtins, METH_NOARGS,
         "Return the built-in namespace dict."},
     {"sys_dict", (PyCFunction)interpreter_sys_dict, METH_NOARGS,
         "Return the 'sys' module's data dictionary."},
-    {"execute", interpreter_exec, METH_O,
+    {"execute", (PyCFunction)interpreter_exec, METH_O,
 	"Execute the passed-in string in the interpreter."},
     {"redirect_output", (PyCFunction)redirect_output, METH_VARARGS,
         "Redirect stdout to stderr.  Returns tuple of objects used."},
+    {"exc_matches", (PyCFunction)exc_matches, METH_O,
+    "Check if the raised exception in the interpreter matches the argument"},
     {NULL}
 };
 
@@ -245,7 +306,7 @@
 \n\
 XXX");
 
-PyTypeObject PyInterpreter_Type = {
+static PyTypeObject PyInterpreter_Type = {
 	PyObject_HEAD_INIT(NULL)
 	0,					/* ob_size */
 	"interpreterInterpreter",		/* tp_name */

Modified: python/branches/bcannon-objcap/securing_python.txt
==============================================================================
--- python/branches/bcannon-objcap/securing_python.txt	(original)
+++ python/branches/bcannon-objcap/securing_python.txt	Thu Sep 21 23:47:56 2006
@@ -34,7 +34,8 @@
         * Just promote removal
     - exit()
         * Have SystemExit exit the process only if no other
-          interpreters are running. <critical>
+          interpreters are running. <critical> [done]
+        * XXX Safe?
 + Filesystem path hiding (`Filesystem Information`_) <critical?>
 + Tweaked stdlib modules
     - mini 'sys' module (`Making the ``sys`` Module Safe`_)
@@ -48,15 +49,10 @@
     - Set 'sys' module settings [done]
     - Set 'sys.modules' [done]
     - API
-        * Python
+        * Python [done]
         * C
     - Securely handle exceptions being raised in sub-interpreter
-        * Raise InterpreterException w/ string for the exception type
-          and a sanitized string for message?
-        * Provide way to compare the raised exception in a safe manner
-          (i.e., expose PyErr_ExceptionMatches())?
-        * Provide exc_info() for those who know what they are doing?
-            + Raise a SecurityWarning when used?
+      [done]
     - Redirect output [done]
 + Tear out old restricted mode code.
 


More information about the Python-checkins mailing list