Commit 1aa2c0f0 authored by Serhiy Storchaka's avatar Serhiy Storchaka

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.
parent c26afcc8
...@@ -357,10 +357,13 @@ class Aifc_read: ...@@ -357,10 +357,13 @@ class Aifc_read:
self._soundpos = 0 self._soundpos = 0
def close(self): def close(self):
if self._decomp: decomp = self._decomp
self._decomp.CloseDecompressor() try:
self._decomp = None if decomp:
self._file.close() self._decomp = None
decomp.CloseDecompressor()
finally:
self._file.close()
def tell(self): def tell(self):
return self._soundpos return self._soundpos
......
...@@ -32,7 +32,8 @@ class Error(Exception): ...@@ -32,7 +32,8 @@ class Error(Exception):
pass pass
# States (what have we written) # States (what have we written)
[_DID_HEADER, _DID_DATA, _DID_RSRC] = range(3) _DID_HEADER = 0
_DID_DATA = 1
# Various constants # Various constants
REASONABLY_LARGE=32768 # Minimal amount we pass the rle-coder REASONABLY_LARGE=32768 # Minimal amount we pass the rle-coder
...@@ -235,17 +236,22 @@ class BinHex: ...@@ -235,17 +236,22 @@ class BinHex:
self._write(data) self._write(data)
def close(self): def close(self):
if self.state < _DID_DATA: if self.state is None:
self.close_data() return
if self.state != _DID_DATA: try:
raise Error, 'Close at the wrong time' if self.state < _DID_DATA:
if self.rlen != 0: self.close_data()
raise Error, \ if self.state != _DID_DATA:
"Incorrect resource-datasize, diff=%r" % (self.rlen,) raise Error, 'Close at the wrong time'
self._writecrc() if self.rlen != 0:
self.ofp.close() raise Error, \
self.state = None "Incorrect resource-datasize, diff=%r" % (self.rlen,)
del self.ofp self._writecrc()
finally:
self.state = None
ofp = self.ofp
del self.ofp
ofp.close()
def binhex(inp, out): def binhex(inp, out):
"""(infilename, outfilename) - Create binhex-encoded copy of a file""" """(infilename, outfilename) - Create binhex-encoded copy of a file"""
...@@ -463,11 +469,15 @@ class HexBin: ...@@ -463,11 +469,15 @@ class HexBin:
return self._read(n) return self._read(n)
def close(self): def close(self):
if self.rlen: if self.state is None:
dummy = self.read_rsrc(self.rlen) return
self._checkcrc() try:
self.state = _DID_RSRC if self.rlen:
self.ifp.close() dummy = self.read_rsrc(self.rlen)
self._checkcrc()
finally:
self.state = None
self.ifp.close()
def hexbin(inp, out): def hexbin(inp, out):
"""(infilename, outfilename) - Decode binhexed file""" """(infilename, outfilename) - Decode binhexed file"""
......
...@@ -85,8 +85,10 @@ class Chunk: ...@@ -85,8 +85,10 @@ class Chunk:
def close(self): def close(self):
if not self.closed: if not self.closed:
self.skip() try:
self.closed = True self.skip()
finally:
self.closed = True
def isatty(self): def isatty(self):
if self.closed: if self.closed:
......
...@@ -124,11 +124,11 @@ class TextFile: ...@@ -124,11 +124,11 @@ class TextFile:
def close (self): def close (self):
"""Close the current file and forget everything we know about it """Close the current file and forget everything we know about it
(filename, current line number).""" (filename, current line number)."""
file = self.file
self.file.close ()
self.file = None self.file = None
self.filename = None self.filename = None
self.current_line = None self.current_line = None
file.close()
def gen_error (self, msg, line=None): def gen_error (self, msg, line=None):
......
...@@ -209,8 +209,10 @@ class _Database(UserDict.DictMixin): ...@@ -209,8 +209,10 @@ class _Database(UserDict.DictMixin):
return len(self._index) return len(self._index)
def close(self): def close(self):
self._commit() try:
self._index = self._datfile = self._dirfile = self._bakfile = None self._commit()
finally:
self._index = self._datfile = self._dirfile = self._bakfile = None
__del__ = close __del__ = close
......
...@@ -233,8 +233,10 @@ class FileInput: ...@@ -233,8 +233,10 @@ class FileInput:
self.close() self.close()
def close(self): def close(self):
self.nextfile() try:
self._files = () self.nextfile()
finally:
self._files = ()
def __iter__(self): def __iter__(self):
return self return self
...@@ -270,23 +272,25 @@ class FileInput: ...@@ -270,23 +272,25 @@ class FileInput:
output = self._output output = self._output
self._output = 0 self._output = 0
if output: try:
output.close() if output:
output.close()
file = self._file finally:
self._file = 0 file = self._file
if file and not self._isstdin: self._file = 0
file.close() try:
if file and not self._isstdin:
backupfilename = self._backupfilename file.close()
self._backupfilename = 0 finally:
if backupfilename and not self._backup: backupfilename = self._backupfilename
try: os.unlink(backupfilename) self._backupfilename = 0
except OSError: pass if backupfilename and not self._backup:
try: os.unlink(backupfilename)
self._isstdin = False except OSError: pass
self._buffer = []
self._bufindex = 0 self._isstdin = False
self._buffer = []
self._bufindex = 0
def readline(self): def readline(self):
try: try:
......
...@@ -594,11 +594,16 @@ class FTP: ...@@ -594,11 +594,16 @@ class FTP:
def close(self): def close(self):
'''Close the connection without assuming anything about it.''' '''Close the connection without assuming anything about it.'''
if self.file is not None: try:
self.file.close() file = self.file
if self.sock is not None: self.file = None
self.sock.close() if file is not None:
self.file = self.sock = None file.close()
finally:
sock = self.sock
self.sock = None
if sock is not None:
sock.close()
try: try:
import ssl import ssl
......
...@@ -369,19 +369,21 @@ class GzipFile(io.BufferedIOBase): ...@@ -369,19 +369,21 @@ class GzipFile(io.BufferedIOBase):
return self.fileobj is None return self.fileobj is None
def close(self): def close(self):
if self.fileobj is None: fileobj = self.fileobj
if fileobj is None:
return return
if self.mode == WRITE: self.fileobj = None
self.fileobj.write(self.compress.flush()) try:
write32u(self.fileobj, self.crc) if self.mode == WRITE:
# self.size may exceed 2GB, or even 4GB fileobj.write(self.compress.flush())
write32u(self.fileobj, self.size & 0xffffffffL) write32u(fileobj, self.crc)
self.fileobj = None # self.size may exceed 2GB, or even 4GB
elif self.mode == READ: write32u(fileobj, self.size & 0xffffffffL)
self.fileobj = None finally:
if self.myfileobj: myfileobj = self.myfileobj
self.myfileobj.close() if myfileobj:
self.myfileobj = None self.myfileobj = None
myfileobj.close()
def flush(self,zlib_mode=zlib.Z_SYNC_FLUSH): def flush(self,zlib_mode=zlib.Z_SYNC_FLUSH):
self._check_closed() self._check_closed()
......
...@@ -560,9 +560,10 @@ class HTTPResponse: ...@@ -560,9 +560,10 @@ class HTTPResponse:
return True return True
def close(self): def close(self):
if self.fp: fp = self.fp
self.fp.close() if fp:
self.fp = None self.fp = None
fp.close()
def isclosed(self): def isclosed(self):
# NOTE: it is possible that we will not ever call self.close(). This # NOTE: it is possible that we will not ever call self.close(). This
...@@ -835,13 +836,17 @@ class HTTPConnection: ...@@ -835,13 +836,17 @@ class HTTPConnection:
def close(self): def close(self):
"""Close the connection to the HTTP server.""" """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 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): def send(self, data):
"""Send `data' to the server.""" """Send `data' to the server."""
......
...@@ -916,14 +916,19 @@ class FileHandler(StreamHandler): ...@@ -916,14 +916,19 @@ class FileHandler(StreamHandler):
""" """
self.acquire() self.acquire()
try: try:
if self.stream: try:
self.flush() if self.stream:
if hasattr(self.stream, "close"): try:
self.stream.close() self.flush()
self.stream = None finally:
# Issue #19523: call unconditionally to stream = self.stream
# prevent a handler leak when delay is set self.stream = None
StreamHandler.close(self) if hasattr(stream, "close"):
stream.close()
finally:
# Issue #19523: call unconditionally to
# prevent a handler leak when delay is set
StreamHandler.close(self)
finally: finally:
self.release() self.release()
......
...@@ -588,9 +588,10 @@ class SocketHandler(logging.Handler): ...@@ -588,9 +588,10 @@ class SocketHandler(logging.Handler):
""" """
self.acquire() self.acquire()
try: try:
if self.sock: sock = self.sock
self.sock.close() if sock:
self.sock = None self.sock = None
sock.close()
finally: finally:
self.release() self.release()
logging.Handler.close(self) logging.Handler.close(self)
...@@ -1160,8 +1161,10 @@ class BufferingHandler(logging.Handler): ...@@ -1160,8 +1161,10 @@ class BufferingHandler(logging.Handler):
This version just flushes and chains to the parent class' close(). This version just flushes and chains to the parent class' close().
""" """
self.flush() try:
logging.Handler.close(self) self.flush()
finally:
logging.Handler.close(self)
class MemoryHandler(BufferingHandler): class MemoryHandler(BufferingHandler):
""" """
...@@ -1213,10 +1216,12 @@ class MemoryHandler(BufferingHandler): ...@@ -1213,10 +1216,12 @@ class MemoryHandler(BufferingHandler):
""" """
Flush, set the target to None and lose the buffer. Flush, set the target to None and lose the buffer.
""" """
self.flush()
self.acquire()
try: try:
self.target = None self.flush()
BufferingHandler.close(self)
finally: finally:
self.release() self.acquire()
try:
self.target = None
BufferingHandler.close(self)
finally:
self.release()
...@@ -719,10 +719,14 @@ class _singlefileMailbox(Mailbox): ...@@ -719,10 +719,14 @@ class _singlefileMailbox(Mailbox):
def close(self): def close(self):
"""Flush and close the mailbox.""" """Flush and close the mailbox."""
self.flush() try:
if self._locked: self.flush()
self.unlock() finally:
self._file.close() # Sync has been done by self.flush() above. try:
if self._locked:
self.unlock()
finally:
self._file.close() # Sync has been done by self.flush() above.
def _lookup(self, key=None): def _lookup(self, key=None):
"""Return (start, stop) or raise KeyError.""" """Return (start, stop) or raise KeyError."""
......
...@@ -285,9 +285,13 @@ class SocketListener(object): ...@@ -285,9 +285,13 @@ class SocketListener(object):
return conn return conn
def close(self): def close(self):
self._socket.close() try:
if self._unlink is not None: self._socket.close()
self._unlink() finally:
unlink = self._unlink
if unlink is not None:
self._unlink = None
unlink()
def SocketClient(address): def SocketClient(address):
......
...@@ -156,9 +156,13 @@ class Queue(object): ...@@ -156,9 +156,13 @@ class Queue(object):
def close(self): def close(self):
self._closed = True self._closed = True
self._reader.close() try:
if self._close: self._reader.close()
self._close() finally:
close = self._close
if close:
self._close = None
close()
def join_thread(self): def join_thread(self):
debug('Queue.join_thread()') debug('Queue.join_thread()')
......
...@@ -140,17 +140,21 @@ class Shelf(UserDict.DictMixin): ...@@ -140,17 +140,21 @@ class Shelf(UserDict.DictMixin):
pass pass
def close(self): def close(self):
self.sync() if self.dict is None:
try: return
self.dict.close()
except AttributeError:
pass
# Catch errors that may happen when close is called from __del__
# because CPython is in interpreter shutdown.
try: try:
self.dict = _ClosedDict() self.sync()
except (NameError, TypeError): try:
self.dict = None 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): def __del__(self):
if not hasattr(self, 'writeback'): if not hasattr(self, 'writeback'):
......
...@@ -750,12 +750,16 @@ class SMTP: ...@@ -750,12 +750,16 @@ class SMTP:
def close(self): def close(self):
"""Close the connection to the SMTP server.""" """Close the connection to the SMTP server."""
if self.file: try:
self.file.close() file = self.file
self.file = None self.file = None
if self.sock: if file:
self.sock.close() file.close()
self.sock = None finally:
sock = self.sock
self.sock = None
if sock:
sock.close()
def quit(self): def quit(self):
......
...@@ -492,26 +492,26 @@ class _Stream: ...@@ -492,26 +492,26 @@ class _Stream:
if self.closed: if self.closed:
return 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 = ""
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 & 0xffffffffL))
self.fileobj.write(struct.pack("<L", self.pos & 0xffffFFFFL))
if not self._extfileobj:
self.fileobj.close()
self.closed = True 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 = ""
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 & 0xffffffffL))
self.fileobj.write(struct.pack("<L", self.pos & 0xffffFFFFL))
finally:
if not self._extfileobj:
self.fileobj.close()
def _init_read_gz(self): def _init_read_gz(self):
"""Initialize for reading a gzip compressed fileobj. """Initialize for reading a gzip compressed fileobj.
...@@ -1796,18 +1796,19 @@ class TarFile(object): ...@@ -1796,18 +1796,19 @@ class TarFile(object):
if self.closed: if self.closed:
return 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 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): def getmember(self, name):
"""Return a TarInfo object for member `name'. If `name' can not be """Return a TarInfo object for member `name'. If `name' can not be
......
...@@ -254,12 +254,13 @@ class Telnet: ...@@ -254,12 +254,13 @@ class Telnet:
def close(self): def close(self):
"""Close the connection.""" """Close the connection."""
if self.sock: sock = self.sock
self.sock.close()
self.sock = 0 self.sock = 0
self.eof = 1 self.eof = 1
self.iacseq = '' self.iacseq = ''
self.sb = 0 self.sb = 0
if sock:
sock.close()
def get_socket(self): def get_socket(self):
"""Return the socket object used internally.""" """Return the socket object used internally."""
......
...@@ -413,9 +413,11 @@ class _TemporaryFileWrapper: ...@@ -413,9 +413,11 @@ class _TemporaryFileWrapper:
def close(self): def close(self):
if not self.close_called: if not self.close_called:
self.close_called = True self.close_called = True
self.file.close() try:
if self.delete: self.file.close()
self.unlink(self.name) finally:
if self.delete:
self.unlink(self.name)
def __del__(self): def __del__(self):
self.close() self.close()
......
...@@ -994,11 +994,16 @@ class addclosehook(addbase): ...@@ -994,11 +994,16 @@ class addclosehook(addbase):
self.hookargs = hookargs self.hookargs = hookargs
def close(self): def close(self):
if self.closehook: try:
self.closehook(*self.hookargs) closehook = self.closehook
self.closehook = None hookargs = self.hookargs
self.hookargs = None if closehook:
addbase.close(self) self.closehook = None
self.hookargs = None
closehook(*hookargs)
finally:
addbase.close(self)
class addinfo(addbase): class addinfo(addbase):
"""class to add an info() method to an open file.""" """class to add an info() method to an open file."""
......
...@@ -180,10 +180,11 @@ class Wave_read: ...@@ -180,10 +180,11 @@ class Wave_read:
self._soundpos = 0 self._soundpos = 0
def close(self): def close(self):
if self._i_opened_the_file:
self._i_opened_the_file.close()
self._i_opened_the_file = None
self._file = None self._file = None
file = self._i_opened_the_file
if file:
self._i_opened_the_file = None
file.close()
def tell(self): def tell(self):
return self._soundpos return self._soundpos
...@@ -444,17 +445,18 @@ class Wave_write: ...@@ -444,17 +445,18 @@ class Wave_write:
self._patchheader() self._patchheader()
def close(self): def close(self):
if self._file: try:
try: if self._file:
self._ensure_header_written(0) self._ensure_header_written(0)
if self._datalength != self._datawritten: if self._datalength != self._datawritten:
self._patchheader() self._patchheader()
self._file.flush() self._file.flush()
finally: finally:
self._file = None self._file = None
if self._i_opened_the_file: file = self._i_opened_the_file
self._i_opened_the_file.close() if file:
self._i_opened_the_file = None self._i_opened_the_file = None
file.close()
# #
# Internal methods. # Internal methods.
......
...@@ -214,14 +214,16 @@ class ExpatParser(xmlreader.IncrementalParser, xmlreader.Locator): ...@@ -214,14 +214,16 @@ class ExpatParser(xmlreader.IncrementalParser, xmlreader.Locator):
self._err_handler.fatalError(exc) self._err_handler.fatalError(exc)
def close(self): 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 # If we are completing an external entity, do nothing here
return return
self.feed("", isFinal = 1) try:
self._cont_handler.endDocument() self.feed("", isFinal = 1)
self._parsing = 0 self._cont_handler.endDocument()
# break cycle created by expat handlers pointing to our methods finally:
self._parser = None self._parsing = 0
# break cycle created by expat handlers pointing to our methods
self._parser = None
def _reset_cont_handler(self): def _reset_cont_handler(self):
self._parser.ProcessingInstructionHandler = \ self._parser.ProcessingInstructionHandler = \
......
...@@ -558,8 +558,13 @@ else: ...@@ -558,8 +558,13 @@ else:
self._parser.Parse(data, 0) self._parser.Parse(data, 0)
def close(self): def close(self):
self._parser.Parse("", 1) # end of data try:
del self._target, self._parser # get rid of circular references 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: class SlowParser:
"""Default XML parser (based on xmllib.XMLParser).""" """Default XML parser (based on xmllib.XMLParser)."""
...@@ -1214,8 +1219,10 @@ class GzipDecodedResponse(gzip.GzipFile if gzip else object): ...@@ -1214,8 +1219,10 @@ class GzipDecodedResponse(gzip.GzipFile if gzip else object):
gzip.GzipFile.__init__(self, mode="rb", fileobj=self.stringio) gzip.GzipFile.__init__(self, mode="rb", fileobj=self.stringio)
def close(self): def close(self):
gzip.GzipFile.close(self) try:
self.stringio.close() gzip.GzipFile.close(self)
finally:
self.stringio.close()
# -------------------------------------------------------------------- # --------------------------------------------------------------------
...@@ -1384,9 +1391,10 @@ class Transport: ...@@ -1384,9 +1391,10 @@ class Transport:
# Used in the event of socket errors. # Used in the event of socket errors.
# #
def close(self): def close(self):
if self._connection[1]: host, connection = self._connection
self._connection[1].close() if connection:
self._connection = (None, None) self._connection = (None, None)
connection.close()
## ##
# Send request header. # Send request header.
......
...@@ -21,6 +21,10 @@ Core and Builtins ...@@ -21,6 +21,10 @@ Core and Builtins
Library 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 - Issue #23881: urllib.ftpwrapper constructor now closes the socket if the FTP
connection failed. connection failed.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment