The read_complete flag lets us know if all of the wrapped file's data has been read. We want to know this because Browser.back() must .reload() the response if not. I've noted here the various cases where .read_complete may be set. >>> text = "To err is human, to moo, bovine.\n"*10 >>> def get_wrapper(): ... import cStringIO ... from mechanize._response import seek_wrapper ... f = cStringIO.StringIO(text) ... wr = seek_wrapper(f) ... return wr .read() case #1 >>> wr = get_wrapper() >>> wr.read_complete False >>> junk = wr.read() >>> wr.read_complete True >>> wr.seek(0) >>> wr.read_complete True Excercise partial .read() and .readline(), and .seek() case #1 >>> wr = get_wrapper() >>> junk = wr.read(10) >>> wr.read_complete False >>> junk = wr.readline() >>> wr.read_complete False >>> wr.seek(0, 2) >>> wr.read_complete True >>> wr.seek(0) >>> wr.read_complete True .readlines() case #1 >>> wr = get_wrapper() >>> junk = wr.readlines() >>> wr.read_complete True >>> wr.seek(0) >>> wr.read_complete True .seek() case #2 >>> wr = get_wrapper() >>> wr.seek(10) >>> wr.read_complete False >>> wr.seek(1000000) .read() case #2 >>> wr = get_wrapper() >>> junk = wr.read(1000000) >>> wr.read_complete # we read to the end, but don't know it yet False >>> junk = wr.read(10) >>> wr.read_complete True .readline() case #1 >>> wr = get_wrapper() >>> junk = wr.read(len(text)-10) >>> wr.read_complete False >>> junk = wr.readline() >>> wr.read_complete # we read to the end, but don't know it yet False >>> junk = wr.readline() >>> wr.read_complete True Test copying and sharing of .read_complete state >>> import copy >>> wr = get_wrapper() >>> wr2 = copy.copy(wr) >>> wr.read_complete False >>> wr2.read_complete False >>> junk = wr2.read() >>> wr.read_complete True >>> wr2.read_complete True Fix from -r36082: .read() after .close() used to break .read_complete state >>> from mechanize._response import test_response >>> r = test_response(text) >>> junk = r.read(64) >>> r.close() >>> r.read_complete False >>> r.read() '' >>> r.read_complete False Tests for the truly horrendous upgrade_response() >>> def is_response(r): ... names = "get_data read readline readlines close seek code msg".split() ... for name in names: ... if not hasattr(r, name): ... return False ... return r.get_data() == "test data" >>> from cStringIO import StringIO >>> from mechanize._response import upgrade_response, make_headers, \ ... make_response, closeable_response, seek_wrapper >>> data="test data"; url="http://example.com/"; code=200; msg="OK" Normal response (closeable_response wrapped with seek_wrapper): return a copy >>> r1 = make_response(data, [], url, code, msg) >>> r2 = upgrade_response(r1) >>> is_response(r2) True >>> r1 is not r2 True >>> r1.wrapped is r2.wrapped True closeable_response with no seek_wrapper: wrap with seek_wrapper >>> r1 = closeable_response(StringIO(data), make_headers([]), url, code, msg) >>> is_response(r1) False >>> r2 = upgrade_response(r1) >>> is_response(r2) True >>> r1 is not r2 True >>> r1 is r2.wrapped True urllib2.addinfourl: extract .fp and wrap it with closeable_response and seek_wrapper >>> import urllib2 >>> r1= urllib2.addinfourl(StringIO(data), make_headers([]), url) >>> is_response(r1) False >>> r2 = upgrade_response(r1) >>> is_response(r2) True >>> r1 is not r2 True >>> r1 is not r2.wrapped True >>> r1.fp is r2.wrapped.fp True addinfourl with code, msg >>> r1= urllib2.addinfourl(StringIO(data), make_headers([]), url) >>> r1.code = 206 >>> r1.msg = "cool" >>> r2 = upgrade_response(r1) >>> is_response(r2) True >>> r2.code == r1.code True >>> r2.msg == r1.msg True addinfourl with seek wrapper: cached data is not lost >>> r1= urllib2.addinfourl(StringIO(data), make_headers([]), url) >>> r1 = seek_wrapper(r1) >>> r1.read(4) 'test' >>> r2 = upgrade_response(r1) >>> is_response(r2) True addinfourl wrapped with HTTPError -- remains an HTTPError of the same subclass (through horrible trickery) >>> hdrs = make_headers([]) >>> r1 = urllib2.addinfourl(StringIO(data), hdrs, url) >>> class MyHTTPError(urllib2.HTTPError): pass >>> r1 = MyHTTPError(url, code, msg, hdrs, r1) >>> is_response(r1) False >>> r2 = upgrade_response(r1) >>> is_response(r2) True >>> isinstance(r2, MyHTTPError) True >>> r2 # doctest: +ELLIPSIS >> r3 = upgrade_response(r2) >>> is_response(r3) True >>> r3 is not r2 True >>> r3.wrapped is r2.wrapped True Test dynamically-created class __repr__ for case where we have the module name >>> r4 = urllib2.addinfourl(StringIO(data), hdrs, url) >>> r4 = urllib2.HTTPError(url, code, msg, hdrs, r4) >>> upgrade_response(r4) # doctest: +ELLIPSIS