]> granicus.if.org Git - python/commitdiff
Patch 1329 (partial) by Christian Heimes.
authorGuido van Rossum <guido@python.org>
Tue, 30 Oct 2007 17:27:30 +0000 (17:27 +0000)
committerGuido van Rossum <guido@python.org>
Tue, 30 Oct 2007 17:27:30 +0000 (17:27 +0000)
Add a closefd flag to open() which can be set to False to prevent closing
the file descriptor when close() is called or when the object is destroyed.
Useful to ensure that sys.std{in,out,err} keep their file descriptors open
when Python is uninitialized.  (This was always a feature in 2.x, it just
wasn't implemented in 3.0 yet.)

Doc/c-api/concrete.rst
Include/fileobject.h
Lib/io.py
Lib/quopri.py
Lib/test/test_io.py
Misc/NEWS
Modules/_fileio.c
Objects/fileobject.c
Python/import.c
Python/pythonrun.c

index cdea26f1bce76608d8359b065f7d25ccf00e84ac..1e1fa0dccf15773d067b97c2a99c481e01d50d39 100644 (file)
@@ -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::
 
index acb8c6ddc19c056947e531b423e6c887a309c868..ae127da0c78df83498740c67bc54ba105023f7ac 100644 (file)
@@ -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 *);
index c2d803ccfedee3cfa897b22b027fb569b1e7e1c3..d7709c45941dbf1e3550fcd5458e3d8051e5ab0f 100644 (file)
--- a/Lib/io.py
+++ b/Lib/io.py
@@ -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():
index 62c0503877cc903807d7ef7fb50e68b96cae6ba2..6b3d13eca451d9008e735a75c19ebb648013b94e 100755 (executable)
@@ -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)
 
index e826ff4b3691dd9d3e360029a94691e7657e2391..9d4163edc1d9fb5ce738b2f932365b06eb6c7809 100644 (file)
@@ -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:
 
index 4ec628f7bcdc2e5e3770dfb303b42c4e34b54338..41b8ab12b98ff2eaf801398892b163a59f4bd9b9 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -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
 -----------------
 
index bc707e85968c23ab326767f961176d1c91a6b000..8469bb248f27ac1fc4b6e4df94c69c915e78f4b8 100644 (file)
@@ -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
index 9f63814ba18b5f408f01e09387a1e804c11146e7..4e18480b31279928d82b1e1949737194b890c950 100644 (file)
@@ -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;
index 24935547e6d5737570454e32a99e460af803b42c..be456f1b50dc9b9f7c3cfc29728ca89ecc9d3b1e 100644 (file)
@@ -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);
index 330667a219b7710782683341c0789ca2aea53f40..76da8fb7f772ccccd3182509f8500cb628a3449a 100644 (file)
@@ -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);