mirror of https://github.com/python/cpython
Issue #12797: Added custom opener parameter to builtin open() and FileIO.open().
This commit is contained in:
parent
ab06e3f285
commit
59142db6d3
|
@ -776,7 +776,7 @@ are always available. They are listed here in alphabetical order.
|
||||||
:meth:`__index__` method that returns an integer.
|
:meth:`__index__` method that returns an integer.
|
||||||
|
|
||||||
|
|
||||||
.. function:: open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True)
|
.. function:: open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
|
||||||
|
|
||||||
Open *file* and return a corresponding stream. If the file cannot be opened,
|
Open *file* and return a corresponding stream. If the file cannot be opened,
|
||||||
an :exc:`OSError` is raised.
|
an :exc:`OSError` is raised.
|
||||||
|
@ -883,6 +883,15 @@ are always available. They are listed here in alphabetical order.
|
||||||
closed. If a filename is given *closefd* has no effect and must be ``True``
|
closed. If a filename is given *closefd* has no effect and must be ``True``
|
||||||
(the default).
|
(the default).
|
||||||
|
|
||||||
|
A custom opener can be used by passing a callable as *opener*. The underlying
|
||||||
|
file descriptor for the file object is then obtained by calling *opener* with
|
||||||
|
(*file*, *flags*). *opener* must return an open file descriptor (passing
|
||||||
|
:mod:`os.open` as *opener* results in functionality similar to passing
|
||||||
|
``None``).
|
||||||
|
|
||||||
|
.. versionchanged:: 3.3
|
||||||
|
The *opener* parameter was added.
|
||||||
|
|
||||||
The type of file object returned by the :func:`open` function depends on the
|
The type of file object returned by the :func:`open` function depends on the
|
||||||
mode. When :func:`open` is used to open a file in a text mode (``'w'``,
|
mode. When :func:`open` is used to open a file in a text mode (``'w'``,
|
||||||
``'r'``, ``'wt'``, ``'rt'``, etc.), it returns a subclass of
|
``'r'``, ``'wt'``, ``'rt'``, etc.), it returns a subclass of
|
||||||
|
|
|
@ -458,7 +458,7 @@ I/O Base Classes
|
||||||
Raw File I/O
|
Raw File I/O
|
||||||
^^^^^^^^^^^^
|
^^^^^^^^^^^^
|
||||||
|
|
||||||
.. class:: FileIO(name, mode='r', closefd=True)
|
.. class:: FileIO(name, mode='r', closefd=True, opener=None)
|
||||||
|
|
||||||
:class:`FileIO` represents an OS-level file containing bytes data.
|
:class:`FileIO` represents an OS-level file containing bytes data.
|
||||||
It implements the :class:`RawIOBase` interface (and therefore the
|
It implements the :class:`RawIOBase` interface (and therefore the
|
||||||
|
@ -479,6 +479,15 @@ Raw File I/O
|
||||||
The :meth:`read` (when called with a positive argument), :meth:`readinto`
|
The :meth:`read` (when called with a positive argument), :meth:`readinto`
|
||||||
and :meth:`write` methods on this class will only make one system call.
|
and :meth:`write` methods on this class will only make one system call.
|
||||||
|
|
||||||
|
A custom opener can be used by passing a callable as *opener*. The underlying
|
||||||
|
file descriptor for the file object is then obtained by calling *opener* with
|
||||||
|
(*name*, *flags*). *opener* must return an open file descriptor (passing
|
||||||
|
:mod:`os.open` as *opener* results in functionality similar to passing
|
||||||
|
``None``).
|
||||||
|
|
||||||
|
.. versionchanged:: 3.3
|
||||||
|
The *opener* parameter was added.
|
||||||
|
|
||||||
In addition to the attributes and methods from :class:`IOBase` and
|
In addition to the attributes and methods from :class:`IOBase` and
|
||||||
:class:`RawIOBase`, :class:`FileIO` provides the following data
|
:class:`RawIOBase`, :class:`FileIO` provides the following data
|
||||||
attributes and methods:
|
attributes and methods:
|
||||||
|
|
10
Lib/_pyio.py
10
Lib/_pyio.py
|
@ -27,7 +27,7 @@ BlockingIOError = BlockingIOError
|
||||||
|
|
||||||
|
|
||||||
def open(file, mode="r", buffering=-1, encoding=None, errors=None,
|
def open(file, mode="r", buffering=-1, encoding=None, errors=None,
|
||||||
newline=None, closefd=True):
|
newline=None, closefd=True, opener=None):
|
||||||
|
|
||||||
r"""Open file and return a stream. Raise IOError upon failure.
|
r"""Open file and return a stream. Raise IOError upon failure.
|
||||||
|
|
||||||
|
@ -122,6 +122,12 @@ def open(file, mode="r", buffering=-1, encoding=None, errors=None,
|
||||||
be kept open when the file is closed. This does not work when a file name is
|
be kept open when the file is closed. This does not work when a file name is
|
||||||
given and must be True in that case.
|
given and must be True in that case.
|
||||||
|
|
||||||
|
A custom opener can be used by passing a callable as *opener*. The
|
||||||
|
underlying file descriptor for the file object is then obtained by calling
|
||||||
|
*opener* with (*file*, *flags*). *opener* must return an open file
|
||||||
|
descriptor (passing os.open as *opener* results in functionality similar to
|
||||||
|
passing None).
|
||||||
|
|
||||||
open() returns a file object whose type depends on the mode, and
|
open() returns a file object whose type depends on the mode, and
|
||||||
through which the standard file operations such as reading and writing
|
through which the standard file operations such as reading and writing
|
||||||
are performed. When open() is used to open a file in a text mode ('w',
|
are performed. When open() is used to open a file in a text mode ('w',
|
||||||
|
@ -176,7 +182,7 @@ def open(file, mode="r", buffering=-1, encoding=None, errors=None,
|
||||||
(writing and "w" or "") +
|
(writing and "w" or "") +
|
||||||
(appending and "a" or "") +
|
(appending and "a" or "") +
|
||||||
(updating and "+" or ""),
|
(updating and "+" or ""),
|
||||||
closefd)
|
closefd, opener=opener)
|
||||||
line_buffering = False
|
line_buffering = False
|
||||||
if buffering == 1 or buffering < 0 and raw.isatty():
|
if buffering == 1 or buffering < 0 and raw.isatty():
|
||||||
buffering = -1
|
buffering = -1
|
||||||
|
|
|
@ -621,6 +621,15 @@ class IOTest(unittest.TestCase):
|
||||||
for obj in test:
|
for obj in test:
|
||||||
self.assertTrue(hasattr(obj, "__dict__"))
|
self.assertTrue(hasattr(obj, "__dict__"))
|
||||||
|
|
||||||
|
def test_opener(self):
|
||||||
|
with self.open(support.TESTFN, "w") as f:
|
||||||
|
f.write("egg\n")
|
||||||
|
fd = os.open(support.TESTFN, os.O_RDONLY)
|
||||||
|
def opener(path, flags):
|
||||||
|
return fd
|
||||||
|
with self.open("non-existent", "r", opener=opener) as f:
|
||||||
|
self.assertEqual(f.read(), "egg\n")
|
||||||
|
|
||||||
class CIOTest(IOTest):
|
class CIOTest(IOTest):
|
||||||
|
|
||||||
def test_IOBase_finalize(self):
|
def test_IOBase_finalize(self):
|
||||||
|
|
|
@ -10,6 +10,9 @@ What's New in Python 3.3 Alpha 1?
|
||||||
Core and Builtins
|
Core and Builtins
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|
- Issue #12797: Added custom opener parameter to builtin open() and
|
||||||
|
FileIO.open().
|
||||||
|
|
||||||
- Issue #10519: Avoid unnecessary recursive function calls in
|
- Issue #10519: Avoid unnecessary recursive function calls in
|
||||||
setobject.c.
|
setobject.c.
|
||||||
|
|
||||||
|
|
|
@ -95,7 +95,7 @@ PyDoc_STRVAR(module_doc,
|
||||||
*/
|
*/
|
||||||
PyDoc_STRVAR(open_doc,
|
PyDoc_STRVAR(open_doc,
|
||||||
"open(file, mode='r', buffering=-1, encoding=None,\n"
|
"open(file, mode='r', buffering=-1, encoding=None,\n"
|
||||||
" errors=None, newline=None, closefd=True) -> file object\n"
|
" errors=None, newline=None, closefd=True, opener=None) -> file object\n"
|
||||||
"\n"
|
"\n"
|
||||||
"Open file and return a stream. Raise IOError upon failure.\n"
|
"Open file and return a stream. Raise IOError upon failure.\n"
|
||||||
"\n"
|
"\n"
|
||||||
|
@ -190,6 +190,12 @@ PyDoc_STRVAR(open_doc,
|
||||||
"when the file is closed. This does not work when a file name is given\n"
|
"when the file is closed. This does not work when a file name is given\n"
|
||||||
"and must be True in that case.\n"
|
"and must be True in that case.\n"
|
||||||
"\n"
|
"\n"
|
||||||
|
"A custom opener can be used by passing a callable as *opener*. The\n"
|
||||||
|
"underlying file descriptor for the file object is then obtained by\n"
|
||||||
|
"calling *opener* with (*file*, *flags*). *opener* must return an open\n"
|
||||||
|
"file descriptor (passing os.open as *opener* results in functionality\n"
|
||||||
|
"similar to passing None).\n"
|
||||||
|
"\n"
|
||||||
"open() returns a file object whose type depends on the mode, and\n"
|
"open() returns a file object whose type depends on the mode, and\n"
|
||||||
"through which the standard file operations such as reading and writing\n"
|
"through which the standard file operations such as reading and writing\n"
|
||||||
"are performed. When open() is used to open a file in a text mode ('w',\n"
|
"are performed. When open() is used to open a file in a text mode ('w',\n"
|
||||||
|
@ -210,8 +216,8 @@ io_open(PyObject *self, PyObject *args, PyObject *kwds)
|
||||||
{
|
{
|
||||||
char *kwlist[] = {"file", "mode", "buffering",
|
char *kwlist[] = {"file", "mode", "buffering",
|
||||||
"encoding", "errors", "newline",
|
"encoding", "errors", "newline",
|
||||||
"closefd", NULL};
|
"closefd", "opener", NULL};
|
||||||
PyObject *file;
|
PyObject *file, *opener = Py_None;
|
||||||
char *mode = "r";
|
char *mode = "r";
|
||||||
int buffering = -1, closefd = 1;
|
int buffering = -1, closefd = 1;
|
||||||
char *encoding = NULL, *errors = NULL, *newline = NULL;
|
char *encoding = NULL, *errors = NULL, *newline = NULL;
|
||||||
|
@ -228,10 +234,10 @@ io_open(PyObject *self, PyObject *args, PyObject *kwds)
|
||||||
_Py_IDENTIFIER(isatty);
|
_Py_IDENTIFIER(isatty);
|
||||||
_Py_IDENTIFIER(fileno);
|
_Py_IDENTIFIER(fileno);
|
||||||
|
|
||||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|sizzzi:open", kwlist,
|
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|sizzziO:open", kwlist,
|
||||||
&file, &mode, &buffering,
|
&file, &mode, &buffering,
|
||||||
&encoding, &errors, &newline,
|
&encoding, &errors, &newline,
|
||||||
&closefd)) {
|
&closefd, &opener)) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -331,7 +337,7 @@ io_open(PyObject *self, PyObject *args, PyObject *kwds)
|
||||||
|
|
||||||
/* Create the Raw file stream */
|
/* Create the Raw file stream */
|
||||||
raw = PyObject_CallFunction((PyObject *)&PyFileIO_Type,
|
raw = PyObject_CallFunction((PyObject *)&PyFileIO_Type,
|
||||||
"Osi", file, rawmode, closefd);
|
"OsiO", file, rawmode, closefd, opener);
|
||||||
if (raw == NULL)
|
if (raw == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
|
|
@ -212,9 +212,9 @@ static int
|
||||||
fileio_init(PyObject *oself, PyObject *args, PyObject *kwds)
|
fileio_init(PyObject *oself, PyObject *args, PyObject *kwds)
|
||||||
{
|
{
|
||||||
fileio *self = (fileio *) oself;
|
fileio *self = (fileio *) oself;
|
||||||
static char *kwlist[] = {"file", "mode", "closefd", NULL};
|
static char *kwlist[] = {"file", "mode", "closefd", "opener", NULL};
|
||||||
const char *name = NULL;
|
const char *name = NULL;
|
||||||
PyObject *nameobj, *stringobj = NULL;
|
PyObject *nameobj, *stringobj = NULL, *opener = Py_None;
|
||||||
char *mode = "r";
|
char *mode = "r";
|
||||||
char *s;
|
char *s;
|
||||||
#ifdef MS_WINDOWS
|
#ifdef MS_WINDOWS
|
||||||
|
@ -233,8 +233,9 @@ fileio_init(PyObject *oself, PyObject *args, PyObject *kwds)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|si:fileio",
|
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|siO:fileio",
|
||||||
kwlist, &nameobj, &mode, &closefd))
|
kwlist, &nameobj, &mode, &closefd,
|
||||||
|
&opener))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (PyFloat_Check(nameobj)) {
|
if (PyFloat_Check(nameobj)) {
|
||||||
|
@ -363,15 +364,35 @@ fileio_init(PyObject *oself, PyObject *args, PyObject *kwds)
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
Py_BEGIN_ALLOW_THREADS
|
|
||||||
errno = 0;
|
errno = 0;
|
||||||
|
if (opener == Py_None) {
|
||||||
|
Py_BEGIN_ALLOW_THREADS
|
||||||
#ifdef MS_WINDOWS
|
#ifdef MS_WINDOWS
|
||||||
if (widename != NULL)
|
if (widename != NULL)
|
||||||
self->fd = _wopen(widename, flags, 0666);
|
self->fd = _wopen(widename, flags, 0666);
|
||||||
else
|
else
|
||||||
#endif
|
#endif
|
||||||
self->fd = open(name, flags, 0666);
|
self->fd = open(name, flags, 0666);
|
||||||
Py_END_ALLOW_THREADS
|
Py_END_ALLOW_THREADS
|
||||||
|
} else {
|
||||||
|
PyObject *fdobj = PyObject_CallFunction(
|
||||||
|
opener, "Oi", nameobj, flags);
|
||||||
|
if (fdobj == NULL)
|
||||||
|
goto error;
|
||||||
|
if (!PyLong_Check(fdobj)) {
|
||||||
|
Py_DECREF(fdobj);
|
||||||
|
PyErr_SetString(PyExc_TypeError,
|
||||||
|
"expected integer from opener");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
self->fd = PyLong_AsLong(fdobj);
|
||||||
|
Py_DECREF(fdobj);
|
||||||
|
if (self->fd == -1) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (self->fd < 0) {
|
if (self->fd < 0) {
|
||||||
#ifdef MS_WINDOWS
|
#ifdef MS_WINDOWS
|
||||||
if (widename != NULL)
|
if (widename != NULL)
|
||||||
|
@ -1017,13 +1038,17 @@ fileio_getstate(fileio *self)
|
||||||
|
|
||||||
|
|
||||||
PyDoc_STRVAR(fileio_doc,
|
PyDoc_STRVAR(fileio_doc,
|
||||||
"file(name: str[, mode: str]) -> file IO object\n"
|
"file(name: str[, mode: str][, opener: None]) -> file IO object\n"
|
||||||
"\n"
|
"\n"
|
||||||
"Open a file. The mode can be 'r', 'w' or 'a' for reading (default),\n"
|
"Open a file. The mode can be 'r', 'w' or 'a' for reading (default),\n"
|
||||||
"writing or appending. The file will be created if it doesn't exist\n"
|
"writing or appending. The file will be created if it doesn't exist\n"
|
||||||
"when opened for writing or appending; it will be truncated when\n"
|
"when opened for writing or appending; it will be truncated when\n"
|
||||||
"opened for writing. Add a '+' to the mode to allow simultaneous\n"
|
"opened for writing. Add a '+' to the mode to allow simultaneous\n"
|
||||||
"reading and writing.");
|
"reading and writing. A custom opener can be used by passing a\n"
|
||||||
|
"callable as *opener*. The underlying file descriptor for the file\n"
|
||||||
|
"object is then obtained by calling opener with (*name*, *flags*).\n"
|
||||||
|
"*opener* must return an open file descriptor (passing os.open as\n"
|
||||||
|
"*opener* results in functionality similar to passing None).");
|
||||||
|
|
||||||
PyDoc_STRVAR(read_doc,
|
PyDoc_STRVAR(read_doc,
|
||||||
"read(size: int) -> bytes. read at most size bytes, returned as bytes.\n"
|
"read(size: int) -> bytes. read at most size bytes, returned as bytes.\n"
|
||||||
|
|
Loading…
Reference in New Issue