[Python-checkins] r51672 - in python/branches/bcannon-objcap: Include/fileobject.h Lib/test/test_descr.py Lib/test/test_objcap.py Modules/bz2module.c Modules/objcapmodule.c Objects/fileobject.c Python/bltinmodule.c securing_python.txt

brett.cannon python-checkins at python.org
Fri Sep 1 00:32:44 CEST 2006


Author: brett.cannon
Date: Fri Sep  1 00:32:42 2006
New Revision: 51672

Modified:
   python/branches/bcannon-objcap/Include/fileobject.h
   python/branches/bcannon-objcap/Lib/test/test_descr.py
   python/branches/bcannon-objcap/Lib/test/test_objcap.py
   python/branches/bcannon-objcap/Modules/bz2module.c
   python/branches/bcannon-objcap/Modules/objcapmodule.c
   python/branches/bcannon-objcap/Objects/fileobject.c
   python/branches/bcannon-objcap/Python/bltinmodule.c
   python/branches/bcannon-objcap/securing_python.txt
Log:
Re-implement the removal of the 'file' type's initializer.

file_init() was renamed _PyFile_Init() and made non-static.  At the C level, a
simulation of calling open() is now done with calling PyFile_Type.tp_new() and
passing the result to _PyFile_Init() with the tuple and dict arguments.

Exposed _PyFile_Init() as the file_init() function in the objcap module.  Takes
in an instance of the 'file' type and the typical arguments to open() and
initializes the instance as needed to refer to a file.  This allows subclasses
of 'file' to work with some changes.  __new__() will now need to be overridden
to make sure to not call file.__new__() with any arguments.  Then, in
__init__(), just pass 'self' to file_init() with the needed arguments to get
the open file bound to the instance (see Lib/test/test_descr.py for an example).
Doing this allows (eventual) import blocking of objcap and thus make open() the
only way to open new files if desired but still provide subclassing 'file' to
be functional if so desired.


Modified: python/branches/bcannon-objcap/Include/fileobject.h
==============================================================================
--- python/branches/bcannon-objcap/Include/fileobject.h	(original)
+++ python/branches/bcannon-objcap/Include/fileobject.h	Fri Sep  1 00:32:42 2006
@@ -44,6 +44,7 @@
 PyAPI_FUNC(int) PyFile_SoftSpace(PyObject *, int);
 PyAPI_FUNC(int) PyFile_WriteString(const char *, PyObject *);
 PyAPI_FUNC(int) PyObject_AsFileDescriptor(PyObject *);
