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:41 +03:00
parent 842f00e725
commit 7e7a3dba5f
27 changed files with 299 additions and 197 deletions

View file

@ -356,7 +356,10 @@ def rewind(self):
self._soundpos = 0
def close(self):
self._file.close()
file = self._file
if file is not None:
self._file = None
file.close()
def tell(self):
return self._soundpos

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
@ -213,16 +214,21 @@ def write_rsrc(self, data):
self._write(data)
def close(self):
if self.state < _DID_DATA:
self.close_data()
if self.state != _DID_DATA:
raise Error('Close at the wrong time')
if self.rlen != 0:
raise Error("Incorrect resource-datasize, diff=%r" % (self.rlen,))
self._writecrc()
self.ofp.close()
self.state = None
del self.ofp
if self.state is None:
return
try:
if self.state < _DID_DATA:
self.close_data()
if self.state != _DID_DATA:
raise Error('Close at the wrong time')
if self.rlen != 0:
raise Error("Incorrect resource-datasize, diff=%r" % (self.rlen,))
self._writecrc()
finally:
self.state = None
ofp = self.ofp
del self.ofp
ofp.close()
def binhex(inp, out):
"""binhex(infilename, outfilename): create binhex-encoded copy of a file"""
@ -436,11 +442,15 @@ def read_rsrc(self, *n):
return self._read(n)
def close(self):
if self.rlen:
dummy = self.read_rsrc(self.rlen)
self._checkcrc()
self.state = _DID_RSRC
self.ifp.close()
if self.state is None:
return
try:
if self.rlen:
dummy = self.read_rsrc(self.rlen)
self._checkcrc()
finally:
self.state = None
self.ifp.close()
def hexbin(inp, out):
"""hexbin(infilename, outfilename) - Decode binhexed file"""

View file

@ -85,8 +85,10 @@ def getsize(self):
def close(self):
if not self.closed:
self.skip()
self.closed = True
try:
self.skip()
finally:
self.closed = True
def isatty(self):
if self.closed:

View file

@ -248,8 +248,10 @@ def __len__(self):
raise error('DBM object has already been closed') from None
def close(self):
self._commit()
self._index = self._datfile = self._dirfile = self._bakfile = None
try:
self._commit()
finally:
self._index = self._datfile = self._dirfile = self._bakfile = None
__del__ = close

View file

@ -118,10 +118,11 @@ def open(self, filename):
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):
outmsg = []

View file

@ -238,8 +238,10 @@ def __del__(self):
self.close()
def close(self):
self.nextfile()
self._files = ()
try:
self.nextfile()
finally:
self._files = ()
def __enter__(self):
return self
@ -281,23 +283,25 @@ def nextfile(self):
output = self._output
self._output = 0
if output:
output.close()
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:
try: os.unlink(backupfilename)
except OSError: pass
file = self._file
self._file = 0
if file and not self._isstdin:
file.close()
backupfilename = self._backupfilename
self._backupfilename = 0
if backupfilename and not self._backup:
try: os.unlink(backupfilename)
except OSError: pass
self._isstdin = False
self._buffer = []
self._bufindex = 0
self._isstdin = False
self._buffer = []
self._bufindex = 0
def readline(self):
try:

View file

@ -667,11 +667,16 @@ def quit(self):
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

@ -500,19 +500,21 @@ def closed(self):
return self.fileobj is None
def close(self):
if self.fileobj is None:
fileobj = self.fileobj
if fileobj is None:
return
if self.mode == WRITE:
self.fileobj.write(self.compress.flush())
write32u(self.fileobj, self.crc)
# self.size may exceed 2GB, or even 4GB
write32u(self.fileobj, self.size & 0xffffffff)
self.fileobj = None
elif self.mode == READ:
self.fileobj = None
if self.myfileobj:
self.myfileobj.close()
self.myfileobj = None
self.fileobj = None
try:
if self.mode == WRITE:
fileobj.write(self.compress.flush())
write32u(fileobj, self.crc)
# self.size may exceed 2GB, or even 4GB
write32u(fileobj, self.size & 0xffffffff)
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

@ -492,9 +492,11 @@ def _close_conn(self):
fp.close()
def close(self):
super().close() # set "closed" flag
if self.fp:
self._close_conn()
try:
super().close() # set "closed" flag
finally:
if self.fp:
self._close_conn()
# These implementations are for the benefit of io.BufferedReader.
@ -873,13 +875,17 @@ def connect(self):
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

@ -1011,14 +1011,19 @@ def close(self):
"""
self.acquire()
try:
if self.stream:
self.flush()
if hasattr(self.stream, "close"):
self.stream.close()
self.stream = None
# Issue #19523: call unconditionally to
# prevent a handler leak when delay is set
StreamHandler.close(self)
try:
if self.stream:
try:
self.flush()
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)
finally:
self.release()

View file

@ -627,9 +627,10 @@ def close(self):
"""
self.acquire()
try:
if self.sock:
self.sock.close()
sock = self.sock
if sock:
self.sock = None
sock.close()
logging.Handler.close(self)
finally:
self.release()
@ -1213,8 +1214,10 @@ def close(self):
This version just flushes and chains to the parent class' close().
"""
self.flush()
logging.Handler.close(self)
try:
self.flush()
finally:
logging.Handler.close(self)
class MemoryHandler(BufferingHandler):
"""
@ -1268,13 +1271,15 @@ def close(self):
"""
Flush, set the target to None and lose the buffer.
"""
self.flush()
self.acquire()
try:
self.target = None
BufferingHandler.close(self)
self.flush()
finally:
self.release()
self.acquire()
try:
self.target = None
BufferingHandler.close(self)
finally:
self.release()
class QueueHandler(logging.Handler):

View file

@ -722,10 +722,14 @@ def _post_message_hook(self, f):
def close(self):
"""Flush and close the mailbox."""
self.flush()
if self._locked:
self.unlock()
self._file.close() # Sync has been done by self.flush() above.
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):
"""Return (start, stop) or raise KeyError."""

View file

@ -469,9 +469,10 @@ def close(self):
'''
Close the bound socket or named pipe of `self`.
'''
if self._listener is not None:
self._listener.close()
listener = self._listener
if listener is not None:
self._listener = None
listener.close()
address = property(lambda self: self._listener._address)
last_accepted = property(lambda self: self._listener._last_accepted)
@ -609,9 +610,13 @@ def accept(self):
return Connection(s.detach())
def close(self):
self._socket.close()
if self._unlink is not None:
self._unlink()
try:
self._socket.close()
finally:
unlink = self._unlink
if unlink is not None:
self._unlink = None
unlink()
def SocketClient(address):

View file

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

View file

@ -276,18 +276,23 @@ def quit(self):
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:
try:
self.sock.shutdown(socket.SHUT_RDWR)
except OSError as e:
# The server might already have closed the connection
if e.errno != errno.ENOTCONN:
raise
finally:
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:
try:
sock.shutdown(socket.SHUT_RDWR)
except OSError as e:
# The server might already have closed the connection
if e.errno != errno.ENOTCONN:
raise
finally:
sock.close()
#__del__ = quit

View file

@ -445,8 +445,10 @@ def select(self, timeout=None):
return ready
def close(self):
self._epoll.close()
super().close()
try:
self._epoll.close()
finally:
super().close()
if hasattr(select, 'kqueue'):
@ -517,8 +519,10 @@ def select(self, timeout=None):
return ready
def close(self):
self._kqueue.close()
super().close()
try:
self._kqueue.close()
finally:
super().close()
# Choose the best implementation: roughly, epoll|kqueue > poll > select.

View file

@ -138,17 +138,21 @@ def __exit__(self, type, value, traceback):
self.close()
def close(self):
self.sync()
if self.dict is None:
return
try:
self.dict.close()
except AttributeError:
pass
# Catch errors that may happen when close is called from __del__
# because CPython is in interpreter shutdown.
try:
self.dict = _ClosedDict()
except (NameError, TypeError):
self.dict = None
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:
self.dict = None
def __del__(self):
if not hasattr(self, 'writeback'):

View file

