Issue #23865: close() methods in multiple modules now are idempotent and more

robust at shutdown. If needs to release multiple resources, they are released
even if errors are occured.
This commit is contained in:
Serhiy Storchaka 2015-04-10 13:24:10 +03:00
parent c26afcc8fc
commit 1aa2c0f073
24 changed files with 263 additions and 175 deletions

View File

@ -357,9 +357,12 @@ class Aifc_read:
self._soundpos = 0
def close(self):
if self._decomp:
self._decomp.CloseDecompressor()
decomp = self._decomp
try:
if decomp:
self._decomp = None
decomp.CloseDecompressor()
finally:
self._file.close()
def tell(self):

View File

@ -32,7 +32,8 @@ class Error(Exception):
pass
# States (what have we written)
[_DID_HEADER, _DID_DATA, _DID_RSRC] = range(3)
_DID_HEADER = 0
_DID_DATA = 1
# Various constants
REASONABLY_LARGE=32768 # Minimal amount we pass the rle-coder
@ -235,6 +236,9 @@ class BinHex:
self._write(data)
def close(self):
if self.state is None:
return
try:
if self.state < _DID_DATA:
self.close_data()
if self.state != _DID_DATA:
@ -243,9 +247,11 @@ class BinHex:
raise Error, \
"Incorrect resource-datasize, diff=%r" % (self.rlen,)
self._writecrc()
self.ofp.close()
finally:
self.state = None
ofp = self.ofp
del self.ofp
ofp.close()
def binhex(inp, out):
"""(infilename, outfilename) - Create binhex-encoded copy of a file"""
@ -463,10 +469,14 @@ class HexBin:
return self._read(n)
def close(self):
if self.state is None:
return
try:
if self.rlen:
dummy = self.read_rsrc(self.rlen)
self._checkcrc()
self.state = _DID_RSRC
finally:
self.state = None
self.ifp.close()
def hexbin(inp, out):

View File

@ -85,7 +85,9 @@ class Chunk:
def close(self):
if not self.closed:
try:
self.skip()
finally:
self.closed = True
def isatty(self):

View File

@ -124,11 +124,11 @@ class TextFile:
def close (self):
"""Close the current file and forget everything we know about it
(filename, current line number)."""
self.file.close ()
file = self.file
self.file = None
self.filename = None
self.current_line = None
file.close()
def gen_error (self, msg, line=None):

View File

@ -209,7 +209,9 @@ class _Database(UserDict.DictMixin):
return len(self._index)
def close(self):
try:
self._commit()
finally:
self._index = self._datfile = self._dirfile = self._bakfile = None
__del__ = close

View File

@ -233,7 +233,9 @@ class FileInput:
self.close()
def close(self):
try:
self.nextfile()
finally:
self._files = ()
def __iter__(self):
@ -270,14 +272,16 @@ class FileInput:
output = self._output
self._output = 0
try:
if output:
output.close()
finally:
file = self._file
self._file = 0
try:
if file and not self._isstdin:
file.close()
finally:
backupfilename = self._backupfilename
self._backupfilename = 0
if backupfilename and not self._backup:

View File

@ -594,11 +594,16 @@ class FTP:
def close(self):
'''Close the connection without assuming anything about it.'''
if self.file is not None:
self.file.close()
if self.sock is not None:
self.sock.close()
self.file = self.sock = None
try:
file = self.file
self.file = None
if file is not None:
file.close()
finally:
sock = self.sock
self.sock = None
if sock is not None:
sock.close()
try:
import ssl

View File

@ -369,19 +369,21 @@ class GzipFile(io.BufferedIOBase):
return self.fileobj is None
def close(self):
if self.fileobj is None:
fileobj = self.fileobj
if fileobj is None:
return
self.fileobj = None
try:
if self.mode == WRITE:
self.fileobj.write(self.compress.flush())
write32u(self.fileobj, self.crc)
fileobj.write(self.compress.flush())
write32u(fileobj, self.crc)
# self.size may exceed 2GB, or even 4GB
write32u(self.fileobj, self.size & 0xffffffffL)
self.fileobj = None
elif self.mode == READ:
self.fileobj = None
if self.myfileobj:
self.myfileobj.close()
write32u(fileobj, self.size & 0xffffffffL)
finally:
myfileobj = self.myfileobj
if myfileobj:
self.myfileobj = None
myfileobj.close()
def flush(self,zlib_mode=zlib.Z_SYNC_FLUSH):
self._check_closed()

