issue27186: add open/io.open; patch by Jelle Zijlstra
This commit is contained in:
parent
228c636908
commit
d62548afed
|
@ -878,11 +878,11 @@ are always available. They are listed here in alphabetical order.
|
||||||
Open *file* and return a corresponding :term:`file object`. If the file
|
Open *file* and return a corresponding :term:`file object`. If the file
|
||||||
cannot be opened, an :exc:`OSError` is raised.
|
cannot be opened, an :exc:`OSError` is raised.
|
||||||
|
|
||||||
*file* is either a string or bytes object giving the pathname (absolute or
|
*file* is either a string, bytes, or :class:`os.PathLike` object giving the
|
||||||
relative to the current working directory) of the file to be opened or
|
pathname (absolute or relative to the current working directory) of the file
|
||||||
an integer file descriptor of the file to be wrapped. (If a file descriptor
|
to be opened or an integer file descriptor of the file to be wrapped. (If a
|
||||||
is given, it is closed when the returned I/O object is closed, unless
|
file descriptor is given, it is closed when the returned I/O object is
|
||||||
*closefd* is set to ``False``.)
|
closed, unless *closefd* is set to ``False``.)
|
||||||
|
|
||||||
*mode* is an optional string that specifies the mode in which the file is
|
*mode* is an optional string that specifies the mode in which the file is
|
||||||
opened. It defaults to ``'r'`` which means open for reading in text mode.
|
opened. It defaults to ``'r'`` which means open for reading in text mode.
|
||||||
|
|
|
@ -161,6 +161,8 @@ def open(file, mode="r", buffering=-1, encoding=None, errors=None,
|
||||||
opened in a text mode, and for bytes a BytesIO can be used like a file
|
opened in a text mode, and for bytes a BytesIO can be used like a file
|
||||||
opened in a binary mode.
|
opened in a binary mode.
|
||||||
"""
|
"""
|
||||||
|
if not isinstance(file, int):
|
||||||
|
file = os.fspath(file)
|
||||||
if not isinstance(file, (str, bytes, int)):
|
if not isinstance(file, (str, bytes, int)):
|
||||||
raise TypeError("invalid file: %r" % file)
|
raise TypeError("invalid file: %r" % file)
|
||||||
if not isinstance(mode, str):
|
if not isinstance(mode, str):
|
||||||
|
|
|
@ -844,6 +844,32 @@ class IOTest(unittest.TestCase):
|
||||||
self.assertEqual(getattr(stream, method)(buffer), 5)
|
self.assertEqual(getattr(stream, method)(buffer), 5)
|
||||||
self.assertEqual(bytes(buffer), b"12345")
|
self.assertEqual(bytes(buffer), b"12345")
|
||||||
|
|
||||||
|
def test_fspath_support(self):
|
||||||
|
class PathLike:
|
||||||
|
def __init__(self, path):
|
||||||
|
self.path = path
|
||||||
|
|
||||||
|
def __fspath__(self):
|
||||||
|
return self.path
|
||||||
|
|
||||||
|
def check_path_succeeds(path):
|
||||||
|
with self.open(path, "w") as f:
|
||||||
|
f.write("egg\n")
|
||||||
|
|
||||||
|
with self.open(path, "r") as f:
|
||||||
|
self.assertEqual(f.read(), "egg\n")
|
||||||
|
|
||||||
|
check_path_succeeds(PathLike(support.TESTFN))
|
||||||
|
check_path_succeeds(PathLike(support.TESTFN.encode('utf-8')))
|
||||||
|
|
||||||
|
bad_path = PathLike(TypeError)
|
||||||
|
with self.assertRaisesRegex(TypeError, 'invalid file'):
|
||||||
|
self.open(bad_path, 'w')
|
||||||
|
|
||||||
|
# ensure that refcounting is correct with some error conditions
|
||||||
|
with self.assertRaisesRegex(ValueError, 'read/write/append mode'):
|
||||||
|
self.open(PathLike(support.TESTFN), 'rwxa')
|
||||||
|
|
||||||
|
|
||||||
class CIOTest(IOTest):
|
class CIOTest(IOTest):
|
||||||
|
|
||||||
|
|
|
@ -238,21 +238,33 @@ _io_open_impl(PyModuleDef *module, PyObject *file, const char *mode,
|
||||||
int text = 0, binary = 0, universal = 0;
|
int text = 0, binary = 0, universal = 0;
|
||||||
|
|
||||||
char rawmode[6], *m;
|
char rawmode[6], *m;
|
||||||
int line_buffering;
|
int line_buffering, is_number;
|
||||||
long isatty;
|
long isatty;
|
||||||
|
|
||||||
PyObject *raw, *modeobj = NULL, *buffer, *wrapper, *result = NULL;
|
PyObject *raw, *modeobj = NULL, *buffer, *wrapper, *result = NULL, *path_or_fd = NULL;
|
||||||
|
|
||||||
_Py_IDENTIFIER(_blksize);
|
_Py_IDENTIFIER(_blksize);
|
||||||
_Py_IDENTIFIER(isatty);
|
_Py_IDENTIFIER(isatty);
|
||||||
_Py_IDENTIFIER(mode);
|
_Py_IDENTIFIER(mode);
|
||||||
_Py_IDENTIFIER(close);
|
_Py_IDENTIFIER(close);
|
||||||
|
|
||||||
if (!PyUnicode_Check(file) &&
|
is_number = PyNumber_Check(file);
|
||||||
!PyBytes_Check(file) &&
|
|
||||||
!PyNumber_Check(file)) {
|
if (is_number) {
|
||||||
|
path_or_fd = file;
|
||||||
|
Py_INCREF(path_or_fd);
|
||||||
|
} else {
|
||||||
|
path_or_fd = PyOS_FSPath(file);
|
||||||
|
if (path_or_fd == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_number &&
|
||||||
|
!PyUnicode_Check(path_or_fd) &&
|
||||||
|
!PyBytes_Check(path_or_fd)) {
|
||||||
PyErr_Format(PyExc_TypeError, "invalid file: %R", file);
|
PyErr_Format(PyExc_TypeError, "invalid file: %R", file);
|
||||||
return NULL;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Decode mode */
|
/* Decode mode */
|
||||||
|
@ -293,7 +305,7 @@ _io_open_impl(PyModuleDef *module, PyObject *file, const char *mode,
|
||||||
if (strchr(mode+i+1, c)) {
|
if (strchr(mode+i+1, c)) {
|
||||||
invalid_mode:
|
invalid_mode:
|
||||||
PyErr_Format(PyExc_ValueError, "invalid mode: '%s'", mode);
|
PyErr_Format(PyExc_ValueError, "invalid mode: '%s'", mode);
|
||||||
return NULL;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -311,51 +323,54 @@ _io_open_impl(PyModuleDef *module, PyObject *file, const char *mode,
|
||||||
if (creating || writing || appending || updating) {
|
if (creating || writing || appending || updating) {
|
||||||
PyErr_SetString(PyExc_ValueError,
|
PyErr_SetString(PyExc_ValueError,
|
||||||
"mode U cannot be combined with x', 'w', 'a', or '+'");
|
"mode U cannot be combined with x', 'w', 'a', or '+'");
|
||||||
return NULL;
|
goto error;
|
||||||
}
|
}
|
||||||
if (PyErr_WarnEx(PyExc_DeprecationWarning,
|
if (PyErr_WarnEx(PyExc_DeprecationWarning,
|
||||||
"'U' mode is deprecated", 1) < 0)
|
"'U' mode is deprecated", 1) < 0)
|
||||||
return NULL;
|
goto error;
|
||||||
reading = 1;
|
reading = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (text && binary) {
|
if (text && binary) {
|
||||||
PyErr_SetString(PyExc_ValueError,
|
PyErr_SetString(PyExc_ValueError,
|
||||||
"can't have text and binary mode at once");
|
"can't have text and binary mode at once");
|
||||||
return NULL;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (creating + reading + writing + appending > 1) {
|
if (creating + reading + writing + appending > 1) {
|
||||||
PyErr_SetString(PyExc_ValueError,
|
PyErr_SetString(PyExc_ValueError,
|
||||||
"must have exactly one of create/read/write/append mode");
|
"must have exactly one of create/read/write/append mode");
|
||||||
return NULL;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (binary && encoding != NULL) {
|
if (binary && encoding != NULL) {
|
||||||
PyErr_SetString(PyExc_ValueError,
|
PyErr_SetString(PyExc_ValueError,
|
||||||
"binary mode doesn't take an encoding argument");
|
"binary mode doesn't take an encoding argument");
|
||||||
return NULL;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (binary && errors != NULL) {
|
if (binary && errors != NULL) {
|
||||||
PyErr_SetString(PyExc_ValueError,
|
PyErr_SetString(PyExc_ValueError,
|
||||||
"binary mode doesn't take an errors argument");
|
"binary mode doesn't take an errors argument");
|
||||||
return NULL;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (binary && newline != NULL) {
|
if (binary && newline != NULL) {
|
||||||
PyErr_SetString(PyExc_ValueError,
|
PyErr_SetString(PyExc_ValueError,
|
||||||
"binary mode doesn't take a newline argument");
|
"binary mode doesn't take a newline argument");
|
||||||
return NULL;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Create the Raw file stream */
|
/* Create the Raw file stream */
|
||||||
raw = PyObject_CallFunction((PyObject *)&PyFileIO_Type,
|
raw = PyObject_CallFunction((PyObject *)&PyFileIO_Type,
|
||||||
"OsiO", file, rawmode, closefd, opener);
|
"OsiO", path_or_fd, rawmode, closefd, opener);
|
||||||
if (raw == NULL)
|
if (raw == NULL)
|
||||||
return NULL;
|
goto error;
|
||||||
result = raw;
|
result = raw;
|
||||||
|
|
||||||
|
Py_DECREF(path_or_fd);
|
||||||
|
path_or_fd = NULL;
|
||||||
|
|
||||||
modeobj = PyUnicode_FromString(mode);
|
modeobj = PyUnicode_FromString(mode);
|
||||||
if (modeobj == NULL)
|
if (modeobj == NULL)
|
||||||
goto error;
|
goto error;
|
||||||
|
@ -461,6 +476,7 @@ _io_open_impl(PyModuleDef *module, PyObject *file, const char *mode,
|
||||||
Py_XDECREF(close_result);
|
Py_XDECREF(close_result);
|
||||||
Py_DECREF(result);
|
Py_DECREF(result);
|
||||||
}
|
}
|
||||||
|
Py_XDECREF(path_or_fd);
|
||||||
Py_XDECREF(modeobj);
|
Py_XDECREF(modeobj);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue