summaryrefslogtreecommitdiffstats
path: root/Modules/_io/textio.c
diff options
context:
space:
mode:
Diffstat (limited to 'Modules/_io/textio.c')
-rw-r--r--Modules/_io/textio.c252
1 files changed, 141 insertions, 111 deletions
diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c
index d2e92fa..2fbb8f3 100644
--- a/Modules/_io/textio.c
+++ b/Modules/_io/textio.c
@@ -24,7 +24,7 @@ PyDoc_STRVAR(textiobase_doc,
static PyObject *
_unsupported(const char *message)
{
- PyErr_SetString(_PyIO_unsupported_operation, message);
+ PyErr_SetString(IO_STATE->unsupported_operation, message);
return NULL;
}
@@ -635,9 +635,9 @@ PyDoc_STRVAR(textiowrapper_doc,
"\n"
"* On output, if newline is None, any '\\n' characters written are\n"
" translated to the system default line separator, os.linesep. If\n"
- " newline is '', no translation takes place. If newline is any of the\n"
- " other legal values, any '\\n' characters written are translated to\n"
- " the given string.\n"
+ " newline is '' or '\\n', no translation takes place. If newline is any\n"
+ " of the other legal values, any '\\n' characters written are translated\n"
+ " to the given string.\n"
"\n"
"If line_buffering is True, a call to flush is implied when a call to\n"
"write contains a newline character."
@@ -660,11 +660,14 @@ typedef struct
PyObject *errors;
const char *writenl; /* utf-8 encoded, NULL stands for \n */
char line_buffering;
+ char write_through;
char readuniversal;
char readtranslate;
char writetranslate;
char seekable;
+ char has_read1;
char telling;
+ char deallocating;
/* Specialized encoding func (see below) */
encodefunc_t encodefunc;
/* Whether or not it's the start of the stream */
@@ -815,22 +818,23 @@ static int
textiowrapper_init(textio *self, PyObject *args, PyObject *kwds)
{
char *kwlist[] = {"buffer", "encoding", "errors",
- "newline", "line_buffering",
+ "newline", "line_buffering", "write_through",
NULL};
PyObject *buffer, *raw;
char *encoding = NULL;
char *errors = NULL;
char *newline = NULL;
- int line_buffering = 0;
+ int line_buffering = 0, write_through = 0;
+ _PyIO_State *state = IO_STATE;
PyObject *res;
int r;
self->ok = 0;
self->detached = 0;
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|zzzi:fileio",
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|zzzii:fileio",
kwlist, &buffer, &encoding, &errors,
- &newline, &line_buffering))
+ &newline, &line_buffering, &write_through))
return -1;
if (newline && newline[0] != '\0'
@@ -855,12 +859,35 @@ textiowrapper_init(textio *self, PyObject *args, PyObject *kwds)
self->decoded_chars_used = 0;
self->pending_bytes_count = 0;
self->encodefunc = NULL;
- self->writenl = NULL;
+ if (encoding == NULL) {
+ /* Try os.device_encoding(fileno) */
+ PyObject *fileno;
+ fileno = PyObject_CallMethod(buffer, "fileno", NULL);
+ /* Ignore only AttributeError and UnsupportedOperation */
+ if (fileno == NULL) {
+ if (PyErr_ExceptionMatches(PyExc_AttributeError) ||
+ PyErr_ExceptionMatches(state->unsupported_operation)) {
+ PyErr_Clear();
+ }
+ else {
+ goto error;
+ }
+ }
+ else {
+ self->encoding = PyObject_CallMethod(state->os_module,
+ "device_encoding",
+ "N", fileno);
+ if (self->encoding == NULL)
+ goto error;
+ else if (!PyUnicode_Check(self->encoding))
+ Py_CLEAR(self->encoding);
+ }
+ }
if (encoding == NULL && self->encoding == NULL) {
- if (_PyIO_locale_module == NULL) {
- _PyIO_locale_module = PyImport_ImportModule("locale");
- if (_PyIO_locale_module == NULL)
+ if (state->locale_module == NULL) {
+ state->locale_module = PyImport_ImportModule("locale");
+ if (state->locale_module == NULL)
goto catch_ImportError;
else
goto use_locale;
@@ -868,7 +895,7 @@ textiowrapper_init(textio *self, PyObject *args, PyObject *kwds)
else {
use_locale:
self->encoding = PyObject_CallMethod(
- _PyIO_locale_module, "getpreferredencoding", NULL);
+ state->locale_module, "getpreferredencoding", NULL);
if (self->encoding == NULL) {
catch_ImportError:
/*
@@ -879,19 +906,22 @@ textiowrapper_init(textio *self, PyObject *args, PyObject *kwds)
*/
if (PyErr_ExceptionMatches(PyExc_ImportError)) {
PyErr_Clear();
- self->encoding = PyString_FromString("ascii");
+ self->encoding = PyUnicode_FromString("ascii");
}
else
goto error;
}
- else if (!PyString_Check(self->encoding))
+ else if (!PyUnicode_Check(self->encoding))
Py_CLEAR(self->encoding);
}
}
- if (self->encoding != NULL)
- encoding = PyString_AsString(self->encoding);
+ if (self->encoding != NULL) {
+ encoding = _PyUnicode_AsString(self->encoding);
+ if (encoding == NULL)
+ goto error;
+ }
else if (encoding != NULL) {
- self->encoding = PyString_FromString(encoding);
+ self->encoding = PyUnicode_FromString(encoding);
if (self->encoding == NULL)
goto error;
}
@@ -909,15 +939,18 @@ textiowrapper_init(textio *self, PyObject *args, PyObject *kwds)
self->chunk_size = 8192;
self->readuniversal = (newline == NULL || newline[0] == '\0');
self->line_buffering = line_buffering;
+ self->write_through = write_through;
self->readtranslate = (newline == NULL);
if (newline) {
- self->readnl = PyString_FromString(newline);
+ self->readnl = PyUnicode_FromString(newline);
if (self->readnl == NULL)
return -1;
}
self->writetranslate = (newline == NULL || newline[0] != '\0');
- if (!self->readuniversal && self->writetranslate) {
- self->writenl = PyString_AsString(self->readnl);
+ if (!self->readuniversal && self->readnl) {
+ self->writenl = _PyUnicode_AsString(self->readnl);
+ if (self->writenl == NULL)
+ goto error;
if (!strcmp(self->writenl, "\n"))
self->writenl = NULL;
}
@@ -977,10 +1010,10 @@ textiowrapper_init(textio *self, PyObject *args, PyObject *kwds)
else
goto error;
}
- else if (PyString_Check(res)) {
+ else if (PyUnicode_Check(res)) {
encodefuncentry *e = encodefuncs;
while (e->name != NULL) {
- if (!strcmp(PyString_AS_STRING(res), e->name)) {
+ if (!PyUnicode_CompareWithASCIIString(res, e->name)) {
self->encodefunc = e->encodefunc;
break;
}
@@ -1019,6 +1052,8 @@ textiowrapper_init(textio *self, PyObject *args, PyObject *kwds)
goto error;
self->seekable = self->telling = r;
+ self->has_read1 = PyObject_HasAttrString(buffer, "read1");
+
self->encoding_start_of_stream = 0;
if (self->seekable && self->encoder) {
PyObject *cookieObj;
@@ -1075,6 +1110,7 @@ _textiowrapper_clear(textio *self)
static void
textiowrapper_dealloc(textio *self)
{
+ self->deallocating = 1;
if (_textiowrapper_clear(self) < 0)
return;
_PyObject_GC_UNTRACK(self);
@@ -1240,10 +1276,8 @@ textiowrapper_write(textio *self, PyObject *args)
CHECK_CLOSED(self);
- if (self->encoder == NULL) {
- PyErr_SetString(PyExc_IOError, "not writable");
- return NULL;
- }
+ if (self->encoder == NULL)
+ return _unsupported("not writable");
Py_INCREF(text);
@@ -1263,7 +1297,9 @@ textiowrapper_write(textio *self, PyObject *args)
text = newtext;
}
- if (self->line_buffering &&
+ if (self->write_through)
+ needflush = 1;
+ else if (self->line_buffering &&
(haslf ||
findchar(PyUnicode_AS_UNICODE(text),
PyUnicode_GET_SIZE(text), '\r')))
@@ -1380,7 +1416,7 @@ textiowrapper_read_chunk(textio *self)
*/
if (self->decoder == NULL) {
- PyErr_SetString(PyExc_IOError, "not readable");
+ _unsupported("not readable");
return -1;
}
@@ -1410,7 +1446,8 @@ textiowrapper_read_chunk(textio *self)
if (chunk_size == NULL)
goto fail;
input_chunk = PyObject_CallMethodObjArgs(self->buffer,
- _PyIO_str_read1, chunk_size, NULL);
+ (self->has_read1 ? _PyIO_str_read1: _PyIO_str_read),
+ chunk_size, NULL);
Py_DECREF(chunk_size);
if (input_chunk == NULL)
goto fail;
@@ -1470,10 +1507,8 @@ textiowrapper_read(textio *self, PyObject *args)
CHECK_CLOSED(self);
- if (self->decoder == NULL) {
- PyErr_SetString(PyExc_IOError, "not readable");
- return NULL;
- }
+ if (self->decoder == NULL)
+ return _unsupported("not readable");
if (_textiowrapper_writeflush(self) < 0)
return NULL;
@@ -1481,7 +1516,7 @@ textiowrapper_read(textio *self, PyObject *args)
if (n < 0) {
/* Read everything */
PyObject *bytes = PyObject_CallMethod(self->buffer, "read", NULL);
- PyObject *decoded, *final;
+ PyObject *decoded;
if (bytes == NULL)
goto fail;
decoded = PyObject_CallMethodObjArgs(self->decoder, _PyIO_str_decode,
@@ -1497,14 +1532,12 @@ textiowrapper_read(textio *self, PyObject *args)
return NULL;
}
- final = PyUnicode_Concat(result, decoded);
- Py_DECREF(result);
- Py_DECREF(decoded);
- if (final == NULL)
+ PyUnicode_AppendAndDel(&result, decoded);
+ if (result == NULL)
goto fail;
Py_CLEAR(self->snapshot);
- return final;
+ return result;
}
else {
int res = 1;
@@ -1622,8 +1655,8 @@ _PyIO_find_line_ending(
}
else {
/* Non-universal mode. */
- Py_ssize_t readnl_len = PyString_GET_SIZE(readnl);
- unsigned char *nl = (unsigned char *) PyString_AS_STRING(readnl);
+ Py_ssize_t readnl_len = PyUnicode_GET_SIZE(readnl);
+ Py_UNICODE *nl = PyUnicode_AS_UNICODE(readnl);
if (readnl_len == 1) {
Py_UNICODE *pos = find_control_char(start, end, nl[0]);
if (pos != NULL)
@@ -1823,24 +1856,12 @@ _textiowrapper_readline(textio *self, Py_ssize_t limit)
static PyObject *
textiowrapper_readline(textio *self, PyObject *args)
{
- PyObject *limitobj = NULL;
Py_ssize_t limit = -1;
CHECK_INITIALIZED(self);
- if (!PyArg_ParseTuple(args, "|O:readline", &limitobj)) {
+ if (!PyArg_ParseTuple(args, "|n:readline", &limit)) {
return NULL;
}
- if (limitobj) {
- if (!PyNumber_Check(limitobj)) {
- PyErr_Format(PyExc_TypeError,
- "integer argument expected, got '%.200s'",
- Py_TYPE(limitobj)->tp_name);
- return NULL;
- }
- limit = PyNumber_AsSsize_t(limitobj, PyExc_OverflowError);
- if (limit == -1 && PyErr_Occurred())
- return NULL;
- }
return _textiowrapper_readline(self, limit);
}
@@ -1945,7 +1966,7 @@ _textiowrapper_decoder_setstate(textio *self, cookie_type *cookie)
res = PyObject_CallMethodObjArgs(self->decoder, _PyIO_str_reset, NULL);
else
res = PyObject_CallMethod(self->decoder, "setstate",
- "((si))", "", cookie->dec_flags);
+ "((yi))", "", cookie->dec_flags);
if (res == NULL)
return -1;
Py_DECREF(res);
@@ -1990,8 +2011,7 @@ textiowrapper_seek(textio *self, PyObject *args)
Py_INCREF(cookieObj);
if (!self->seekable) {
- PyErr_SetString(PyExc_IOError,
- "underlying stream is not seekable");
+ _unsupported("underlying stream is not seekable");
goto fail;
}
@@ -2002,8 +2022,7 @@ textiowrapper_seek(textio *self, PyObject *args)
goto fail;
if (cmp == 0) {
- PyErr_SetString(PyExc_IOError,
- "can't do nonzero cur-relative seeks");
+ _unsupported("can't do nonzero cur-relative seeks");
goto fail;
}
@@ -2023,8 +2042,7 @@ textiowrapper_seek(textio *self, PyObject *args)
goto fail;
if (cmp == 0) {
- PyErr_SetString(PyExc_IOError,
- "can't do nonzero end-relative seeks");
+ _unsupported("can't do nonzero end-relative seeks");
goto fail;
}
@@ -2057,13 +2075,8 @@ textiowrapper_seek(textio *self, PyObject *args)
goto fail;
if (cmp == 1) {
- PyObject *repr = PyObject_Repr(cookieObj);
- if (repr != NULL) {
- PyErr_Format(PyExc_ValueError,
- "negative seek position %s",
- PyString_AS_STRING(repr));
- Py_DECREF(repr);
- }
+ PyErr_Format(PyExc_ValueError,
+ "negative seek position %R", cookieObj);
goto fail;
}
@@ -2131,7 +2144,7 @@ textiowrapper_seek(textio *self, PyObject *args)
self->decoded_chars_used = cookie.chars_to_skip;
}
else {
- self->snapshot = Py_BuildValue("is", cookie.dec_flags, "");
+ self->snapshot = Py_BuildValue("iy", cookie.dec_flags, "");
if (self->snapshot == NULL)
goto fail;
}
@@ -2163,8 +2176,7 @@ textiowrapper_tell(textio *self, PyObject *args)
CHECK_CLOSED(self);
if (!self->seekable) {
- PyErr_SetString(PyExc_IOError,
- "underlying stream is not seekable");
+ _unsupported("underlying stream is not seekable");
goto fail;
}
if (!self->telling) {
@@ -2241,7 +2253,7 @@ textiowrapper_tell(textio *self, PyObject *args)
int dec_flags;
PyObject *decoded = PyObject_CallMethod(
- self->decoder, "decode", "s#", input, 1);
+ self->decoder, "decode", "y#", input, 1);
if (decoded == NULL)
goto fail;
assert (PyUnicode_Check(decoded));
@@ -2254,7 +2266,7 @@ textiowrapper_tell(textio *self, PyObject *args)
_PyIO_str_getstate, NULL);
if (state == NULL)
goto fail;
- if (!PyArg_Parse(state, "(s#i)", &dec_buffer, &dec_buffer_len, &dec_flags)) {
+ if (!PyArg_Parse(state, "(y#i)", &dec_buffer, &dec_buffer_len, &dec_flags)) {
Py_DECREF(state);
goto fail;
}
@@ -2275,7 +2287,7 @@ textiowrapper_tell(textio *self, PyObject *args)
if (input == input_end) {
/* We didn't get enough decoded data; signal EOF to get more. */
PyObject *decoded = PyObject_CallMethod(
- self->decoder, "decode", "si", "", /* final = */ 1);
+ self->decoder, "decode", "yi", "", /* final = */ 1);
if (decoded == NULL)
goto fail;
assert (PyUnicode_Check(decoded));
@@ -2341,36 +2353,51 @@ textiowrapper_truncate(textio *self, PyObject *args)
static PyObject *
textiowrapper_repr(textio *self)
{
- PyObject *nameobj, *res;
- PyObject *namerepr = NULL, *encrepr = NULL;
+ PyObject *nameobj, *modeobj, *res, *s;
CHECK_INITIALIZED(self);
+ res = PyUnicode_FromString("<_io.TextIOWrapper");
+ if (res == NULL)
+ return NULL;
nameobj = PyObject_GetAttrString((PyObject *) self, "name");
if (nameobj == NULL) {
if (PyErr_ExceptionMatches(PyExc_AttributeError))
PyErr_Clear();
else
goto error;
- encrepr = PyObject_Repr(self->encoding);
- res = PyString_FromFormat("<_io.TextIOWrapper encoding=%s>",
- PyString_AS_STRING(encrepr));
}
else {
- encrepr = PyObject_Repr(self->encoding);
- namerepr = PyObject_Repr(nameobj);
- res = PyString_FromFormat("<_io.TextIOWrapper name=%s encoding=%s>",
- PyString_AS_STRING(namerepr),
- PyString_AS_STRING(encrepr));
+ s = PyUnicode_FromFormat(" name=%R", nameobj);
Py_DECREF(nameobj);
+ if (s == NULL)
+ goto error;
+ PyUnicode_AppendAndDel(&res, s);
+ if (res == NULL)
+ return NULL;
}
- Py_XDECREF(namerepr);
- Py_XDECREF(encrepr);
- return res;
-
+ modeobj = PyObject_GetAttrString((PyObject *) self, "mode");
+ if (modeobj == NULL) {
+ if (PyErr_ExceptionMatches(PyExc_AttributeError))
+ PyErr_Clear();
+ else
+ goto error;
+ }
+ else {
+ s = PyUnicode_FromFormat(" mode=%R", modeobj);
+ Py_DECREF(modeobj);
+ if (s == NULL)
+ goto error;
+ PyUnicode_AppendAndDel(&res, s);
+ if (res == NULL)
+ return NULL;
+ }
+ s = PyUnicode_FromFormat("%U encoding=%R>",
+ res, self->encoding);
+ Py_DECREF(res);
+ return s;
error:
- Py_XDECREF(namerepr);
- Py_XDECREF(encrepr);
+ Py_XDECREF(res);
return NULL;
}
@@ -2413,6 +2440,14 @@ textiowrapper_isatty(textio *self, PyObject *args)
}
static PyObject *
+textiowrapper_getstate(textio *self, PyObject *args)
+{
+ PyErr_Format(PyExc_TypeError,
+ "cannot serialize '%s' object", Py_TYPE(self)->tp_name);
+ return NULL;
+}
+
+static PyObject *
textiowrapper_flush(textio *self, PyObject *args)
{
CHECK_INITIALIZED(self);
@@ -2437,31 +2472,26 @@ textiowrapper_close(textio *self, PyObject *args)
Py_DECREF(res);
if (r < 0)
return NULL;
-
+
if (r > 0) {
Py_RETURN_NONE; /* stream already closed */
}
else {
- PyObject *exc = NULL, *val, *tb;
+ if (self->deallocating) {
+ res = PyObject_CallMethod(self->buffer, "_dealloc_warn", "O", self);
+ if (res)
+ Py_DECREF(res);
+ else
+ PyErr_Clear();
+ }
res = PyObject_CallMethod((PyObject *)self, "flush", NULL);
- if (res == NULL)
- PyErr_Fetch(&exc, &val, &tb);
+ if (res == NULL) {
+ return NULL;
+ }
else
Py_DECREF(res);
- res = PyObject_CallMethod(self->buffer, "close", NULL);
- if (exc != NULL) {
- if (res != NULL) {
- Py_CLEAR(res);
- PyErr_Restore(exc, val, tb);
- }
- else {
- Py_DECREF(exc);
- Py_XDECREF(val);
- Py_XDECREF(tb);
- }
- }
- return res;
+ return PyObject_CallMethod(self->buffer, "close", NULL);
}
}
@@ -2541,8 +2571,7 @@ static PyObject *
textiowrapper_errors_get(textio *self, void *context)
{
CHECK_INITIALIZED(self);
- Py_INCREF(self->errors);
- return self->errors;
+ return PyUnicode_FromString(PyBytes_AS_STRING(self->errors));
}
static PyObject *
@@ -2557,7 +2586,7 @@ textiowrapper_chunk_size_set(textio *self, PyObject *arg, void *context)
{
Py_ssize_t n;
CHECK_INITIALIZED_INT(self);
- n = PyNumber_AsSsize_t(arg, PyExc_TypeError);
+ n = PyNumber_AsSsize_t(arg, PyExc_ValueError);
if (n == -1 && PyErr_Occurred())
return -1;
if (n <= 0) {
@@ -2582,6 +2611,7 @@ static PyMethodDef textiowrapper_methods[] = {
{"readable", (PyCFunction)textiowrapper_readable, METH_NOARGS},
{"writable", (PyCFunction)textiowrapper_writable, METH_NOARGS},
{"isatty", (PyCFunction)textiowrapper_isatty, METH_NOARGS},
+ {"__getstate__", (PyCFunction)textiowrapper_getstate, METH_NOARGS},
{"seek", (PyCFunction)textiowrapper_seek, METH_VARARGS},
{"tell", (PyCFunction)textiowrapper_tell, METH_NOARGS},