diff options
-rw-r--r-- | Doc/c-api/concrete.rst | 6 | ||||
-rw-r--r-- | Include/fileobject.h | 3 | ||||
-rw-r--r-- | Lib/io.py | 13 | ||||
-rwxr-xr-x | Lib/quopri.py | 14 | ||||
-rw-r--r-- | Lib/test/test_io.py | 3 | ||||
-rw-r--r-- | Misc/NEWS | 3 | ||||
-rw-r--r-- | Modules/_fileio.c | 36 | ||||
-rw-r--r-- | Objects/fileobject.c | 6 | ||||
-rw-r--r-- | Python/import.c | 2 | ||||
-rw-r--r-- | Python/pythonrun.c | 8 |
10 files changed, 63 insertions, 31 deletions
diff --git a/Doc/c-api/concrete.rst b/Doc/c-api/concrete.rst index cdea26f..1e1fa0d 100644 --- a/Doc/c-api/concrete.rst +++ b/Doc/c-api/concrete.rst @@ -2401,12 +2401,12 @@ change in future releases of Python. :ctype:`PyFileObject`. -.. cfunction:: PyFile_FromFd(int fd, char *name, char *mode, int buffering, char *encoding, char *newline) +.. cfunction:: PyFile_FromFd(int fd, char *name, char *mode, int buffering, char *encoding, char *newline, int closefd) Create a new :ctype:`PyFileObject` from the file descriptor of an already opened file *fd*. The arguments *name*, *encoding* and *newline* can be - *NULL* as well as buffering can be *-1* to use the defaults. Return *NULL* on - failure. + *NULL* to use the defaults; *buffering* can be *-1* to use the default. + Return *NULL* on failure. .. warning:: diff --git a/Include/fileobject.h b/Include/fileobject.h index acb8c6d..ae127da 100644 --- a/Include/fileobject.h +++ b/Include/fileobject.h @@ -8,7 +8,8 @@ extern "C" { #define PY_STDIOTEXTMODE "b" -PyAPI_FUNC(PyObject *) PyFile_FromFd(int, char *, char *, int, char *, char *); +PyAPI_FUNC(PyObject *) PyFile_FromFd(int, char *, char *, int, char *, char *, + int); PyAPI_FUNC(PyObject *) PyFile_GetLine(PyObject *, int); PyAPI_FUNC(int) PyFile_WriteObject(PyObject *, PyObject *, int); PyAPI_FUNC(int) PyFile_WriteString(const char *, PyObject *); @@ -49,7 +49,8 @@ class BlockingIOError(IOError): self.characters_written = characters_written -def open(file, mode="r", buffering=None, encoding=None, newline=None): +def open(file, mode="r", buffering=None, encoding=None, newline=None, + closefd=True): r"""Replacement for the built-in open function. Args: @@ -81,9 +82,12 @@ def open(file, mode="r", buffering=None, encoding=None, newline=None): other legal values, any `'\n'` characters written are translated to the given string. + closefd: optional argument to keep the underlying file descriptor + open when the file is closed. It must not be false when + a filename is given. + (*) If a file descriptor is given, it is closed when the returned - I/O object is closed. If you don't want this to happen, use - os.dup() to create a duplicate file descriptor. + I/O object is closed, unless closefd=False is give. Mode strings characters: 'r': open for reading (default) @@ -138,7 +142,8 @@ def open(file, mode="r", buffering=None, encoding=None, newline=None): (reading and "r" or "") + (writing and "w" or "") + (appending and "a" or "") + - (updating and "+" or "")) + (updating and "+" or ""), + closefd) if buffering is None: buffering = -1 if buffering < 0 and raw.isatty(): diff --git a/Lib/quopri.py b/Lib/quopri.py index 62c0503..6b3d13e 100755 --- a/Lib/quopri.py +++ b/Lib/quopri.py @@ -227,12 +227,14 @@ def main(): sys.stderr.write("%s: can't open (%s)\n" % (file, msg)) sts = 1 continue - if deco: - decode(fp, sys.stdout.buffer) - else: - encode(fp, sys.stdout.buffer, tabs) - if fp is not sys.stdin: - fp.close() + try: + if deco: + decode(fp, sys.stdout.buffer) + else: + encode(fp, sys.stdout.buffer, tabs) + finally: + if file != '-': + fp.close() if sts: sys.exit(sts) diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index e826ff4..9d4163e 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -259,6 +259,9 @@ class IOTest(unittest.TestCase): self.assertEqual(f.write(a), n) f.close() + def test_closefd(self): + self.assertRaises(ValueError, io.open, test_support.TESTFN, 'w', + closefd=False) class MemorySeekTestMixin: @@ -28,6 +28,9 @@ Core and Builtins with `Py_FileSystemDefaultEncoding` and a new API method `PyUnicode_DecodeFSDefault(char*)` was added. +- io.open() and _fileio.FileIO have grown a new argument closefd. A false + value disables the closing of the file descriptor. + Extension Modules ----------------- diff --git a/Modules/_fileio.c b/Modules/_fileio.c index bc707e8..8469bb2 100644 --- a/Modules/_fileio.c +++ b/Modules/_fileio.c @@ -33,6 +33,7 @@ typedef struct { unsigned readable : 1; unsigned writable : 1; int seekable : 2; /* -1 means unknown */ + int closefd : 1; PyObject *weakreflist; } PyFileIOObject; @@ -59,6 +60,13 @@ internal_close(PyFileIOObject *self) static PyObject * fileio_close(PyFileIOObject *self) { + if (!self->closefd) { + if (PyErr_WarnEx(PyExc_RuntimeWarning, + "Trying to close unclosable fd!", 3) < 0) { + return NULL; + } + Py_RETURN_NONE; + } errno = internal_close(self); if (errno < 0) { PyErr_SetFromErrno(PyExc_IOError); @@ -119,7 +127,7 @@ static int fileio_init(PyObject *oself, PyObject *args, PyObject *kwds) { PyFileIOObject *self = (PyFileIOObject *) oself; - static char *kwlist[] = {"file", "mode", NULL}; + static char *kwlist[] = {"file", "mode", "closefd", NULL}; char *name = NULL; char *mode = "r"; char *s; @@ -130,6 +138,7 @@ fileio_init(PyObject *oself, PyObject *args, PyObject *kwds) int rwa = 0, plus = 0, append = 0; int flags = 0; int fd = -1; + int closefd = 1; assert(PyFileIO_Check(oself)); if (self->fd >= 0) { @@ -138,8 +147,8 @@ fileio_init(PyObject *oself, PyObject *args, PyObject *kwds) return -1; } - if (PyArg_ParseTupleAndKeywords(args, kwds, "i|s:fileio", - kwlist, &fd, &mode)) { + if (PyArg_ParseTupleAndKeywords(args, kwds, "i|si:fileio", + kwlist, &fd, &mode, &closefd)) { if (fd < 0) { PyErr_SetString(PyExc_ValueError, "Negative filedescriptor"); @@ -153,8 +162,9 @@ fileio_init(PyObject *oself, PyObject *args, PyObject *kwds) if (GetVersion() < 0x80000000) { /* On NT, so wide API available */ PyObject *po; - if (PyArg_ParseTupleAndKeywords(args, kwds, "U|s:fileio", - kwlist, &po, &mode)) { + if (PyArg_ParseTupleAndKeywords(args, kwds, "U|si:fileio", + kwlist, &po, &mode, &closefd) + ) { widename = PyUnicode_AS_UNICODE(po); } else { /* Drop the argument parsing error as narrow @@ -162,13 +172,13 @@ fileio_init(PyObject *oself, PyObject *args, PyObject *kwds) PyErr_Clear(); } } - if (widename == NULL) + if (widename == NULL) #endif { - if (!PyArg_ParseTupleAndKeywords(args, kwds, "et|s:fileio", + if (!PyArg_ParseTupleAndKeywords(args, kwds, "et|si:fileio", kwlist, Py_FileSystemDefaultEncoding, - &name, &mode)) + &name, &mode, &closefd)) goto error; } } @@ -237,8 +247,16 @@ fileio_init(PyObject *oself, PyObject *args, PyObject *kwds) if (fd >= 0) { self->fd = fd; + self->closefd = closefd; } else { + self->closefd = 1; + if (!closefd) { + PyErr_SetString(PyExc_ValueError, + "Cannot use closefd=True with file name"); + goto error; + } + Py_BEGIN_ALLOW_THREADS errno = 0; #ifdef MS_WINDOWS @@ -270,7 +288,7 @@ fileio_dealloc(PyFileIOObject *self) if (self->weakreflist != NULL) PyObject_ClearWeakRefs((PyObject *) self); - if (self->fd >= 0) { + if (self->fd >= 0 && self->closefd) { errno = internal_close(self); if (errno < 0) { #ifdef HAVE_STRERROR diff --git a/Objects/fileobject.c b/Objects/fileobject.c index 9f63814..4e18480 100644 --- a/Objects/fileobject.c +++ b/Objects/fileobject.c @@ -27,15 +27,15 @@ extern "C" { PyObject * PyFile_FromFd(int fd, char *name, char *mode, int buffering, char *encoding, - char *newline) + char *newline, int closefd) { PyObject *io, *stream, *nameobj = NULL; io = PyImport_ImportModule("io"); if (io == NULL) return NULL; - stream = PyObject_CallMethod(io, "open", "isiss", fd, mode, - buffering, encoding, newline); + stream = PyObject_CallMethod(io, "open", "isissi", fd, mode, + buffering, encoding, newline, closefd); Py_DECREF(io); if (stream == NULL) return NULL; diff --git a/Python/import.c b/Python/import.c index 2493554..be456f1 100644 --- a/Python/import.c +++ b/Python/import.c @@ -2588,7 +2588,7 @@ call_find_module(char *name, PyObject *path) (char*)PyUnicode_GetDefaultEncoding(); } fob = PyFile_FromFd(fd, pathname, fdp->mode, -1, - (char*)encoding, NULL); + (char*)encoding, NULL, 1); if (fob == NULL) { close(fd); PyMem_FREE(found_encoding); diff --git a/Python/pythonrun.c b/Python/pythonrun.c index 330667a..76da8fb 100644 --- a/Python/pythonrun.c +++ b/Python/pythonrun.c @@ -720,7 +720,7 @@ initstdio(void) /* Set sys.stdin */ if (!(std = PyFile_FromFd(fileno(stdin), "<stdin>", "r", -1, - NULL, "\n"))) { + NULL, "\n", 0))) { goto error; } PySys_SetObject("__stdin__", std); @@ -729,16 +729,16 @@ initstdio(void) /* Set sys.stdout */ if (!(std = PyFile_FromFd(fileno(stdout), "<stdout>", "w", -1, - NULL, "\n"))) { + NULL, "\n", 0))) { goto error; } PySys_SetObject("__stdout__", std); PySys_SetObject("stdout", std); Py_DECREF(std); - /* Set sys.stderr */ + /* Set sys.stderr, replaces the preliminary stderr */ if (!(std = PyFile_FromFd(fileno(stderr), "<stderr>", "w", -1, - NULL, "\n"))) { + NULL, "\n", 0))) { goto error; } PySys_SetObject("__stderr__", std); |