+PyAPI_FUNC(int) _PyFile_Init(PyObject *, PyObject *, PyObject *);
 
 /* The default encoding used by the platform file system APIs
    If non-NULL, this is different than the default encoding for strings

Modified: python/branches/bcannon-objcap/Lib/test/test_descr.py
==============================================================================
--- python/branches/bcannon-objcap/Lib/test/test_descr.py	(original)
+++ python/branches/bcannon-objcap/Lib/test/test_descr.py	Fri Sep  1 00:32:42 2006
@@ -3,6 +3,7 @@
 from test.test_support import verify, vereq, verbose, TestFailed, TESTFN, get_original_stdout
 from copy import deepcopy
 import warnings
+import objcap
 
 warnings.filterwarnings("ignore",
          r'complex divmod\(\), // and % are deprecated$',
@@ -2452,6 +2453,13 @@
 
         lineno = 0
         ateof = 0
+
+        def __new__(self, *args, **kwargs):
+            return file.__new__(self)
+
+        def __init__(self, *args, **kwargs):
+            objcap.file_init(self, *args, **kwargs)
+
         def readline(self):
             if self.ateof:
                 return ""

Modified: python/branches/bcannon-objcap/Lib/test/test_objcap.py
==============================================================================
--- python/branches/bcannon-objcap/Lib/test/test_objcap.py	(original)
+++ python/branches/bcannon-objcap/Lib/test/test_objcap.py	Fri Sep  1 00:32:42 2006
@@ -23,9 +23,47 @@
         self.failUnless(objcap.subclasses(object))
 
 
+class FileInitTests(unittest.TestCase):
+
+    """Test removal of file type initializer and addition of file_init()."""
+
+    def test_removal(self):
+        # Calling constructor with any arguments should fail.
+        self.failUnlessRaises(TypeError, file, test_support.TESTFN, 'w')
+
+    def test_file_subclassing(self):
+        # Should still be possible to subclass 'file'.
+
+        class FileSubclass(file):
+            pass
+
+        self.failUnless(FileSubclass())
+
+    def test_file_init(self):
+        # Should be able to use file_init() to initialize instances of 'file'.
+        ins = file()
+        try:
+            objcap.file_init(ins, test_support.TESTFN, 'w')
+        finally:
+            ins.close()
+
+        ins = file()
+        try:
+            objcap.file_init(ins, test_support.TESTFN)
+        finally:
+            ins.close()
+
+        ins = file()
+        try:
+            objcap.file_init(ins, test_support.TESTFN, 'r', 0)
+        finally:
+            ins.close()
+
+
 def test_main():
     test_support.run_unittest(
-        ObjectSubclasses
+        ObjectSubclasses,
+        FileInitTests,
     )
 
 

Modified: python/branches/bcannon-objcap/Modules/bz2module.c
==============================================================================
--- python/branches/bcannon-objcap/Modules/bz2module.c	(original)
+++ python/branches/bcannon-objcap/Modules/bz2module.c	Fri Sep  1 00:32:42 2006
@@ -1298,6 +1298,7 @@
 	int compresslevel = 9;
 	int bzerror;
 	int mode_char = 0;
+	PyObject *file_ins_args = NULL;
 
 	self->size = -1;
 
@@ -1353,10 +1354,22 @@
 
 	mode = (mode_char == 'r') ? "rb" : "wb";
 
-	self->file = PyObject_CallFunction((PyObject*)&PyFile_Type, "(Osi)",
-					   name, mode, buffering);
-	if (self->file == NULL)
+	file_ins_args = Py_BuildValue("Osi", name, mode, buffering);
+	if (!file_ins_args)
+	    return -1;
+
+	self->file = PyFile_Type.tp_new(&PyFile_Type, NULL, NULL);
+	if (self->file == NULL) {
+		Py_DECREF(file_ins_args);
 		return -1;
+	}
+
+
+	if (_PyFile_Init(self->file, file_ins_args, NULL) < 0) {
+	    Py_DECREF(file_ins_args);
+	    return -1;
+	}
+	Py_DECREF(file_ins_args);
 
 	/* From now on, we have stuff to dealloc, so jump to error label
 	 * instead of returning */

Modified: python/branches/bcannon-objcap/Modules/objcapmodule.c
==============================================================================
--- python/branches/bcannon-objcap/Modules/objcapmodule.c	(original)
+++ python/branches/bcannon-objcap/Modules/objcapmodule.c	Fri Sep  1 00:32:42 2006
@@ -53,9 +53,90 @@
 "subclasses(object) -> return a list of subclasses.\n\
 Originally object.__subclasses__().");
 