View File

@ -560,9 +560,10 @@ class HTTPResponse:
return True
def close(self):
if self.fp:
self.fp.close()
fp = self.fp
if fp:
self.fp = None
fp.close()
def isclosed(self):
# NOTE: it is possible that we will not ever call self.close(). This
@ -835,13 +836,17 @@ class HTTPConnection:
def close(self):
"""Close the connection to the HTTP server."""
if self.sock:
self.sock.close() # close it manually... there may be other refs
self.sock = None
if self.__response:
self.__response.close()
self.__response = None
self.__state = _CS_IDLE
try:
sock = self.sock
if sock:
self.sock = None
sock.close() # close it manually... there may be other refs
finally:
response = self.__response
if response:
self.__response = None
response.close()
def send(self, data):
"""Send `data' to the server."""

View File

@ -915,12 +915,17 @@ class FileHandler(StreamHandler):
Closes the stream.
"""
self.acquire()
try:
try:
if self.stream:
try:
self.flush()
if hasattr(self.stream, "close"):
self.stream.close()
finally:
stream = self.stream
self.stream = None
if hasattr(stream, "close"):
stream.close()
finally:
# Issue #19523: call unconditionally to
# prevent a handler leak when delay is set
StreamHandler.close(self)

View File

@ -588,9 +588,10 @@ class SocketHandler(logging.Handler):
"""
self.acquire()
try:
if self.sock:
self.sock.close()
sock = self.sock
if sock:
self.sock = None
sock.close()
finally:
self.release()
logging.Handler.close(self)
@ -1160,7 +1161,9 @@ class BufferingHandler(logging.Handler):
This version just flushes and chains to the parent class' close().
"""
try:
self.flush()
finally:
logging.Handler.close(self)
class MemoryHandler(BufferingHandler):
@ -1213,7 +1216,9 @@ class MemoryHandler(BufferingHandler):
"""
Flush, set the target to None and lose the buffer.
"""
try:
self.flush()
finally:
self.acquire()
try:
self.target = None

View File

@ -719,9 +719,13 @@ class _singlefileMailbox(Mailbox):
def close(self):
"""Flush and close the mailbox."""
try:
self.flush()
finally:
try:
if self._locked:
self.unlock()
finally:
self._file.close() # Sync has been done by self.flush() above.
def _lookup(self, key=None):

View File

@ -285,9 +285,13 @@ class SocketListener(object):
return conn
def close(self):
try:
self._socket.close()
if self._unlink is not None:
self._unlink()
finally:
unlink = self._unlink
if unlink is not None:
self._unlink = None
unlink()
def SocketClient(address):

View File

@ -156,9 +156,13 @@ class Queue(object):
def close(self):
self._closed = True
try:
self._reader.close()
if self._close:
self._close()
finally:
close = self._close
if close:
self._close = None
close()
def join_thread(self):
debug('Queue.join_thread()')

View File

@ -140,16 +140,20 @@ class Shelf(UserDict.DictMixin):
pass
def close(self):
if self.dict is None:
return
try:
self.sync()
try:
self.dict.close()
except AttributeError:
pass
finally:
# Catch errors that may happen when close is called from __del__
# because CPython is in interpreter shutdown.
try:
self.dict = _ClosedDict()
except (NameError, TypeError):
except:
self.dict = None
def __del__(self):

View File

@ -750,12 +750,16 @@ class SMTP:
def close(self):
"""Close the connection to the SMTP server."""
if self.file:
self.file.close()
try:
file = self.file
self.file = None
if self.sock:
self.sock.close()
if file:
file.close()
finally:
sock = self.sock
self.sock = None
if sock:
sock.close()
def quit(self):

View File

@ -492,6 +492,8 @@ class _Stream:
if self.closed:
return
self.closed = True
try:
if self.mode == "w" and self.comptype != "tar":
self.buf += self.cmp.flush()
@ -507,12 +509,10 @@ class _Stream:
# it to look positive on all boxes.
self.fileobj.write(struct.pack("<L", self.crc & 0xffffffffL))
self.fileobj.write(struct.pack("<L", self.pos & 0xffffFFFFL))
finally:
if not self._extfileobj:
self.fileobj.close()
self.closed = True
def _init_read_gz(self):
"""Initialize for reading a gzip compressed fileobj.
"""
@ -1796,6 +1796,8 @@ class TarFile(object):
if self.closed:
return
self.closed = True
try:
if self.mode in "aw":
self.fileobj.write(NUL * (BLOCKSIZE * 2))
self.offset += (BLOCKSIZE * 2)
@ -1804,10 +1806,9 @@ class TarFile(object):
blocks, remainder = divmod(self.offset, RECORDSIZE)
if remainder > 0:
self.fileobj.write(NUL * (RECORDSIZE - remainder))
finally:
if not self._extfileobj:
self.fileobj.close()
self.closed = True
def getmember(self, name):
"""Return a TarInfo object for member `name'. If `name' can not be

