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.)
This commit is contained in:
Guido van Rossum 2007-10-30 17:27:30 +00:00
parent 2673a57234
commit 2dced8b602
10 changed files with 63 additions and 31 deletions

View 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::

View 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 *);

View file

@ -49,7 +49,8 @@ def __init__(self, errno, strerror, characters_written=0):
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():

View file

@ -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)

View file

@ -259,6 +259,9 @@ def test_array_writes(self):
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:

View file

@ -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
-----------------

View 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

View 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;

View 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);

View 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);