+/*
+   Initialize a file object.
+
+   Need to strip out file instance and then pass remaining arguments to
+   _PyFile_Init().
+*/
+static PyObject *
+file_init(PyObject *self, PyObject *args, PyObject *kwds)
+{
+    PyObject *file_ins = NULL;
+    static char *kwlist[] = {"file_instance", "name", "mode", "buffering", 0};
+    PyObject *name = NULL;
+    PyObject *mode = NULL;
+    PyObject *buffering = NULL;
+    PyObject *init_args = NULL;
+    Py_ssize_t arg_count = 1;
+    Py_ssize_t arg_pos = 0;
+    int ret;
+
+    /* Unpack all arguments. */
+    if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO|OO:file_init", kwlist,
+		&file_ins, &name, &mode, &buffering))
+	return NULL;
+
+    /* Figure out how many arguments to _PyFile_Init() we have. */
+    if (mode)
+	arg_count += 1;
+    if (buffering)
+	arg_count += 1;
+
+    /* Construct a new argument tuple for _PyFile_Init() sans file instance. */
+    init_args = PyTuple_New(arg_count);
+    PyTuple_SET_ITEM(init_args, arg_pos++, name);
+    Py_INCREF(name);
+    if (mode) {
+	Py_INCREF(mode);
+	PyTuple_SET_ITEM(init_args, arg_pos++, mode);
+    }
+    if (buffering) {
+	Py_INCREF(buffering);
+	PyTuple_SET_ITEM(init_args, arg_pos++, buffering);
+    }
+    
+    /* Initialize file instance. */
+    Py_INCREF(file_ins);
+    ret = _PyFile_Init(file_ins, init_args, NULL);
+    Py_DECREF(file_ins);
+    Py_DECREF(init_args);
+
+    if (ret < 0)
+	return NULL;
+
+    Py_RETURN_NONE;
+}
+
+
+PyDoc_VAR(file_init_doc) =
+PyDoc_STR(
+"file_init(file_instance, name[, mode[, buffering]]) -> None\n"
+"\n"
+"Initialize a file object.  The mode can be 'r', 'w' or 'a' for reading (default),\n"
+"writing or appending.  The file itself will be created if it doesn't exist\n"
+"when opened for writing or appending; it will be truncated when\n"
+"opened for writing.  Add a 'b' to the mode for binary files.\n"
+"Add a '+' to the mode to allow simultaneous reading and writing.\n"
+"If the buffering argument is given, 0 means unbuffered, 1 means line\n"
+"buffered, and larger numbers specify the buffer size.\n"
+)
+PyDoc_STR(
+"Add a 'U' to mode to open the file for input with universal newline\n"
+"support.  Any line ending in the input file will be seen as a '\\n'\n"
+"in Python.  Also, a file so opened gains the attribute 'newlines';\n"
+"the value for this attribute is one of None (no newline read yet),\n"
+"'\\r', '\\n', '\\r\\n' or a tuple containing all the newline types seen.\n"
+"\n"
+"'U' cannot be combined with 'w' or '+' mode.\n"
+);
+
+
 
 static PyMethodDef module_methods[] = {
-    {"subclasses", (PyCFunction)object_subclasses, METH_O, "XXX"},
+    {"subclasses", (PyCFunction)object_subclasses, METH_O, object_subclass_doc},
+    {"file_init", (PyCFunction)file_init, METH_VARARGS | METH_KEYWORDS,
+	file_init_doc},
     {NULL, NULL}
 };
 

Modified: python/branches/bcannon-objcap/Objects/fileobject.c
==============================================================================
--- python/branches/bcannon-objcap/Objects/fileobject.c	(original)
+++ python/branches/bcannon-objcap/Objects/fileobject.c	Fri Sep  1 00:32:42 2006
@@ -1945,6 +1945,13 @@
 
 	assert(type != NULL && type->tp_alloc != NULL);
 
+	if ((args && PyTuple_GET_SIZE(args)) ||
+		(kwds && PyDict_Check(kwds) && PyDict_Size(kwds))) {
+	    PyErr_SetString(PyExc_TypeError,
+			    "file type's __new__ takes no parameters");
+	    return NULL;
+	}
+
 	if (not_yet_string == NULL) {
 		not_yet_string = PyString_FromString("<uninitialized file>");
 		if (not_yet_string == NULL)
@@ -1966,8 +1973,12 @@
 	return self;
 }
 
-static int
-file_init(PyObject *self, PyObject *args, PyObject *kwds)
+/*
+   Initialize a 'file' instance based on the arguments that would normally be
+   passed to the open() built-in.
+*/
+int
+_PyFile_Init(PyObject *self, PyObject *args, PyObject *kwds)
 {
 	PyFileObject *foself = (PyFileObject *)self;
 	int ret = 0;
@@ -1977,7 +1988,11 @@
 	int bufsize = -1;
 	int wideargument = 0;
 
-	assert(PyFile_Check(self));
+	if (!PyFile_Check(self)) {
+	    PyErr_SetString(PyExc_TypeError,
+		    "can only initialize instances of the 'file' type");
+	    return -1;
+	}
 	if (foself->f_fp != NULL) {
 		/* Have to close the existing file first. */
 		PyObject *closeresult = file_close(foself);
@@ -2016,7 +2031,7 @@
                 if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|si:file", 
                                                  kwlist, &o_name, &mode, 
                                                  &bufsize))
