summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lib/_pyio.py67
-rw-r--r--Lib/test/test_io.py14
-rw-r--r--Misc/NEWS2
-rw-r--r--Modules/_io/_iomodule.c36
4 files changed, 80 insertions, 39 deletions
diff --git a/Lib/_pyio.py b/Lib/_pyio.py
index b04d23a..d4cfb6e 100644
--- a/Lib/_pyio.py
+++ b/Lib/_pyio.py
@@ -200,38 +200,45 @@ def open(file, mode="r", buffering=-1, encoding=None, errors=None,
(appending and "a" or "") +
(updating and "+" or ""),
closefd, opener=opener)
- line_buffering = False
- if buffering == 1 or buffering < 0 and raw.isatty():
- buffering = -1
- line_buffering = True
- if buffering < 0:
- buffering = DEFAULT_BUFFER_SIZE
- try:
- bs = os.fstat(raw.fileno()).st_blksize
- except (OSError, AttributeError):
- pass
+ result = raw
+ try:
+ line_buffering = False
+ if buffering == 1 or buffering < 0 and raw.isatty():
+ buffering = -1
+ line_buffering = True
+ if buffering < 0:
+ buffering = DEFAULT_BUFFER_SIZE
+ try:
+ bs = os.fstat(raw.fileno()).st_blksize
+ except (OSError, AttributeError):
+ pass
+ else:
+ if bs > 1:
+ buffering = bs
+ if buffering < 0:
+ raise ValueError("invalid buffering size")
+ if buffering == 0:
+ if binary:
+ return result
+ raise ValueError("can't have unbuffered text I/O")
+ if updating:
+ buffer = BufferedRandom(raw, buffering)
+ elif creating or writing or appending:
+ buffer = BufferedWriter(raw, buffering)
+ elif reading:
+ buffer = BufferedReader(raw, buffering)
else:
- if bs > 1:
- buffering = bs
- if buffering < 0:
- raise ValueError("invalid buffering size")
- if buffering == 0:
+ raise ValueError("unknown mode: %r" % mode)
+ result = buffer
if binary:
- return raw
- raise ValueError("can't have unbuffered text I/O")
- if updating:
- buffer = BufferedRandom(raw, buffering)
- elif creating or writing or appending:
- buffer = BufferedWriter(raw, buffering)
- elif reading:
- buffer = BufferedReader(raw, buffering)
- else:
- raise ValueError("unknown mode: %r" % mode)
- if binary:
- return buffer
- text = TextIOWrapper(buffer, encoding, errors, newline, line_buffering)
- text.mode = mode
- return text
+ return result
+ text = TextIOWrapper(buffer, encoding, errors, newline, line_buffering)
+ result = text
+ text.mode = mode
+ return result
+ except:
+ result.close()
+ raise
class DocDescriptor:
diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py
index a94b85a..1cf97dd 100644
--- a/Lib/test/test_io.py
+++ b/Lib/test/test_io.py
@@ -653,6 +653,20 @@ class IOTest(unittest.TestCase):
fileio.close()
f2.readline()
+ def test_nonbuffered_textio(self):
+ with warnings.catch_warnings(record=True) as recorded:
+ with self.assertRaises(ValueError):
+ self.open(support.TESTFN, 'w', buffering=0)
+ support.gc_collect()
+ self.assertEqual(recorded, [])
+
+ def test_invalid_newline(self):
+ with warnings.catch_warnings(record=True) as recorded:
+ with self.assertRaises(ValueError):
+ self.open(support.TESTFN, 'w', newline='invalid')
+ support.gc_collect()
+ self.assertEqual(recorded, [])
+
class CIOTest(IOTest):
diff --git a/Misc/NEWS b/Misc/NEWS
index 6185590..29f5b61 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -22,6 +22,8 @@ Core and Builtins
Library
-------
+- Issue #21310: Fixed possible resource leak in failed open().
+
- Issue #21677: Fixed chaining nonnormalized exceptions in io close() methods.
- Issue #11709: Fix the pydoc.help function to not fail when sys.stdin is not a
diff --git a/Modules/_io/_iomodule.c b/Modules/_io/_iomodule.c
index be0464c..1dbd563 100644
--- a/Modules/_io/_iomodule.c
+++ b/Modules/_io/_iomodule.c
@@ -235,11 +235,12 @@ io_open(PyObject *self, PyObject *args, PyObject *kwds)
char rawmode[6], *m;
int line_buffering, isatty;
- PyObject *raw, *modeobj = NULL, *buffer = NULL, *wrapper = NULL;
+ PyObject *raw, *modeobj = NULL, *buffer, *wrapper, *result = NULL;
_Py_IDENTIFIER(isatty);
_Py_IDENTIFIER(fileno);
_Py_IDENTIFIER(mode);
+ _Py_IDENTIFIER(close);
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|sizzziO:open", kwlist,
&file, &mode, &buffering,
@@ -354,6 +355,7 @@ io_open(PyObject *self, PyObject *args, PyObject *kwds)
"OsiO", file, rawmode, closefd, opener);
if (raw == NULL)
return NULL;
+ result = raw;
modeobj = PyUnicode_FromString(mode);
if (modeobj == NULL)
@@ -412,7 +414,7 @@ io_open(PyObject *self, PyObject *args, PyObject *kwds)
}
Py_DECREF(modeobj);
- return raw;
+ return result;
}
/* wraps into a buffered file */
@@ -433,15 +435,16 @@ io_open(PyObject *self, PyObject *args, PyObject *kwds)
buffer = PyObject_CallFunction(Buffered_class, "Oi", raw, buffering);
}
- Py_CLEAR(raw);
if (buffer == NULL)
goto error;
+ result = buffer;
+ Py_DECREF(raw);
/* if binary, returns the buffered file */
if (binary) {
Py_DECREF(modeobj);
- return buffer;
+ return result;
}
/* wraps into a TextIOWrapper */
@@ -450,20 +453,35 @@ io_open(PyObject *self, PyObject *args, PyObject *kwds)
buffer,
encoding, errors, newline,
line_buffering);
- Py_CLEAR(buffer);
if (wrapper == NULL)
goto error;
+ result = wrapper;
+ Py_DECREF(buffer);
if (_PyObject_SetAttrId(wrapper, &PyId_mode, modeobj) < 0)
goto error;
Py_DECREF(modeobj);
- return wrapper;
+ return result;
error:
- Py_XDECREF(raw);
+ if (result != NULL) {
+ PyObject *exc, *val, *tb;
+ PyErr_Fetch(&exc, &val, &tb);
+ if (_PyObject_CallMethodId(result, &PyId_close, NULL) != NULL)
+ PyErr_Restore(exc, val, tb);
+ else {
+ PyObject *val2;
+ PyErr_NormalizeException(&exc, &val, &tb);
+ Py_XDECREF(exc);
+ Py_XDECREF(tb);
+ PyErr_Fetch(&exc, &val2, &tb);
+ PyErr_NormalizeException(&exc, &val2, &tb);
+ PyException_SetContext(val2, val);
+ PyErr_Restore(exc, val2, tb);
+ }
+ Py_DECREF(result);
+ }
Py_XDECREF(modeobj);
- Py_XDECREF(buffer);
- Py_XDECREF(wrapper);
return NULL;
}