Skip to content

Commit d6dfbda

Browse files
author
antoine.pitrou
committed
Issue #3860: GzipFile and BZ2File now support the context manager protocol.
git-svn-id: http://svn.python.org/projects/python/trunk@68484 6015fed2-1504-0410-9fe1-9d1591cc4771
1 parent 6a1c9ad commit d6dfbda

File tree

4 files changed

+83
-1
lines changed

4 files changed

+83
-1
lines changed

Lib/gzip.py

+8
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,14 @@ def next(self):
454454
else:
455455
raise StopIteration
456456

457+
def __enter__(self):
458+
if self.fileobj is None:
459+
raise ValueError("I/O operation on closed GzipFile object")
460+
return self
461+
462+
def __exit__(self, *args):
463+
self.close()
464+
457465

458466
def _test():
459467
# Act like gzip; with -d, act like gunzip.

Lib/test/test_bz2.py

+22
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,28 @@ def testBug1191043(self):
284284
bz2f.close()
285285
self.assertEqual(xlines, ['Test'])
286286

287+
def testContextProtocol(self):
288+
# BZ2File supports the context management protocol
289+
f = None
290+
with BZ2File(self.filename, "wb") as f:
291+
f.write(b"xxx")
292+
f = BZ2File(self.filename, "rb")
293+
f.close()
294+
try:
295+
with f:
296+
pass
297+
except ValueError:
298+
pass
299+
else:
300+
self.fail("__enter__ on a closed file didn't raise an exception")
301+
try:
302+
with BZ2File(self.filename, "wb") as f:
303+
1/0
304+
except ZeroDivisionError:
305+
pass
306+
else:
307+
self.fail("1/0 didn't raise an exception")
308+
287309

288310
class BZ2CompressorTest(BaseTest):
289311
def testCompress(self):

Lib/test/test_gzip.py

+21-1
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,6 @@ def test_mtime(self):
166166
fWrite = gzip.GzipFile(self.filename, 'w', mtime = mtime)
167167
fWrite.write(data1)
168168
fWrite.close()
169-
170169
fRead = gzip.GzipFile(self.filename)
171170
dataRead = fRead.read()
172171
self.assertEqual(dataRead, data1)
@@ -222,6 +221,27 @@ def test_metadata(self):
222221

223222
fRead.close()
224223

224+
def test_with_open(self):
225+
# GzipFile supports the context management protocol
226+
with gzip.GzipFile(self.filename, "wb") as f:
227+
f.write(b"xxx")
228+
f = gzip.GzipFile(self.filename, "rb")
229+
f.close()
230+
try:
231+
with f:
232+
pass
233+
except ValueError:
234+
pass
235+
else:
236+
self.fail("__enter__ on a closed file didn't raise an exception")
237+
try:
238+
with gzip.GzipFile(self.filename, "wb") as f:
239+
1/0
240+
except ZeroDivisionError:
241+
pass
242+
else:
243+
self.fail("1/0 didn't raise an exception")
244+
225245
def test_main(verbose=None):
226246
test_support.run_unittest(TestGzip)
227247

Modules/bz2module.c

+32
Original file line numberDiff line numberDiff line change
@@ -1201,6 +1201,36 @@ BZ2File_close(BZ2FileObject *self)
12011201
return ret;
12021202
}
12031203

1204+
PyDoc_STRVAR(BZ2File_enter_doc,
1205+
"__enter__() -> self.");
1206+
1207+
static PyObject *
1208+
BZ2File_enter(BZ2FileObject *self)
1209+
{
1210+
if (self->mode == MODE_CLOSED) {
1211+
PyErr_SetString(PyExc_ValueError,
1212+
"I/O operation on closed file");
1213+
return NULL;
1214+
}
1215+
Py_INCREF(self);
1216+
return (PyObject *) self;
1217+
}
1218+
1219+
PyDoc_STRVAR(BZ2File_exit_doc,
1220+
"__exit__(*excinfo) -> None. Closes the file.");
1221+
1222+
static PyObject *
1223+
BZ2File_exit(BZ2FileObject *self, PyObject *args)
1224+
{
1225+
PyObject *ret = PyObject_CallMethod((PyObject *) self, "close", NULL);
1226+
if (!ret)
1227+
/* If error occurred, pass through */
1228+
return NULL;
1229+
Py_DECREF(ret);
1230+
Py_RETURN_NONE;
1231+
}
1232+
1233+
12041234
static PyObject *BZ2File_getiter(BZ2FileObject *self);
12051235

12061236
static PyMethodDef BZ2File_methods[] = {
@@ -1213,6 +1243,8 @@ static PyMethodDef BZ2File_methods[] = {
12131243
{"seek", (PyCFunction)BZ2File_seek, METH_VARARGS, BZ2File_seek__doc__},
12141244
{"tell", (PyCFunction)BZ2File_tell, METH_NOARGS, BZ2File_tell__doc__},
12151245
{"close", (PyCFunction)BZ2File_close, METH_NOARGS, BZ2File_close__doc__},
1246+
{"__enter__", (PyCFunction)BZ2File_enter, METH_NOARGS, BZ2File_enter_doc},
1247+
{"__exit__", (PyCFunction)BZ2File_exit, METH_VARARGS, BZ2File_exit_doc},
12161248
{NULL, NULL} /* sentinel */
12171249
};
12181250

0 commit comments

Comments
 (0)