Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 59142db

Browse files
committed
Issue #12797: Added custom opener parameter to builtin open() and FileIO.open().
1 parent ab06e3f commit 59142db

7 files changed

Lines changed: 89 additions & 22 deletions

File tree

Doc/library/functions.rst

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -776,7 +776,7 @@ are always available. They are listed here in alphabetical order.
776776
:meth:`__index__` method that returns an integer.
777777

778778

779-
.. function:: open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True)
779+
.. function:: open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
780780

781781
Open *file* and return a corresponding stream. If the file cannot be opened,
782782
an :exc:`OSError` is raised.
@@ -883,6 +883,15 @@ are always available. They are listed here in alphabetical order.
883883
closed. If a filename is given *closefd* has no effect and must be ``True``
884884
(the default).
885885

886+
A custom opener can be used by passing a callable as *opener*. The underlying
887+
file descriptor for the file object is then obtained by calling *opener* with
888+
(*file*, *flags*). *opener* must return an open file descriptor (passing
889+
:mod:`os.open` as *opener* results in functionality similar to passing
890+
``None``).
891+
892+
.. versionchanged:: 3.3
893+
The *opener* parameter was added.
894+
886895
The type of file object returned by the :func:`open` function depends on the
887896
mode. When :func:`open` is used to open a file in a text mode (``'w'``,
888897
``'r'``, ``'wt'``, ``'rt'``, etc.), it returns a subclass of

Doc/library/io.rst

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -458,7 +458,7 @@ I/O Base Classes
458458
Raw File I/O
459459
^^^^^^^^^^^^
460460

461-
.. class:: FileIO(name, mode='r', closefd=True)
461+
.. class:: FileIO(name, mode='r', closefd=True, opener=None)
462462

463463
:class:`FileIO` represents an OS-level file containing bytes data.
464464
It implements the :class:`RawIOBase` interface (and therefore the
@@ -479,6 +479,15 @@ Raw File I/O
479479
The :meth:`read` (when called with a positive argument), :meth:`readinto`
480480
and :meth:`write` methods on this class will only make one system call.
481481

482+
A custom opener can be used by passing a callable as *opener*. The underlying
483+
file descriptor for the file object is then obtained by calling *opener* with
484+
(*name*, *flags*). *opener* must return an open file descriptor (passing
485+
:mod:`os.open` as *opener* results in functionality similar to passing
486+
``None``).
487+
488+
.. versionchanged:: 3.3
489+
The *opener* parameter was added.
490+
482491
In addition to the attributes and methods from :class:`IOBase` and
483492
:class:`RawIOBase`, :class:`FileIO` provides the following data
484493
attributes and methods:

Lib/_pyio.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727

2828

2929
def open(file, mode="r", buffering=-1, encoding=None, errors=None,
30-
newline=None, closefd=True):
30+
newline=None, closefd=True, opener=None):
3131