-                        return -1;
+                        goto Error;
 
 		if (fill_file_fields(foself, NULL, o_name, mode,
 				     fclose) == NULL)
@@ -2038,24 +2053,11 @@
 
 PyDoc_VAR(file_doc) =
 PyDoc_STR(
-"file(name[, mode[, buffering]]) -> file object\n"
-"\n"
-"Open a file.  The mode can be 'r', 'w' or 'a' for reading (default),\n"
-"writing or appending.  The file will be created if it doesn't exist\n"
-"when opened for writing or appending; it will be truncated when\n"
-"opened for writing.  Add a 'b' to the mode for binary files.\n"
-"Add a '+' to the mode to allow simultaneous reading and writing.\n"
-"If the buffering argument is given, 0 means unbuffered, 1 means line\n"
-"buffered, and larger numbers specify the buffer size.\n"
-)
-PyDoc_STR(
-"Add a 'U' to mode to open the file for input with universal newline\n"
-"support.  Any line ending in the input file will be seen as a '\\n'\n"
-"in Python.  Also, a file so opened gains the attribute 'newlines';\n"
-"the value for this attribute is one of None (no newline read yet),\n"
-"'\\r', '\\n', '\\r\\n' or a tuple containing all the newline types seen.\n"
+"file() -> uninitialized file object\n"
 "\n"
-"'U' cannot be combined with 'w' or '+' mode.\n"
+"To initialize a file object instance, pass it to\n\
+objcap.file_init() with the proper arguments.  Otherwise open a file\n\
+using the built-in open() function."
 );
 
 PyTypeObject PyFile_Type = {
@@ -2096,7 +2098,7 @@
 	0,					/* tp_descr_get */
 	0,					/* tp_descr_set */
 	0,					/* tp_dictoffset */
-	file_init,				/* tp_init */
+	0,					/* tp_init */
 	PyType_GenericAlloc,			/* tp_alloc */
 	file_new,				/* tp_new */
 	PyObject_Del,                           /* tp_free */

Modified: python/branches/bcannon-objcap/Python/bltinmodule.c
==============================================================================
--- python/branches/bcannon-objcap/Python/bltinmodule.c	(original)
+++ python/branches/bcannon-objcap/Python/bltinmodule.c	Fri Sep  1 00:32:42 2006
@@ -1344,7 +1344,14 @@
 static PyObject *
 builtin_open(PyObject *self, PyObject *args, PyObject *kwds)
 {
-	return PyObject_Call((PyObject*)&PyFile_Type, args, kwds);
+    PyObject *file_ins = PyFile_Type.tp_new(&PyFile_Type, NULL, NULL);
+    if (!file_ins)
+	return NULL;
+
+    if (_PyFile_Init(file_ins, args, kwds) < 0)
+	return NULL;
+
+    return file_ins;
 }
 
 PyDoc_STRVAR(open_doc,

Modified: python/branches/bcannon-objcap/securing_python.txt
==============================================================================
--- python/branches/bcannon-objcap/securing_python.txt	(original)
+++ python/branches/bcannon-objcap/securing_python.txt	Fri Sep  1 00:32:42 2006
@@ -7,13 +7,13 @@
 + Remove object.__subclasses__ (`Mutable Shared State`_) [done]
 + Dangerous constructors (`Constructors`_)
     - file
-        * Create PyFile_Init() from file_init()
+        * Create PyFile_Init() from file_init() [done]
         * Switch current C-level uses of 'file' constructor to
-          use PyFile_Type.tp_new() and PyFile_Init().
-            + built-in open()
-            + bz2 module
+          use PyFile_Type.tp_new() and PyFile_Init(). [done]
+            + built-in open() [done]
+            + bz2 module [done]
         * Expose PyFile_Init() in objcap module so that file
-          subclasses are actually worth something.
+          subclasses are actually worth something. [done]
         * Create PyFile_Safe*() version of C API that goes through
           open() built-in.
             + Convert C strings to Python objects and do a direct


More information about the Python-checkins mailing list