Kaydet (Commit) 2dced8b6 authored tarafından Guido van Rossum's avatar Guido van Rossum

Patch 1329 (partial) by Christian Heimes.

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.)
üst 2673a572
...@@ -2401,12 +2401,12 @@ change in future releases of Python. ...@@ -2401,12 +2401,12 @@ change in future releases of Python.
:ctype:`PyFileObject`. :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 Create a new :ctype:`PyFileObject` from the file descriptor of an already
opened file *fd*. The arguments *name*, *encoding* and *newline* can be 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 *NULL* to use the defaults; *buffering* can be *-1* to use the default.
failure. Return *NULL* on failure.
.. warning:: .. warning::
......
...@@ -8,7 +8,8 @@ extern "C" { ...@@ -8,7 +8,8 @@ extern "C" {
#define PY_STDIOTEXTMODE "b" #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(PyObject *) PyFile_GetLine(PyObject *, int);
PyAPI_FUNC(int) PyFile_WriteObject(PyObject *, PyObject *, int); PyAPI_FUNC(int) PyFile_WriteObject(PyObject *, PyObject *, int);
PyAPI_FUNC(int) PyFile_WriteString(const char *, PyObject *); PyAPI_FUNC(int) PyFile_WriteString(const char *, PyObject *);
......
...@@ -49,7 +49,8 @@ class BlockingIOError(IOError): ...@@ -49,7 +49,8 @@ class BlockingIOError(IOError):
self.characters_written = characters_written 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. r"""Replacement for the built-in open function.
Args: Args:
...@@ -81,9 +82,12 @@ def open(file, mode="r", buffering=None, encoding=None, newline=None): ...@@ -81,9 +82,12 @@ def open(file, mode="r", buffering=None, encoding=None, newline=None):
other legal values, any `'\n'` characters written are other legal values, any `'\n'` characters written are
translated to the given string. 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 (*) 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 I/O object is closed, unless closefd=False is give.
os.dup() to create a duplicate file descriptor.
Mode strings characters: Mode strings characters:
'r': open for reading (default) 'r': open for reading (default)
...@@ -138,7 +142,8 @@ def open(file, mode="r", buffering=None, encoding=None, newline=None): ...@@ -138,7 +142,8 @@ def open(file, mode="r", buffering=None, encoding=None, newline=None):
(reading and "r" or "") + (reading and "r" or "") +
(writing and "w" or "") + (writing and "w" or "") +
(appending and "a" or "") + (appending and "a" or "") +
(updating and "+" or "")) (updating and "+" or ""),
closefd)
if buffering is None: if buffering is None:
buffering = -1 buffering = -1
if buffering < 0 and raw.isatty(): if buffering < 0 and raw.isatty():
......
...@@ -227,12 +227,14 @@ def main(): ...@@ -227,12 +227,14 @@ def main():
sys.stderr.write("%s: can't open (%s)\n" % (file, msg)) sys.stderr.write("%s: can't open (%s)\n" % (file, msg))
sts = 1 sts = 1
continue continue
if deco: try:
decode(fp, sys.stdout.buffer) if deco:
else: decode(fp, sys.stdout.buffer)
encode(fp, sys.stdout.buffer, tabs) else:
if fp is not sys.stdin: encode(fp, sys.stdout.buffer, tabs)
fp.close() finally:
if file != '-':
fp.close()
if sts: if sts:
sys.exit(sts) sys.exit(sts)
......
...@@ -259,6 +259,9 @@ class IOTest(unittest.TestCase): ...@@ -259,6 +259,9 @@ class IOTest(unittest.TestCase):
self.assertEqual(f.write(a), n) self.assertEqual(f.write(a), n)
f.close() f.close()
def test_closefd(self):
self.assertRaises(ValueError, io.open, test_support.TESTFN, 'w',
closefd=False)
class MemorySeekTestMixin: class MemorySeekTestMixin:
......
...@@ -28,6 +28,9 @@ Core and Builtins ...@@ -28,6 +28,9 @@ Core and Builtins
with `Py_FileSystemDefaultEncoding` and a new API method with `Py_FileSystemDefaultEncoding` and a new API method
`PyUnicode_DecodeFSDefault(char*)` was added. `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 Extension Modules
----------------- -----------------
......
...@@ -33,6 +33,7 @@ typedef struct { ...@@ -33,6 +33,7 @@ typedef struct {
unsigned readable : 1; unsigned readable : 1;
unsigned writable : 1; unsigned writable : 1;
int seekable : 2; /* -1 means unknown */ int seekable : 2; /* -1 means unknown */
int closefd : 1;
PyObject *weakreflist; PyObject *weakreflist;
} PyFileIOObject; } PyFileIOObject;
...@@ -59,6 +60,13 @@ internal_close(PyFileIOObject *self) ...@@ -59,6 +60,13 @@ internal_close(PyFileIOObject *self)
static PyObject * static PyObject *
fileio_close(PyFileIOObject *self) 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); errno = internal_close(self);
if (errno < 0) { if (errno < 0) {
PyErr_SetFromErrno(PyExc_IOError); PyErr_SetFromErrno(PyExc_IOError);
...@@ -119,7 +127,7 @@ static int ...@@ -119,7 +127,7 @@ static int
fileio_init(PyObject *oself, PyObject *args, PyObject *kwds) fileio_init(PyObject *oself, PyObject *args, PyObject *kwds)
{ {
PyFileIOObject *self = (PyFileIOObject *) oself; PyFileIOObject *self = (PyFileIOObject *) oself;
static char *kwlist[] = {"file", "mode", NULL}; static char *kwlist[] = {"file", "mode", "closefd", NULL};
char *name = NULL; char *name = NULL;
char *mode = "r"; char *mode = "r";
char *s; char *s;
...@@ -130,6 +138,7 @@ fileio_init(PyObject *oself, PyObject *args, PyObject *kwds) ...@@ -130,6 +138,7 @@ fileio_init(PyObject *oself, PyObject *args, PyObject *kwds)
int rwa = 0, plus = 0, append = 0; int rwa = 0, plus = 0, append = 0;
int flags = 0; int flags = 0;
int fd = -1; int fd = -1;
int closefd = 1;
assert(PyFileIO_Check(oself)); assert(PyFileIO_Check(oself));
if (self->fd >= 0) { if (self->fd >= 0) {
...@@ -138,8 +147,8 @@ fileio_init(PyObject *oself, PyObject *args, PyObject *kwds) ...@@ -138,8 +147,8 @@ fileio_init(PyObject *oself, PyObject *args, PyObject *kwds)
return -1; return -1;
} }
if (PyArg_ParseTupleAndKeywords(args, kwds, "i|s:fileio", if (PyArg_ParseTupleAndKeywords(args, kwds, "i|si:fileio",
kwlist, &fd, &mode)) { kwlist, &fd, &mode, &closefd)) {
if (fd < 0) { if (fd < 0) {
PyErr_SetString(PyExc_ValueError, PyErr_SetString(PyExc_ValueError,
"Negative filedescriptor"); "Negative filedescriptor");
...@@ -153,8 +162,9 @@ fileio_init(PyObject *oself, PyObject *args, PyObject *kwds) ...@@ -153,8 +162,9 @@ fileio_init(PyObject *oself, PyObject *args, PyObject *kwds)
if (GetVersion() < 0x80000000) { if (GetVersion() < 0x80000000) {
/* On NT, so wide API available */ /* On NT, so wide API available */
PyObject *po; PyObject *po;
if (PyArg_ParseTupleAndKeywords(args, kwds, "U|s:fileio", if (PyArg_ParseTupleAndKeywords(args, kwds, "U|si:fileio",
kwlist, &po, &mode)) { kwlist, &po, &mode, &closefd)
) {
widename = PyUnicode_AS_UNICODE(po); widename = PyUnicode_AS_UNICODE(po);
} else { } else {
/* Drop the argument parsing error as narrow /* Drop the argument parsing error as narrow
...@@ -162,13 +172,13 @@ fileio_init(PyObject *oself, PyObject *args, PyObject *kwds) ...@@ -162,13 +172,13 @@ fileio_init(PyObject *oself, PyObject *args, PyObject *kwds)
PyErr_Clear(); PyErr_Clear();
} }
} }
if (widename == NULL) if (widename == NULL)
#endif #endif
{ {
if (!PyArg_ParseTupleAndKeywords(args, kwds, "et|s:fileio", if (!PyArg_ParseTupleAndKeywords(args, kwds, "et|si:fileio",
kwlist, kwlist,
Py_FileSystemDefaultEncoding, Py_FileSystemDefaultEncoding,
&name, &mode)) &name, &mode, &closefd))
goto error; goto error;
} }
} }
...@@ -237,8 +247,16 @@ fileio_init(PyObject *oself, PyObject *args, PyObject *kwds) ...@@ -237,8 +247,16 @@ fileio_init(PyObject *oself, PyObject *args, PyObject *kwds)
if (fd >= 0) { if (fd >= 0) {
self->fd = fd; self->fd = fd;
self->closefd = closefd;
} }
else { else {
self->closefd = 1;
if (!closefd) {
PyErr_SetString(PyExc_ValueError,
"Cannot use closefd=True with file name");
goto error;
}
Py_BEGIN_ALLOW_THREADS Py_BEGIN_ALLOW_THREADS
errno = 0; errno = 0;
#ifdef MS_WINDOWS #ifdef MS_WINDOWS
...@@ -270,7 +288,7 @@ fileio_dealloc(PyFileIOObject *self) ...@@ -270,7 +288,7 @@ fileio_dealloc(PyFileIOObject *self)
if (self->weakreflist != NULL) if (self->weakreflist != NULL)
PyObject_ClearWeakRefs((PyObject *) self); PyObject_ClearWeakRefs((PyObject *) self);
if (self->fd >= 0) { if (self->fd >= 0 && self->closefd) {
errno = internal_close(self); errno = internal_close(self);
if (errno < 0) { if (errno < 0) {
#ifdef HAVE_STRERROR #ifdef HAVE_STRERROR
......
...@@ -27,15 +27,15 @@ extern "C" { ...@@ -27,15 +27,15 @@ extern "C" {
PyObject * PyObject *
PyFile_FromFd(int fd, char *name, char *mode, int buffering, char *encoding, PyFile_FromFd(int fd, char *name, char *mode, int buffering, char *encoding,
char *newline) char *newline, int closefd)
{ {
PyObject *io, *stream, *nameobj = NULL; PyObject *io, *stream, *nameobj = NULL;
io = PyImport_ImportModule("io"); io = PyImport_ImportModule("io");
if (io == NULL) if (io == NULL)
return NULL; return NULL;
stream = PyObject_CallMethod(io, "open", "isiss", fd, mode, stream = PyObject_CallMethod(io, "open", "isissi", fd, mode,
buffering, encoding, newline); buffering, encoding, newline, closefd);
Py_DECREF(io); Py_DECREF(io);
if (stream == NULL) if (stream == NULL)
return NULL; return NULL;
......
...@@ -2588,7 +2588,7 @@ call_find_module(char *name, PyObject *path) ...@@ -2588,7 +2588,7 @@ call_find_module(char *name, PyObject *path)
(char*)PyUnicode_GetDefaultEncoding(); (char*)PyUnicode_GetDefaultEncoding();
} }
fob = PyFile_FromFd(fd, pathname, fdp->mode, -1, fob = PyFile_FromFd(fd, pathname, fdp->mode, -1,
(char*)encoding, NULL); (char*)encoding, NULL, 1);
if (fob == NULL) { if (fob == NULL) {
close(fd); close(fd);
PyMem_FREE(found_encoding); PyMem_FREE(found_encoding);
......
...@@ -720,7 +720,7 @@ initstdio(void) ...@@ -720,7 +720,7 @@ initstdio(void)
/* Set sys.stdin */ /* Set sys.stdin */
if (!(std = PyFile_FromFd(fileno(stdin), "<stdin>", "r", -1, if (!(std = PyFile_FromFd(fileno(stdin), "<stdin>", "r", -1,
NULL, "\n"))) { NULL, "\n", 0))) {
goto error; goto error;
} }
PySys_SetObject("__stdin__", std); PySys_SetObject("__stdin__", std);
...@@ -729,16 +729,16 @@ initstdio(void) ...@@ -729,16 +729,16 @@ initstdio(void)
/* Set sys.stdout */ /* Set sys.stdout */
if (!(std = PyFile_FromFd(fileno(stdout), "<stdout>", "w", -1, if (!(std = PyFile_FromFd(fileno(stdout), "<stdout>", "w", -1,
NULL, "\n"))) { NULL, "\n", 0))) {
goto error; goto error;
} }
PySys_SetObject("__stdout__", std); PySys_SetObject("__stdout__", std);
PySys_SetObject("stdout", std); PySys_SetObject("stdout", std);
Py_DECREF(std); Py_DECREF(std);
/* Set sys.stderr */ /* Set sys.stderr, replaces the preliminary stderr */
if (!(std = PyFile_FromFd(fileno(stderr), "<stderr>", "w", -1, if (!(std = PyFile_FromFd(fileno(stderr), "<stderr>", "w", -1,
NULL, "\n"))) { NULL, "\n", 0))) {
goto error; goto error;
} }
PySys_SetObject("__stderr__", std); PySys_SetObject("__stderr__", std);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment