Index: Objects/fileobject.c =================================================================== --- Objects/fileobject.c (revision 60703) +++ Objects/fileobject.c (working copy) @@ -32,6 +32,10 @@ #include #endif +#ifdef HAVE_FCNTL_H +#include +#endif /* HAVE_FCNTL_H */ + #ifdef HAVE_GETC_UNLOCKED #define GETC(f) getc_unlocked(f) #define FLOCKFILE(f) flockfile(f) @@ -272,10 +276,11 @@ return (PyObject *)f; } -PyObject * -PyFile_FromFile(FILE *fp, char *name, char *mode, int (*close)(FILE *)) +static PyObject * +_PyFile_FromFileType(FILE *fp, char *name, char *mode, int (*close)(FILE *), + PyTypeObject *type) { - PyFileObject *f = (PyFileObject *)PyFile_Type.tp_new(&PyFile_Type, + PyFileObject *f = (PyFileObject *)PyFile_Type.tp_new(type, NULL, NULL); if (f != NULL) { PyObject *o_name = PyString_FromString(name); @@ -291,6 +296,12 @@ } PyObject * +PyFile_FromFile(FILE *fp, char *name, char *mode, int (*close)(FILE *)) +{ + return _PyFile_FromFileType(fp, name, mode, close, &PyFile_Type); +} + +PyObject * PyFile_FromString(char *name, char *mode) { extern int fclose(FILE *); @@ -1660,9 +1671,9 @@ } static PyObject * -file_exit(PyFileObject *f, PyObject *args) +file_exit(PyObject *f, PyObject *args) { - PyObject *ret = file_close(f); + PyObject *ret = PyObject_CallMethod(f, "close", ""); if (!ret) /* If error occurred, pass through */ return NULL; @@ -1673,6 +1684,62 @@ Py_RETURN_NONE; } +PyDoc_STRVAR(fdopen_doc, +"fdopen(fd [, mode='r' [, bufsize]]) -> file_object\n\n\ +Return an open file object connected to a file descriptor."); + +static PyObject * +file_fdopen(PyTypeObject *type, PyObject *args) +{ + int fd; + char *orgmode = "r"; + int bufsize = -1; + FILE *fp; + PyObject *f; + char *mode; + char *name = ""; + if (!PyArg_ParseTuple(args, "i|sis", &fd, &orgmode, &bufsize, &name)) + return NULL; + + /* Sanitize mode. See fileobject.c */ + mode = PyMem_MALLOC(strlen(orgmode)+3); + if (!mode) { + PyErr_NoMemory(); + return NULL; + } + strcpy(mode, orgmode); + if (_PyFile_SanitizeMode(mode)) { + PyMem_FREE(mode); + return NULL; + } + Py_BEGIN_ALLOW_THREADS +#if !defined(MS_WINDOWS) && defined(HAVE_FCNTL_H) + if (mode[0] == 'a') { + /* try to make sure the O_APPEND flag is set */ + int flags; + flags = fcntl(fd, F_GETFL); + if (flags != -1) + fcntl(fd, F_SETFL, flags | O_APPEND); + fp = fdopen(fd, mode); + if (fp == NULL && flags != -1) + /* restore old mode if fdopen failed */ + fcntl(fd, F_SETFL, flags); + } else { + fp = fdopen(fd, mode); + } +#else + fp = fdopen(fd, mode); +#endif + Py_END_ALLOW_THREADS + PyMem_FREE(mode); + if (fp == NULL) + return PyErr_SetFromErrno(PyExc_OSError); + f = _PyFile_FromFileType(fp, name, orgmode, fclose, type); + if (f != NULL) + PyFile_SetBufSize(f, bufsize); + return f; +} + PyDoc_STRVAR(readline_doc, "readline([size]) -> next line from the file, as a string.\n" "\n" @@ -1782,6 +1849,7 @@ {"isatty", (PyCFunction)file_isatty, METH_NOARGS, isatty_doc}, {"__enter__", (PyCFunction)file_self, METH_NOARGS, enter_doc}, {"__exit__", (PyCFunction)file_exit, METH_VARARGS, exit_doc}, + {"fdopen", (PyCFunction)file_fdopen, METH_VARARGS|METH_CLASS, fdopen_doc}, {NULL, NULL} /* sentinel */ }; @@ -1985,6 +2053,8 @@ return self; } + + static int file_init(PyObject *self, PyObject *args, PyObject *kwds) { Index: Lib/tempfile.py =================================================================== --- Lib/tempfile.py (revision 60703) +++ Lib/tempfile.py (working copy) @@ -370,98 +370,58 @@ raise IOError, (_errno.EEXIST, "No usable temporary filename found") +class NamedTemporaryFile(file): + def __new__(cls, mode='w+b', bufsize=-1, suffix="", + prefix=template, dir=None, delete=True): + """Create and return a temporary file. + Arguments: + 'prefix', 'suffix', 'dir' -- as for mkstemp. + 'mode' -- the mode argument to os.fdopen (default "w+b"). + 'bufsize' -- the buffer size argument to os.fdopen (default -1). + 'delete' -- whether the file is deleted on close (default True). + The file is created as mkstemp() would do it. -class _TemporaryFileWrapper: - """Temporary file wrapper + Returns an object with a file-like interface; the name of the file + is accessible as file.name. The file will be automatically deleted + when it is closed unless the 'delete' argument is set to False. + """ - This class provides a wrapper around files opened for - temporary use. In particular, it seeks to automatically - remove the file when it is no longer needed. - """ + if dir is None: + dir = gettempdir() - def __init__(self, file, name, delete=True): - self.file = file - self.name = name + if 'b' in mode: + flags = _bin_openflags + else: + flags = _text_openflags + + # Setting O_TEMPORARY in the flags causes the OS to delete + # the file when it is closed. This is only supported by Windows. + if _os.name == 'nt' and delete: + flags |= _os.O_TEMPORARY + + (fd, name) = _mkstemp_inner(dir, prefix, suffix, flags) + self = cls.fdopen(fd, mode, bufsize, name) self.close_called = False self.delete = delete - def __getattr__(self, name): - # Attribute lookups are delegated to the underlying file - # and cached for non-numeric results - # (i.e. methods are cached, closed and friends are not) - file = self.__dict__['file'] - a = getattr(file, name) - if not issubclass(type(a), type(0)): - setattr(self, name, a) - return a - - # The underlying __enter__ method returns the wrong object - # (self.file) so override it to return the wrapper - def __enter__(self): - self.file.__enter__() return self - # NT provides delete-on-close as a primitive, so we don't need - # the wrapper to do anything special. We still use it so that - # file.name is useful (i.e. not "(fdopen)") with NamedTemporaryFile. - if _os.name != 'nt': - # Cache the unlinker so we don't get spurious errors at - # shutdown when the module-level "os" is None'd out. Note - # that this must be referenced as self.unlink, because the - # name TemporaryFileWrapper may also get None'd out before - # __del__ is called. - unlink = _os.unlink + def __init__(self, *args, **kwds): + pass + + unlink = _os.unlink - def close(self): - if not self.close_called: - self.close_called = True - self.file.close() - if self.delete: - self.unlink(self.name) + def close(self): + if not self.close_called: + self.close_called = True + super(NamedTemporaryFile, self).close() + if self.delete: + self.unlink(self.name) - def __del__(self): - self.close() + def __del__(self): + self.close() - # Need to trap __exit__ as well to ensure the file gets - # deleted when used in a with statement - def __exit__(self, exc, value, tb): - result = self.file.__exit__(exc, value, tb) - self.close() - return result - -def NamedTemporaryFile(mode='w+b', bufsize=-1, suffix="", - prefix=template, dir=None, delete=True): - """Create and return a temporary file. - Arguments: - 'prefix', 'suffix', 'dir' -- as for mkstemp. - 'mode' -- the mode argument to os.fdopen (default "w+b"). - 'bufsize' -- the buffer size argument to os.fdopen (default -1). - 'delete' -- whether the file is deleted on close (default True). - The file is created as mkstemp() would do it. - - Returns an object with a file-like interface; the name of the file - is accessible as file.name. The file will be automatically deleted - when it is closed unless the 'delete' argument is set to False. - """ - - if dir is None: - dir = gettempdir() - - if 'b' in mode: - flags = _bin_openflags - else: - flags = _text_openflags - - # Setting O_TEMPORARY in the flags causes the OS to delete - # the file when it is closed. This is only supported by Windows. - if _os.name == 'nt' and delete: - flags |= _os.O_TEMPORARY - - (fd, name) = _mkstemp_inner(dir, prefix, suffix, flags) - file = _os.fdopen(fd, mode, bufsize) - return _TemporaryFileWrapper(file, name, delete) - if _os.name != 'posix' or _os.sys.platform == 'cygwin': # On non-POSIX and Cygwin systems, assume that we cannot unlink a file # while it is open.