3232
r"""Open file and return a stream. Raise IOError upon failure.
3333
@@ -122,6 +122,12 @@ def open(file, mode="r", buffering=-1, encoding=None, errors=None,
122122
be kept open when the file is closed. This does not work when a file name is
123123
given and must be True in that case.
124124
125+
A custom opener can be used by passing a callable as *opener*. The
126+
underlying file descriptor for the file object is then obtained by calling
127+
*opener* with (*file*, *flags*). *opener* must return an open file
128+
descriptor (passing os.open as *opener* results in functionality similar to
129+
passing None).
130+
125131
open() returns a file object whose type depends on the mode, and
126132
through which the standard file operations such as reading and writing
127133
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,
176182
(writing and "w" or "") +
177183
(appending and "a" or "") +
178184
(updating and "+" or ""),
179-
closefd)
185+
closefd, opener=opener)
180186
line_buffering = False
181187
if buffering == 1 or buffering < 0 and raw.isatty():
182188
buffering = -1

Lib/test/test_io.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -621,6 +621,15 @@ def test_types_have_dict(self):
621621
for obj in test:
622622
self.assertTrue(hasattr(obj, "__dict__"))
623623

624+
def test_opener(self):
625+
with self.open(support.TESTFN, "w") as f:
626+
f.write("egg\n")
627+
fd = os.open(support.TESTFN, os.O_RDONLY)
628+
def opener(path, flags):
629+
return fd
630+
with self.open("non-existent", "r", opener=opener) as f:
631+
self.assertEqual(f.read(), "egg\n")
632+
624633
class CIOTest(IOTest):
625634

626635
def test_IOBase_finalize(self):

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ What's New in Python 3.3 Alpha 1?
1010
Core and Builtins
1111
-----------------
1212

13+
- Issue #12797: Added custom opener parameter to builtin open() and
14+
FileIO.open().
15+
1316
- Issue #10519: Avoid unnecessary recursive function calls in
1417
setobject.c.
1518

Modules/_io/_iomodule.c

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ PyDoc_STRVAR(module_doc,
9595
*/
9696
PyDoc_STRVAR(open_doc,
9797
"open(file, mode='r', buffering=-1, encoding=None,\n"
98-
" errors=None, newline=None, closefd=True) -> file object\n"
98+
" errors=None, newline=None, closefd=True, opener=None) -> file object\n"
9999
"\n"
100100
"Open file and return a stream. Raise IOError upon failure.\n"
101101
"\n"
@@ -190,6 +190,12 @@ PyDoc_STRVAR(open_doc,
190190
"when the file is closed. This does not work when a file name is given\n"
191191
"and must be True in that case.\n"
192192
"\n"
193+
"A custom opener can be used by passing a callable as *opener*. The\n"
194+
"underlying file descriptor for the file object is then obtained by\n"
195+
"calling *opener* with (*file*, *flags*). *opener* must return an open\n"
196+
"file descriptor (passing os.open as *opener* results in functionality\n"
197+
"similar to passing None).\n"
198+
"\n"
193199
"open() returns a file object whose type depends on the mode, and\n"
194200
"through which the standard file operations such as reading and writing\n"
195201
"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)
210216
{
211217
char *kwlist[] = {"file", "mode", "buffering",
212218
"encoding", "errors", "newline",
213-
"closefd", NULL};
214-
PyObject *file;
219+
"closefd", "opener", NULL};
220+
PyObject *file, *opener = Py_None;
215221
char *mode = "r";
216222
int buffering = -1, closefd = 1;
217223
char *encoding = NULL, *errors = NULL, *newline = NULL;
@@ -228,10 +234,10 @@ io_open(PyObject *self, PyObject *args, PyObject *kwds)
228234
_Py_IDENTIFIER(isatty);
229235
_Py_IDENTIFIER(fileno);
230236

231-
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|sizzzi:open", kwlist,
237+
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|sizzziO:open", kwlist,
232238
&file, &mode, &buffering,
233239
&encoding, &errors, &newline,
234-
&closefd)) {
240+
&closefd, &opener)) {
235241
return NULL;
236242
}
237243

@@ -331,7 +337,7 @@ io_open(PyObject *self, PyObject *args, PyObject *kwds)
331337

332338
/* Create the Raw file stream */
333339
raw = PyObject_CallFunction((PyObject *)&PyFileIO_Type,
334-
"Osi", file, rawmode, closefd);
340+
"OsiO", file, rawmode, closefd, opener);
335341
if (raw == NULL)
336342
return NULL;
337343

Modules/_io/fileio.c

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -212,9 +212,9 @@ static int
212212
fileio_init(PyObject *oself, PyObject *args, PyObject *kwds)
213213
{
214214
fileio *self = (fileio *) oself;
215-
static char *kwlist[] = {"file", "mode", "closefd", NULL};
215+
static char *kwlist[] = {"file", "mode", "closefd", "opener", NULL};
216216
const char *name = NULL;
217-
PyObject *nameobj, *stringobj = NULL;
217+
PyObject *nameobj, *stringobj = NULL, *opener = Py_None;
218218
char *mode = "r";
219219
char *s;
220220
#ifdef MS_WINDOWS
@@ -233,8 +233,9 @@ fileio_init(PyObject *oself, PyObject *args, PyObject *kwds)
233233
return -1;
234234
}
235235

236-
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|si:fileio",
237-
kwlist, &nameobj, &mode, &closefd))
236+
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|siO:fileio",
237+
kwlist, &nameobj, &mode, &closefd,
238+
&opener))
238239
return -1;
239240

240241
if (PyFloat_Check(nameobj)) {
@@ -363,15 +364,35 @@ fileio_init(PyObject *oself, PyObject *args, PyObject *kwds)
363364
goto error;
364365
}
365366

366-
Py_BEGIN_ALLOW_THREADS
367367
errno = 0;
368+
if (opener == Py_None) {
369+
Py_BEGIN_ALLOW_THREADS
368370
#ifdef MS_WINDOWS
369-
if (widename != NULL)
370-
self->fd = _wopen(widename, flags, 0666);
371-
else
371+
if (widename != NULL)
372+
self->fd = _wopen(widename, flags, 0666);
373+
else
372374
#endif
373-
self->fd = open(name, flags, 0666);
374-
Py_END_ALLOW_THREADS
375+
self->fd = open(name, flags, 0666);
376+
Py_END_ALLOW_THREADS
377+
} else {
378+
PyObject *fdobj = PyObject_CallFunction(
379+
opener, "Oi", nameobj, flags);
380+
if (fdobj == NULL)
381+
goto error;
382+
if (!PyLong_Check(fdobj)) {
383+
Py_DECREF(fdobj);
384+
PyErr_SetString(PyExc_TypeError,
385+
"expected integer from opener");
386+
goto error;
387+
}
388+
389+
self->fd = PyLong_AsLong(fdobj);
390+
Py_DECREF(fdobj);
391+
if (self->fd == -1) {
392+
goto error;
393+
}
394+
}
395+
375396
if (self->fd < 0) {
376397
#ifdef MS_WINDOWS
377398
if (widename != NULL)
@@ -1017,13 +1038,17 @@ fileio_getstate(fileio *self)
10171038

10181039

10191040
PyDoc_STRVAR(fileio_doc,
1020-
"file(name: str[, mode: str]) -> file IO object\n"
1041+
"file(name: str[, mode: str][, opener: None]) -> file IO object\n"
10211042
"\n"
10221043
"Open a file. The mode can be 'r', 'w' or 'a' for reading (default),\n"
10231044
"writing or appending. The file will be created if it doesn't exist\n"
10241045
"when opened for writing or appending; it will be truncated when\n"
10251046
"opened for writing. Add a '+' to the mode to allow simultaneous\n"
1026-
"reading and writing.");
1047+
"reading and writing. A custom opener can be used by passing a\n"
1048+
"callable as *opener*. The underlying file descriptor for the file\n"
1049+
"object is then obtained by calling opener with (*name*, *flags*).\n"
1050+
"*opener* must return an open file descriptor (passing os.open as\n"
1051+
"*opener* results in functionality similar to passing None).");
10271052

10281053
PyDoc_STRVAR(read_doc,
10291054
"read(size: int) -> bytes. read at most size bytes, returned as bytes.\n"

0 commit comments

Comments
 (0)