summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/c-api/concrete.rst6
-rw-r--r--Include/fileobject.h3
-rw-r--r--Lib/io.py13
-rwxr-xr-xLib/quopri.py14
-rw-r--r--Lib/test/test_io.py3
-rw-r--r--Misc/NEWS3
-rw-r--r--Modules/_fileio.c36
-rw-r--r--Objects/fileobject.c6
-rw-r--r--Python/import.c2
-rw-r--r--Python/pythonrun.c8
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 *);
diff --git a/Lib/io.py b/Lib/io.py
index c2d803c..d7709c4 100644
--- 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():
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:
diff --git a/Misc/NEWS b/Misc/NEWS
index 4ec628f..41b8ab1 100644
--- 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
-----------------
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);