View File

@ -254,12 +254,13 @@ class Telnet:
def close(self):
"""Close the connection."""
if self.sock:
self.sock.close()
sock = self.sock
self.sock = 0
self.eof = 1
self.iacseq = ''
self.sb = 0
if sock:
sock.close()
def get_socket(self):
"""Return the socket object used internally."""

View File

@ -413,7 +413,9 @@ class _TemporaryFileWrapper:
def close(self):
if not self.close_called:
self.close_called = True
try:
self.file.close()
finally:
if self.delete:
self.unlink(self.name)

View File

@ -994,12 +994,17 @@ class addclosehook(addbase):
self.hookargs = hookargs
def close(self):
if self.closehook:
self.closehook(*self.hookargs)
try:
closehook = self.closehook
hookargs = self.hookargs
if closehook:
self.closehook = None
self.hookargs = None
closehook(*hookargs)
finally:
addbase.close(self)
class addinfo(addbase):
"""class to add an info() method to an open file."""

View File

@ -180,10 +180,11 @@ class Wave_read:
self._soundpos = 0
def close(self):
if self._i_opened_the_file:
self._i_opened_the_file.close()
self._i_opened_the_file = None
self._file = None
file = self._i_opened_the_file
if file:
self._i_opened_the_file = None
file.close()
def tell(self):
return self._soundpos
@ -444,17 +445,18 @@ class Wave_write:
self._patchheader()
def close(self):
if self._file:
try:
if self._file:
self._ensure_header_written(0)
if self._datalength != self._datawritten:
self._patchheader()
self._file.flush()
finally:
self._file = None
if self._i_opened_the_file:
self._i_opened_the_file.close()
file = self._i_opened_the_file
if file:
self._i_opened_the_file = None
file.close()
#
# Internal methods.

View File

@ -214,11 +214,13 @@ class ExpatParser(xmlreader.IncrementalParser, xmlreader.Locator):
self._err_handler.fatalError(exc)
def close(self):
if self._entity_stack:
if self._entity_stack or self._parser is None:
# If we are completing an external entity, do nothing here
return
try:
self.feed("", isFinal = 1)
self._cont_handler.endDocument()
finally:
self._parsing = 0
# break cycle created by expat handlers pointing to our methods
self._parser = None

View File

@ -558,8 +558,13 @@ else:
self._parser.Parse(data, 0)
def close(self):
self._parser.Parse("", 1) # end of data
try:
parser = self._parser
except AttributeError:
pass
else:
del self._target, self._parser # get rid of circular references
parser.Parse("", 1) # end of data
class SlowParser:
"""Default XML parser (based on xmllib.XMLParser)."""
@ -1214,7 +1219,9 @@ class GzipDecodedResponse(gzip.GzipFile if gzip else object):
gzip.GzipFile.__init__(self, mode="rb", fileobj=self.stringio)
def close(self):
try:
gzip.GzipFile.close(self)
finally:
self.stringio.close()
@ -1384,9 +1391,10 @@ class Transport:
# Used in the event of socket errors.
#
def close(self):
if self._connection[1]:
self._connection[1].close()
host, connection = self._connection
if connection:
self._connection = (None, None)
connection.close()
##
# Send request header.

View File

@ -21,6 +21,10 @@ Core and Builtins
Library
-------
- Issue #23865: close() methods in multiple modules now are idempotent and more
robust at shutdown. If needs to release multiple resources, they are released
even if errors are occured.
- Issue #23881: urllib.ftpwrapper constructor now closes the socket if the FTP
connection failed.