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
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
participants (1)
-
brett.cannon