# HG changeset patch # User Antoine Pitrou # Date 1229622663 -3600 diff -r 02d00e0ffb16 -r b69edf8ec561 Lib/gzip.py --- a/Lib/gzip.py +++ b/Lib/gzip.py @@ -441,6 +441,14 @@ class GzipFile: else: raise StopIteration + def __enter__(self): + if self.fileobj is None: + raise ValueError("I/O operation on closed GzipFile object") + return self + + def __exit__(self, *args): + self.close() + def _test(): # Act like gzip; with -d, act like gunzip. diff -r 02d00e0ffb16 -r b69edf8ec561 Lib/test/test_bz2.py --- a/Lib/test/test_bz2.py +++ b/Lib/test/test_bz2.py @@ -284,6 +284,28 @@ class BZ2FileTest(BaseTest): bz2f.close() self.assertEqual(xlines, ['Test']) + def testContextProtocol(self): + # BZ2File supports the context management protocol + f = None + with BZ2File(self.filename, "wb") as f: + f.write(b"xxx") + f = BZ2File(self.filename, "rb") + f.close() + try: + with f: + pass + except ValueError: + pass + else: + self.fail("__enter__ on a closed file didn't raise an exception") + try: + with BZ2File(self.filename, "wb") as f: + 1/0 + except ZeroDivisionError: + pass + else: + self.fail("1/0 didn't raise an exception") + class BZ2CompressorTest(BaseTest): def testCompress(self): diff -r 02d00e0ffb16 -r b69edf8ec561 Lib/test/test_gzip.py --- a/Lib/test/test_gzip.py +++ b/Lib/test/test_gzip.py @@ -160,6 +160,27 @@ class TestGzip(unittest.TestCase): self.assertEqual(f.name, self.filename) f.close() + def test_with_open(self): + # GzipFile supports the context management protocol + with gzip.GzipFile(self.filename, "wb") as f: + f.write(b"xxx") + f = gzip.GzipFile(self.filename, "rb") + f.close() + try: + with f: + pass + except ValueError: + pass + else: + self.fail("__enter__ on a closed file didn't raise an exception") + try: + with gzip.GzipFile(self.filename, "wb") as f: + 1/0 + except ZeroDivisionError: + pass + else: + self.fail("1/0 didn't raise an exception") + def test_main(verbose=None): test_support.run_unittest(TestGzip) diff -r 02d00e0ffb16 -r b69edf8ec561 Modules/bz2module.c --- a/Modules/bz2module.c +++ b/Modules/bz2module.c @@ -1201,6 +1201,36 @@ BZ2File_close(BZ2FileObject *self) return ret; } +PyDoc_STRVAR(BZ2File_enter_doc, +"__enter__() -> self."); + +static PyObject * +BZ2File_enter(BZ2FileObject *self) +{ + if (self->mode == MODE_CLOSED) { + PyErr_SetString(PyExc_ValueError, + "I/O operation on closed file"); + return NULL; + } + Py_INCREF(self); + return (PyObject *) self; +} + +PyDoc_STRVAR(BZ2File_exit_doc, +"__exit__(*excinfo) -> None. Closes the file."); + +static PyObject * +BZ2File_exit(BZ2FileObject *self, PyObject *args) +{ + PyObject *ret = PyObject_CallMethod((PyObject *) self, "close", NULL); + if (!ret) + /* If error occurred, pass through */ + return NULL; + Py_DECREF(ret); + Py_RETURN_NONE; +} + + static PyObject *BZ2File_getiter(BZ2FileObject *self); static PyMethodDef BZ2File_methods[] = { @@ -1213,6 +1243,8 @@ static PyMethodDef BZ2File_methods[] = { {"seek", (PyCFunction)BZ2File_seek, METH_VARARGS, BZ2File_seek__doc__}, {"tell", (PyCFunction)BZ2File_tell, METH_NOARGS, BZ2File_tell__doc__}, {"close", (PyCFunction)BZ2File_close, METH_NOARGS, BZ2File_close__doc__}, + {"__enter__", (PyCFunction)BZ2File_enter, METH_NOARGS, BZ2File_enter_doc}, + {"__exit__", (PyCFunction)BZ2File_exit, METH_VARARGS, BZ2File_exit_doc}, {NULL, NULL} /* sentinel */ };