diff options
Diffstat (limited to 'Modules/_io/fileio.c')
| -rw-r--r-- | Modules/_io/fileio.c | 290 |
1 files changed, 163 insertions, 127 deletions
diff --git a/Modules/_io/fileio.c b/Modules/_io/fileio.c index f3ce776..74508a7 100644 --- a/Modules/_io/fileio.c +++ b/Modules/_io/fileio.c @@ -52,13 +52,15 @@ typedef struct { unsigned int appending : 1; signed int seekable : 2; /* -1 means unknown */ unsigned int closefd : 1; - unsigned int deallocating: 1; + char finalizing; PyObject *weakreflist; PyObject *dict; } fileio; PyTypeObject PyFileIO_Type; +_Py_IDENTIFIER(name); + #define PyFileIO_Check(op) (PyObject_TypeCheck((op), &PyFileIO_Type)) int @@ -124,24 +126,31 @@ internal_close(fileio *self) static PyObject * fileio_close(fileio *self) { + PyObject *res; + PyObject *exc, *val, *tb; + int rc; _Py_IDENTIFIER(close); + res = _PyObject_CallMethodId((PyObject*)&PyRawIOBase_Type, + &PyId_close, "O", self); if (!self->closefd) { self->fd = -1; - Py_RETURN_NONE; + return res; } - if (self->deallocating) { + if (res == NULL) + PyErr_Fetch(&exc, &val, &tb); + if (self->finalizing) { PyObject *r = fileio_dealloc_warn(self, (PyObject *) self); if (r) Py_DECREF(r); else PyErr_Clear(); } - errno = internal_close(self); - if (errno < 0) - return NULL; - - return _PyObject_CallMethodId((PyObject*)&PyRawIOBase_Type, - &PyId_close, "O", self); + rc = internal_close(self); + if (res == NULL) + _PyErr_ChainExceptions(exc, val, tb); + if (rc < 0) + Py_CLEAR(res); + return res; } static PyObject * @@ -204,6 +213,9 @@ check_fd(int fd) return 0; } +#ifdef O_CLOEXEC +extern int _Py_open_cloexec_works; +#endif static int fileio_init(PyObject *oself, PyObject *args, PyObject *kwds) @@ -223,6 +235,11 @@ fileio_init(PyObject *oself, PyObject *args, PyObject *kwds) int fd = -1; int closefd = 1; int fd_is_own = 0; +#ifdef O_CLOEXEC + int *atomic_flag_works = &_Py_open_cloexec_works; +#elif !defined(MS_WINDOWS) + int *atomic_flag_works = NULL; +#endif assert(PyFileIO_Check(oself)); if (self->fd >= 0) { @@ -250,7 +267,7 @@ fileio_init(PyObject *oself, PyObject *args, PyObject *kwds) if (fd < 0) { if (!PyErr_Occurred()) { PyErr_SetString(PyExc_ValueError, - "Negative filedescriptor"); + "negative file descriptor"); return -1; } PyErr_Clear(); @@ -258,15 +275,14 @@ fileio_init(PyObject *oself, PyObject *args, PyObject *kwds) #ifdef MS_WINDOWS if (PyUnicode_Check(nameobj)) { - int rv = _PyUnicode_HasNULChars(nameobj); - if (rv) { - if (rv != -1) - PyErr_SetString(PyExc_TypeError, "embedded NUL character"); - return -1; - } - widename = PyUnicode_AsUnicode(nameobj); + Py_ssize_t length; + widename = PyUnicode_AsUnicodeAndSize(nameobj, &length); if (widename == NULL) return -1; + if (wcslen(widename) != length) { + PyErr_SetString(PyExc_TypeError, "embedded NUL character"); + return -1; + } } else #endif if (fd < 0) @@ -343,6 +359,12 @@ fileio_init(PyObject *oself, PyObject *args, PyObject *kwds) flags |= O_BINARY; #endif +#ifdef MS_WINDOWS + flags |= O_NOINHERIT; +#elif defined(O_CLOEXEC) + flags |= O_CLOEXEC; +#endif + if (fd >= 0) { if (check_fd(fd)) goto error; @@ -366,10 +388,18 @@ fileio_init(PyObject *oself, PyObject *args, PyObject *kwds) else #endif self->fd = open(name, flags, 0666); + Py_END_ALLOW_THREADS - } else { - PyObject *fdobj = PyObject_CallFunction( - opener, "Oi", nameobj, flags); + } + else { + PyObject *fdobj; + +#ifndef MS_WINDOWS + /* the opener may clear the atomic flag */ + atomic_flag_works = NULL; +#endif + + fdobj = PyObject_CallFunction(opener, "Oi", nameobj, flags); if (fdobj == NULL) goto error; if (!PyLong_Check(fdobj)) { @@ -388,14 +418,14 @@ fileio_init(PyObject *oself, PyObject *args, PyObject *kwds) fd_is_own = 1; if (self->fd < 0) { -#ifdef MS_WINDOWS - if (widename != NULL) - PyErr_SetFromErrnoWithFilenameObject(PyExc_IOError, nameobj); - else -#endif - PyErr_SetFromErrnoWithFilename(PyExc_IOError, name); + PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, nameobj); goto error; } + +#ifndef MS_WINDOWS + if (_Py_set_inheritable(self->fd, 0, atomic_flag_works) < 0) + goto error; +#endif } if (dircheck(self, nameobj) < 0) goto error; @@ -405,7 +435,7 @@ fileio_init(PyObject *oself, PyObject *args, PyObject *kwds) _setmode(self->fd, O_BINARY); #endif - if (PyObject_SetAttrString((PyObject *)self, "name", nameobj) < 0) + if (_PyObject_SetAttrId((PyObject *)self, &PyId_name, nameobj) < 0) goto error; if (self->appending) { @@ -449,7 +479,7 @@ fileio_clear(fileio *self) static void fileio_dealloc(fileio *self) { - self->deallocating = 1; + self->finalizing = 1; if (_PyIOBase_finalize((PyObject *) self) < 0) return; _PyObject_GC_UNTRACK(self); @@ -469,8 +499,10 @@ err_closed(void) static PyObject * err_mode(char *action) { - PyErr_Format(IO_STATE->unsupported_operation, - "File not open for %s", action); + _PyIO_State *state = IO_STATE(); + if (state != NULL) + PyErr_Format(state->unsupported_operation, + "File not open for %s", action); return NULL; } @@ -535,7 +567,7 @@ fileio_readinto(fileio *self, PyObject *args) len = pbuf.len; Py_BEGIN_ALLOW_THREADS errno = 0; -#if defined(MS_WIN64) || defined(MS_WINDOWS) +#ifdef MS_WINDOWS if (len > INT_MAX) len = INT_MAX; n = read(self->fd, pbuf.buf, (int)len); @@ -558,33 +590,27 @@ fileio_readinto(fileio *self, PyObject *args) return PyLong_FromSsize_t(n); } +#ifndef HAVE_FSTAT + +static PyObject * +fileio_readall(fileio *self) +{ + _Py_IDENTIFIER(readall); + return _PyObject_CallMethodId((PyObject*)&PyRawIOBase_Type, + &PyId_readall, "O", self); +} + +#else + static size_t -new_buffersize(fileio *self, size_t currentsize -#ifdef HAVE_FSTAT - , Py_off_t pos, Py_off_t end -#endif - ) +new_buffersize(fileio *self, size_t currentsize) { size_t addend; -#ifdef HAVE_FSTAT - if (end != (Py_off_t)-1) { - /* Files claiming a size smaller than SMALLCHUNK may - actually be streaming pseudo-files. In this case, we - apply the more aggressive algorithm below. - */ - if (end >= SMALLCHUNK && end >= pos && pos >= 0) { - /* Add 1 so if the file were to grow we'd notice. */ - Py_off_t bufsize = currentsize + end - pos + 1; - if (bufsize < PY_SSIZE_T_MAX) - return (size_t)bufsize; - else - return PY_SSIZE_T_MAX; - } - } -#endif + /* Expand the buffer by an amount proportional to the current size, giving us amortized linear-time behavior. For bigger sizes, use a less-than-double growth factor to avoid excessive allocation. */ + assert(currentsize <= PY_SSIZE_T_MAX); if (currentsize > 65536) addend = currentsize >> 3; else @@ -598,26 +624,19 @@ new_buffersize(fileio *self, size_t currentsize static PyObject * fileio_readall(fileio *self) { -#ifdef HAVE_FSTAT struct stat st; Py_off_t pos, end; -#endif PyObject *result; - Py_ssize_t total = 0; + Py_ssize_t bytes_read = 0; Py_ssize_t n; - size_t newsize; + size_t bufsize; if (self->fd < 0) return err_closed(); if (!_PyVerify_fd(self->fd)) return PyErr_SetFromErrno(PyExc_IOError); - result = PyBytes_FromStringAndSize(NULL, SMALLCHUNK); - if (result == NULL) - return NULL; - -#ifdef HAVE_FSTAT -#if defined(MS_WIN64) || defined(MS_WINDOWS) +#ifdef MS_WINDOWS pos = _lseeki64(self->fd, 0L, SEEK_CUR); #else pos = lseek(self->fd, 0L, SEEK_CUR); @@ -626,44 +645,46 @@ fileio_readall(fileio *self) end = st.st_size; else end = (Py_off_t)-1; -#endif + + if (end > 0 && end >= pos && pos >= 0 && end - pos < PY_SSIZE_T_MAX) { + /* This is probably a real file, so we try to allocate a + buffer one byte larger than the rest of the file. If the + calculation is right then we should get EOF without having + to enlarge the buffer. */ + bufsize = (size_t)(end - pos + 1); + } else { + bufsize = SMALLCHUNK; + } + + result = PyBytes_FromStringAndSize(NULL, bufsize); + if (result == NULL) + return NULL; + while (1) { -#ifdef HAVE_FSTAT - newsize = new_buffersize(self, total, pos, end); -#else - newsize = new_buffersize(self, total); -#endif - if (newsize > PY_SSIZE_T_MAX || newsize <= 0) { - PyErr_SetString(PyExc_OverflowError, - "unbounded read returned more bytes " - "than a Python string can hold "); - Py_DECREF(result); - return NULL; - } + if (bytes_read >= (Py_ssize_t)bufsize) { + bufsize = new_buffersize(self, bytes_read); + if (bufsize > PY_SSIZE_T_MAX || bufsize <= 0) { + PyErr_SetString(PyExc_OverflowError, + "unbounded read returned more bytes " + "than a Python bytes object can hold"); + Py_DECREF(result); + return NULL; + } - if (PyBytes_GET_SIZE(result) < (Py_ssize_t)newsize) { - if (_PyBytes_Resize(&result, newsize) < 0) { - if (total == 0) { - Py_DECREF(result); + if (PyBytes_GET_SIZE(result) < (Py_ssize_t)bufsize) { + if (_PyBytes_Resize(&result, bufsize) < 0) return NULL; - } - PyErr_Clear(); - break; } } Py_BEGIN_ALLOW_THREADS errno = 0; - n = newsize - total; -#if defined(MS_WIN64) || defined(MS_WINDOWS) + n = bufsize - bytes_read; +#ifdef MS_WINDOWS if (n > INT_MAX) n = INT_MAX; - n = read(self->fd, - PyBytes_AS_STRING(result) + total, - (int)n); + n = read(self->fd, PyBytes_AS_STRING(result) + bytes_read, (int)n); #else - n = read(self->fd, - PyBytes_AS_STRING(result) + total, - n); + n = read(self->fd, PyBytes_AS_STRING(result) + bytes_read, n); #endif Py_END_ALLOW_THREADS if (n == 0) @@ -676,9 +697,9 @@ fileio_readall(fileio *self) } continue; } - if (total > 0) - break; if (errno == EAGAIN) { + if (bytes_read > 0) + break; Py_DECREF(result); Py_RETURN_NONE; } @@ -686,22 +707,19 @@ fileio_readall(fileio *self) PyErr_SetFromErrno(PyExc_IOError); return NULL; } - total += n; -#ifdef HAVE_FSTAT + bytes_read += n; pos += n; -#endif } - if (PyBytes_GET_SIZE(result) > total) { - if (_PyBytes_Resize(&result, total) < 0) { - /* This should never happen, but just in case */ - Py_DECREF(result); + if (PyBytes_GET_SIZE(result) > bytes_read) { + if (_PyBytes_Resize(&result, bytes_read) < 0) return NULL; - } } return result; } +#endif /* HAVE_FSTAT */ + static PyObject * fileio_read(fileio *self, PyObject *args) { @@ -722,7 +740,7 @@ fileio_read(fileio *self, PyObject *args) return fileio_readall(self); } -#if defined(MS_WIN64) || defined(MS_WINDOWS) +#ifdef MS_WINDOWS if (size > INT_MAX) size = INT_MAX; #endif @@ -734,7 +752,7 @@ fileio_read(fileio *self, PyObject *args) if (_PyVerify_fd(self->fd)) { Py_BEGIN_ALLOW_THREADS errno = 0; -#if defined(MS_WIN64) || defined(MS_WINDOWS) +#ifdef MS_WINDOWS n = read(self->fd, ptr, (int)size); #else n = read(self->fd, ptr, size); @@ -755,7 +773,7 @@ fileio_read(fileio *self, PyObject *args) if (n != size) { if (_PyBytes_Resize(&bytes, n) < 0) { - Py_DECREF(bytes); + Py_CLEAR(bytes); return NULL; } } @@ -782,7 +800,7 @@ fileio_write(fileio *self, PyObject *args) Py_BEGIN_ALLOW_THREADS errno = 0; len = pbuf.len; -#if defined(MS_WIN64) || defined(MS_WINDOWS) +#ifdef MS_WINDOWS if (len > 32767 && isatty(self->fd)) { /* Issue #11395: the Windows console returns an error (12: not enough space error) on writing into stdout if stdout mode is @@ -855,7 +873,7 @@ portable_lseek(int fd, PyObject *posobj, int whence) if (_PyVerify_fd(fd)) { Py_BEGIN_ALLOW_THREADS -#if defined(MS_WIN64) || defined(MS_WINDOWS) +#ifdef MS_WINDOWS res = _lseeki64(fd, pos, whence); #else res = lseek(fd, pos, whence); @@ -1028,7 +1046,6 @@ mode_string(fileio *self) static PyObject * fileio_repr(fileio *self) { - _Py_IDENTIFIER(name); PyObject *nameobj, *res; if (self->fd < 0) @@ -1076,10 +1093,10 @@ fileio_getstate(fileio *self) PyDoc_STRVAR(fileio_doc, "file(name: str[, mode: str][, opener: None]) -> file IO object\n" "\n" -"Open a file. The mode can be 'r', 'w', 'x' or 'a' for reading (default),\n" +"Open a file. The mode can be 'r' (default), 'w', 'x' or 'a' for reading,\n" "writing, exclusive creation or appending. The file will be created if it\n" "doesn't exist when opened for writing or appending; it will be truncated\n" -"when opened for writing. A `FileExistsError` will be raised if it already\n" +"when opened for writing. A FileExistsError will be raised if it already\n" "exists when opened for creating. Opening a file for creating implies\n" "writing so this mode behaves in a similar way to 'w'.Add a '+' to the mode\n" "to allow simultaneous reading and writing. A custom opener can be used by\n" @@ -1093,46 +1110,51 @@ PyDoc_STRVAR(read_doc, "\n" "Only makes one system call, so less data may be returned than requested\n" "In non-blocking mode, returns None if no data is available.\n" -"On end-of-file, returns ''."); +"Return an empty bytes object at EOF."); PyDoc_STRVAR(readall_doc, "readall() -> bytes. read all data from the file, returned as bytes.\n" "\n" "In non-blocking mode, returns as much as is immediately available,\n" -"or None if no data is available. On end-of-file, returns ''."); +"or None if no data is available. Return an empty bytes object at EOF."); PyDoc_STRVAR(write_doc, "write(b: bytes) -> int. Write bytes b to file, return number written.\n" "\n" "Only makes one system call, so not all of the data may be written.\n" -"The number of bytes actually written is returned."); +"The number of bytes actually written is returned. In non-blocking mode,\n" +"returns None if the write would block." +); PyDoc_STRVAR(fileno_doc, -"fileno() -> int. \"file descriptor\".\n" -"\n" -"This is needed for lower-level file interfaces, such the fcntl module."); +"fileno() -> int. Return the underlying file descriptor (an integer)."); PyDoc_STRVAR(seek_doc, -"seek(offset: int[, whence: int]) -> None. Move to new file position.\n" +"seek(offset: int[, whence: int]) -> int. Move to new file position and\n" +"return the file position.\n" "\n" "Argument offset is a byte count. Optional argument whence defaults to\n" -"0 (offset from start of file, offset should be >= 0); other values are 1\n" -"(move relative to current position, positive or negative), and 2 (move\n" -"relative to end of file, usually negative, although many platforms allow\n" -"seeking beyond the end of a file)." +"SEEK_SET or 0 (offset from start of file, offset should be >= 0); other values\n" +"are SEEK_CUR or 1 (move relative to current position, positive or negative),\n" +"and SEEK_END or 2 (move relative to end of file, usually negative, although\n" +"many platforms allow seeking beyond the end of a file).\n" "\n" "Note that not all file objects are seekable."); #ifdef HAVE_FTRUNCATE PyDoc_STRVAR(truncate_doc, -"truncate([size: int]) -> None. Truncate the file to at most size bytes.\n" +"truncate([size: int]) -> int. Truncate the file to at most size bytes\n" +"and return the truncated size.\n" "\n" -"Size defaults to the current file position, as returned by tell()." +"Size defaults to the current file position, as returned by tell().\n" "The current file position is changed to the value of size."); #endif PyDoc_STRVAR(tell_doc, -"tell() -> int. Current file position"); +"tell() -> int. Current file position.\n" +"\n" +"Can raise OSError for non seekable files." +); PyDoc_STRVAR(readinto_doc, "readinto() -> Same as RawIOBase.readinto()."); @@ -1141,10 +1163,10 @@ PyDoc_STRVAR(close_doc, "close() -> None. Close the file.\n" "\n" "A closed file cannot be used for further I/O operations. close() may be\n" -"called more than once without error. Changes the fileno to -1."); +"called more than once without error."); PyDoc_STRVAR(isatty_doc, -"isatty() -> bool. True if the file is connected to a tty device."); +"isatty() -> bool. True if the file is connected to a TTY device."); PyDoc_STRVAR(seekable_doc, "seekable() -> bool. True if file supports random-access."); @@ -1199,11 +1221,16 @@ get_mode(fileio *self, void *closure) static PyGetSetDef fileio_getsetlist[] = { {"closed", (getter)get_closed, NULL, "True if the file is closed"}, {"closefd", (getter)get_closefd, NULL, - "True if the file descriptor will be closed"}, + "True if the file descriptor will be closed by close()."}, {"mode", (getter)get_mode, NULL, "String giving the file mode"}, {NULL}, }; +static PyMemberDef fileio_members[] = { + {"_finalizing", T_BOOL, offsetof(fileio, finalizing), 0}, + {NULL} +}; + PyTypeObject PyFileIO_Type = { PyVarObject_HEAD_INIT(NULL, 0) "_io.FileIO", @@ -1225,7 +1252,7 @@ PyTypeObject PyFileIO_Type = { 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE - | Py_TPFLAGS_HAVE_GC, /* tp_flags */ + | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_HAVE_FINALIZE, /* tp_flags */ fileio_doc, /* tp_doc */ (traverseproc)fileio_traverse, /* tp_traverse */ (inquiry)fileio_clear, /* tp_clear */ @@ -1234,7 +1261,7 @@ PyTypeObject PyFileIO_Type = { 0, /* tp_iter */ 0, /* tp_iternext */ fileio_methods, /* tp_methods */ - 0, /* tp_members */ + fileio_members, /* tp_members */ fileio_getsetlist, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ @@ -1245,4 +1272,13 @@ PyTypeObject PyFileIO_Type = { PyType_GenericAlloc, /* tp_alloc */ fileio_new, /* tp_new */ PyObject_GC_Del, /* tp_free */ + 0, /* tp_is_gc */ + 0, /* tp_bases */ + 0, /* tp_mro */ + 0, /* tp_cache */ + 0, /* tp_subclasses */ + 0, /* tp_weaklist */ + 0, /* tp_del */ + 0, /* tp_version_tag */ + 0, /* tp_finalize */ }; |