@ -855,12 +855,16 @@ def send_message(self, msg, from_addr=None, to_addrs=None,
def close(self):
"""Close the connection to the SMTP server."""
if self.file:
self.file.close()
self.file = None
if self.sock:
self.sock.close()
self.sock = None
try:
file = self.file
self.file = None
if file:
file.close()
finally:
sock = self.sock
self.sock = None
if sock:
sock.close()
def quit(self):
"""Terminate the SMTP session."""

View file

@ -295,9 +295,11 @@ def setpos(self, pos):
self._soundpos = pos
def close(self):
if self._opened and self._file:
self._file.close()
self._file = None
file = self._file
if file:
self._file = None
if self._opened:
file.close()
class Au_write:
@ -438,9 +440,10 @@ def close(self):
self._patchheader()
self._file.flush()
finally:
if self._opened and self._file:
self._file.close()
file = self._file
self._file = None
if self._opened:
file.close()
#
# private methods

View file

@ -449,26 +449,26 @@ def close(self):
if self.closed:
return
if self.mode == "w" and self.comptype != "tar":
self.buf += self.cmp.flush()
if self.mode == "w" and self.buf:
self.fileobj.write(self.buf)
self.buf = b""
if self.comptype == "gz":
# The native zlib crc is an unsigned 32-bit integer, but
# the Python wrapper implicitly casts that to a signed C
# long. So, on a 32-bit box self.crc may "look negative",
# while the same crc on a 64-bit box may "look positive".
# To avoid irksome warnings from the `struct` module, force
# it to look positive on all boxes.
self.fileobj.write(struct.pack("<L", self.crc & 0xffffffff))
self.fileobj.write(struct.pack("<L", self.pos & 0xffffFFFF))
if not self._extfileobj:
self.fileobj.close()
self.closed = True
try:
if self.mode == "w" and self.comptype != "tar":
self.buf += self.cmp.flush()
if self.mode == "w" and self.buf:
self.fileobj.write(self.buf)
self.buf = b""
if self.comptype == "gz":
# The native zlib crc is an unsigned 32-bit integer, but
# the Python wrapper implicitly casts that to a signed C
# long. So, on a 32-bit box self.crc may "look negative",
# while the same crc on a 64-bit box may "look positive".
# To avoid irksome warnings from the `struct` module, force
# it to look positive on all boxes.
self.fileobj.write(struct.pack("<L", self.crc & 0xffffffff))
self.fileobj.write(struct.pack("<L", self.pos & 0xffffFFFF))
finally:
if not self._extfileobj:
self.fileobj.close()
def _init_read_gz(self):
"""Initialize for reading a gzip compressed fileobj.
@ -1705,18 +1705,19 @@ def close(self):
if self.closed:
return
if self.mode in "aw":
self.fileobj.write(NUL * (BLOCKSIZE * 2))
self.offset += (BLOCKSIZE * 2)
# fill up the end with zero-blocks
# (like option -b20 for tar does)
blocks, remainder = divmod(self.offset, RECORDSIZE)
if remainder > 0:
self.fileobj.write(NUL * (RECORDSIZE - remainder))
if not self._extfileobj:
self.fileobj.close()
self.closed = True
try:
if self.mode in "aw":
self.fileobj.write(NUL * (BLOCKSIZE * 2))
self.offset += (BLOCKSIZE * 2)
# fill up the end with zero-blocks
# (like option -b20 for tar does)
blocks, remainder = divmod(self.offset, RECORDSIZE)
if remainder > 0:
self.fileobj.write(NUL * (RECORDSIZE - remainder))
finally:
if not self._extfileobj:
self.fileobj.close()
def getmember(self, name):
"""Return a TarInfo object for member `name'. If `name' can not be

View file

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

View file

@ -357,9 +357,11 @@ def __init__(self, file, name, delete=True):
def close(self, unlink=_os.unlink):
if not self.close_called and self.file is not None:
self.close_called = True
self.file.close()
if self.delete:
unlink(self.name)
try:
self.file.close()
finally:
if self.delete:
unlink(self.name)
# Need to ensure the file is deleted on __del__
def __del__(self):

View file

@ -43,11 +43,15 @@ def __init__(self, fp, closehook, *hookargs):
self.hookargs = hookargs
def close(self):
if self.closehook:
self.closehook(*self.hookargs)
self.closehook = None
self.hookargs = None
super(addclosehook, self).close()
try:
closehook = self.closehook
hookargs = self.hookargs
if closehook:
self.closehook = None
self.hookargs = None
closehook(*hookargs)
finally:
super(addclosehook, self).close()
class addinfo(addbase):

View file

@ -186,10 +186,11 @@ def rewind(self):
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
@ -428,17 +429,18 @@ def writeframes(self, data):
self._patchheader()
def close(self):
if self._file:
try:
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()
self._i_opened_the_file = None
finally:
self._file = None
file = self._i_opened_the_file
if file:
self._i_opened_the_file = None
file.close()
#
# Internal methods.

View file

@ -211,17 +211,19 @@ def feed(self, data, isFinal = 0):
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
self.feed("", isFinal = 1)
self._cont_handler.endDocument()
self._parsing = 0
# break cycle created by expat handlers pointing to our methods
self._parser = None
bs = self._source.getByteStream()
if bs is not None:
bs.close()
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
bs = self._source.getByteStream()
if bs is not None:
bs.close()
def _reset_cont_handler(self):
self._parser.ProcessingInstructionHandler = \

View file

@ -446,8 +446,13 @@ def feed(self, data):
self._parser.Parse(data, 0)
def close(self):
self._parser.Parse("", 1) # end of data
del self._target, self._parser # get rid of circular references
try:
parser = self._parser
except AttributeError:
pass
else:
del self._target, self._parser # get rid of circular references
parser.Parse(b"", True) # end of data
# --------------------------------------------------------------------
# XML-RPC marshalling and unmarshalling code
@ -1079,8 +1084,10 @@ def __init__(self, response):
gzip.GzipFile.__init__(self, mode="rb", fileobj=self.io)
def close(self):
gzip.GzipFile.close(self)
self.io.close()
try:
gzip.GzipFile.close(self)
finally:
self.io.close()
# --------------------------------------------------------------------
@ -1235,9 +1242,10 @@ def make_connection(self, host):
# 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 HTTP request.

View file

@ -24,6 +24,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.request.ftpwrapper constructor now closes the socket if
the FTP connection failed to fix a ResourceWarning.