cpython: e826940911c8 (original) (raw)
Mercurial > cpython
changeset 95518:e826940911c8 3.4
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. [#23865]
line wrap: on
line diff
--- a/Lib/aifc.py +++ b/Lib/aifc.py @@ -356,7 +356,10 @@ class Aifc_read: self._soundpos = 0 def close(self):
self._file.close()[](#l1.7)
file = self._file[](#l1.8)
if file is not None:[](#l1.9)
self._file = None[](#l1.10)
file.close()[](#l1.11)
def tell(self): return self._soundpos
--- a/Lib/binhex.py +++ b/Lib/binhex.py @@ -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 @@ class BinHex: self._write(data) def close(self):
if self.state < _DID_DATA:[](#l2.17)
self.close_data()[](#l2.18)
if self.state != _DID_DATA:[](#l2.19)
raise Error('Close at the wrong time')[](#l2.20)
if self.rlen != 0:[](#l2.21)
raise Error("Incorrect resource-datasize, diff=%r" % (self.rlen,))[](#l2.22)
self._writecrc()[](#l2.23)
self.ofp.close()[](#l2.24)
self.state = None[](#l2.25)
del self.ofp[](#l2.26)
if self.state is None:[](#l2.27)
return[](#l2.28)
try:[](#l2.29)
if self.state < _DID_DATA:[](#l2.30)
self.close_data()[](#l2.31)
if self.state != _DID_DATA:[](#l2.32)
raise Error('Close at the wrong time')[](#l2.33)
if self.rlen != 0:[](#l2.34)
raise Error("Incorrect resource-datasize, diff=%r" % (self.rlen,))[](#l2.35)
self._writecrc()[](#l2.36)
finally:[](#l2.37)
self.state = None[](#l2.38)
ofp = self.ofp[](#l2.39)
del self.ofp[](#l2.40)
ofp.close()[](#l2.41)
def binhex(inp, out): """binhex(infilename, outfilename): create binhex-encoded copy of a file""" @@ -436,11 +442,15 @@ class HexBin: return self._read(n) def close(self):
if self.rlen:[](#l2.49)
dummy = self.read_rsrc(self.rlen)[](#l2.50)
self._checkcrc()[](#l2.51)
self.state = _DID_RSRC[](#l2.52)
self.ifp.close()[](#l2.53)
if self.state is None:[](#l2.54)
return[](#l2.55)
try:[](#l2.56)
if self.rlen:[](#l2.57)
dummy = self.read_rsrc(self.rlen)[](#l2.58)
self._checkcrc()[](#l2.59)
finally:[](#l2.60)
self.state = None[](#l2.61)
self.ifp.close()[](#l2.62)
def hexbin(inp, out): """hexbin(infilename, outfilename) - Decode binhexed file"""
--- a/Lib/chunk.py +++ b/Lib/chunk.py @@ -85,8 +85,10 @@ class Chunk: def close(self): if not self.closed:
self.skip()[](#l3.7)
self.closed = True[](#l3.8)
try:[](#l3.9)
self.skip()[](#l3.10)
finally:[](#l3.11)
self.closed = True[](#l3.12)
def isatty(self): if self.closed:
--- a/Lib/dbm/dumb.py +++ b/Lib/dbm/dumb.py @@ -248,8 +248,10 @@ class _Database(collections.MutableMappi raise error('DBM object has already been closed') from None def close(self):
self._commit()[](#l4.7)
self._index = self._datfile = self._dirfile = self._bakfile = None[](#l4.8)
try:[](#l4.9)
self._commit()[](#l4.10)
finally:[](#l4.11)
self._index = self._datfile = self._dirfile = self._bakfile = None[](#l4.12)
--- a/Lib/distutils/text_file.py +++ b/Lib/distutils/text_file.py @@ -118,10 +118,11 @@ class TextFile: def close(self): """Close the current file and forget everything we know about it (filename, current line number)."""
self.file.close()[](#l5.7)
file = self.file[](#l5.8) self.file = None[](#l5.9) self.filename = None[](#l5.10) self.current_line = None[](#l5.11)
file.close()[](#l5.12)
def gen_error(self, msg, line=None): outmsg = []
--- a/Lib/fileinput.py +++ b/Lib/fileinput.py @@ -238,8 +238,10 @@ class FileInput: self.close() def close(self):
self.nextfile()[](#l6.7)
self._files = ()[](#l6.8)
try:[](#l6.9)
self.nextfile()[](#l6.10)
finally:[](#l6.11)
self._files = ()[](#l6.12)
def enter(self): return self @@ -281,23 +283,25 @@ class FileInput: output = self._output self._output = 0
if output:[](#l6.20)
output.close()[](#l6.21)
file = self._file[](#l6.23)
self._file = 0[](#l6.24)
if file and not self._isstdin:[](#l6.25)
file.close()[](#l6.26)
try:[](#l6.27)
if output:[](#l6.28)
output.close()[](#l6.29)
finally:[](#l6.30)
file = self._file[](#l6.31)
self._file = 0[](#l6.32)
try:[](#l6.33)
if file and not self._isstdin:[](#l6.34)
file.close()[](#l6.35)
finally:[](#l6.36)
backupfilename = self._backupfilename[](#l6.37)
self._backupfilename = 0[](#l6.38)
if backupfilename and not self._backup:[](#l6.39)
try: os.unlink(backupfilename)[](#l6.40)
except OSError: pass[](#l6.41)
backupfilename = self._backupfilename[](#l6.43)
self._backupfilename = 0[](#l6.44)
if backupfilename and not self._backup:[](#l6.45)
try: os.unlink(backupfilename)[](#l6.46)
except OSError: pass[](#l6.47)
self._isstdin = False[](#l6.49)
self._buffer = [][](#l6.50)
self._bufindex = 0[](#l6.51)
self._isstdin = False[](#l6.52)
self._buffer = [][](#l6.53)
self._bufindex = 0[](#l6.54)
--- a/Lib/ftplib.py +++ b/Lib/ftplib.py @@ -667,11 +667,16 @@ class FTP: def close(self): '''Close the connection without assuming anything about it.'''
if self.file is not None:[](#l7.7)
self.file.close()[](#l7.8)
if self.sock is not None:[](#l7.9)
self.sock.close()[](#l7.10)
self.file = self.sock = None[](#l7.11)
try:[](#l7.12)
file = self.file[](#l7.13)
self.file = None[](#l7.14)
if file is not None:[](#l7.15)
file.close()[](#l7.16)
finally:[](#l7.17)
sock = self.sock[](#l7.18)
self.sock = None[](#l7.19)
if sock is not None:[](#l7.20)
sock.close()[](#l7.21)
--- a/Lib/gzip.py +++ b/Lib/gzip.py @@ -500,19 +500,21 @@ class GzipFile(io.BufferedIOBase): return self.fileobj is None def close(self):
if self.fileobj is None:[](#l8.7)
fileobj = self.fileobj[](#l8.8)
if fileobj is None:[](#l8.9) return[](#l8.10)
if self.mode == WRITE:[](#l8.11)
self.fileobj.write(self.compress.flush())[](#l8.12)
write32u(self.fileobj, self.crc)[](#l8.13)
# self.size may exceed 2GB, or even 4GB[](#l8.14)
write32u(self.fileobj, self.size & 0xffffffff)[](#l8.15)
self.fileobj = None[](#l8.16)
elif self.mode == READ:[](#l8.17)
self.fileobj = None[](#l8.18)
if self.myfileobj:[](#l8.19)
self.myfileobj.close()[](#l8.20)
self.myfileobj = None[](#l8.21)
self.fileobj = None[](#l8.22)
try:[](#l8.23)
if self.mode == WRITE:[](#l8.24)
fileobj.write(self.compress.flush())[](#l8.25)
write32u(fileobj, self.crc)[](#l8.26)
# self.size may exceed 2GB, or even 4GB[](#l8.27)
write32u(fileobj, self.size & 0xffffffff)[](#l8.28)
finally:[](#l8.29)
myfileobj = self.myfileobj[](#l8.30)
if myfileobj:[](#l8.31)
self.myfileobj = None[](#l8.32)
myfileobj.close()[](#l8.33)
def flush(self,zlib_mode=zlib.Z_SYNC_FLUSH): self._check_closed()
--- a/Lib/http/client.py +++ b/Lib/http/client.py @@ -492,9 +492,11 @@ class HTTPResponse(io.RawIOBase): fp.close() def close(self):
super().close() # set "closed" flag[](#l9.7)
if self.fp:[](#l9.8)
self._close_conn()[](#l9.9)
try:[](#l9.10)
super().close() # set "closed" flag[](#l9.11)
finally:[](#l9.12)
if self.fp:[](#l9.13)
self._close_conn()[](#l9.14)
# These implementations are for the benefit of io.BufferedReader. @@ -873,13 +875,17 @@ class HTTPConnection: def close(self): """Close the connection to the HTTP server."""
if self.sock:[](#l9.22)
self.sock.close() # close it manually... there may be other refs[](#l9.23)
self.sock = None[](#l9.24)
if self.__response:[](#l9.25)
self.__response.close()[](#l9.26)
self.__response = None[](#l9.27) self.__state = _CS_IDLE[](#l9.28)
try:[](#l9.29)
sock = self.sock[](#l9.30)
if sock:[](#l9.31)
self.sock = None[](#l9.32)
sock.close() # close it manually... there may be other refs[](#l9.33)
finally:[](#l9.34)
response = self.__response[](#l9.35)
if response:[](#l9.36)
self.__response = None[](#l9.37)
response.close()[](#l9.38)
def send(self, data): """Send `data' to the server.
--- a/Lib/logging/init.py +++ b/Lib/logging/init.py @@ -1011,14 +1011,19 @@ class FileHandler(StreamHandler): """ self.acquire() try:
if self.stream:[](#l10.7)
self.flush()[](#l10.8)
if hasattr(self.stream, "close"):[](#l10.9)
self.stream.close()[](#l10.10)
self.stream = None[](#l10.11)
# Issue #19523: call unconditionally to[](#l10.12)
# prevent a handler leak when delay is set[](#l10.13)
StreamHandler.close(self)[](#l10.14)
try:[](#l10.15)
if self.stream:[](#l10.16)
try:[](#l10.17)
self.flush()[](#l10.18)
finally:[](#l10.19)
stream = self.stream[](#l10.20)
self.stream = None[](#l10.21)
if hasattr(stream, "close"):[](#l10.22)
stream.close()[](#l10.23)
finally:[](#l10.24)
# Issue #19523: call unconditionally to[](#l10.25)
# prevent a handler leak when delay is set[](#l10.26)
StreamHandler.close(self)[](#l10.27) finally:[](#l10.28) self.release()[](#l10.29)
--- a/Lib/logging/handlers.py +++ b/Lib/logging/handlers.py @@ -627,9 +627,10 @@ class SocketHandler(logging.Handler): """ self.acquire() try:
if self.sock:[](#l11.7)
self.sock.close()[](#l11.8)
sock = self.sock[](#l11.9)
if sock:[](#l11.10) self.sock = None[](#l11.11)
sock.close()[](#l11.12) logging.Handler.close(self)[](#l11.13) finally:[](#l11.14) self.release()[](#l11.15)
@@ -1213,8 +1214,10 @@ class BufferingHandler(logging.Handler): This version just flushes and chains to the parent class' close(). """
self.flush()[](#l11.20)
logging.Handler.close(self)[](#l11.21)
try:[](#l11.22)
self.flush()[](#l11.23)
finally:[](#l11.24)
logging.Handler.close(self)[](#l11.25)
class MemoryHandler(BufferingHandler): """ @@ -1268,13 +1271,15 @@ class MemoryHandler(BufferingHandler): """ Flush, set the target to None and lose the buffer. """
self.flush()[](#l11.33)
self.acquire()[](#l11.34) try:[](#l11.35)
self.target = None[](#l11.36)
BufferingHandler.close(self)[](#l11.37)
self.flush()[](#l11.38) finally:[](#l11.39)
self.release()[](#l11.40)
self.acquire()[](#l11.41)
try:[](#l11.42)
self.target = None[](#l11.43)
BufferingHandler.close(self)[](#l11.44)
finally:[](#l11.45)
self.release()[](#l11.46)
class QueueHandler(logging.Handler):
--- a/Lib/mailbox.py +++ b/Lib/mailbox.py @@ -722,10 +722,14 @@ class _singlefileMailbox(Mailbox): def close(self): """Flush and close the mailbox."""
self.flush()[](#l12.7)
if self._locked:[](#l12.8)
self.unlock()[](#l12.9)
self._file.close() # Sync has been done by self.flush() above.[](#l12.10)
try:[](#l12.11)
self.flush()[](#l12.12)
finally:[](#l12.13)
try:[](#l12.14)
if self._locked:[](#l12.15)
self.unlock()[](#l12.16)
finally:[](#l12.17)
self._file.close() # Sync has been done by self.flush() above.[](#l12.18)
def _lookup(self, key=None): """Return (start, stop) or raise KeyError."""
--- a/Lib/multiprocessing/connection.py
+++ b/Lib/multiprocessing/connection.py
@@ -469,9 +469,10 @@ class Listener(object):
'''
Close the bound socket or named pipe of self
.
'''
if self._listener is not None:[](#l13.7)
self._listener.close()[](#l13.8)
listener = self._listener[](#l13.9)
if listener is not None:[](#l13.10) self._listener = None[](#l13.11)
listener.close()[](#l13.12)
address = property(lambda self: self._listener._address) last_accepted = property(lambda self: self._listener._last_accepted) @@ -609,9 +610,13 @@ class SocketListener(object): return Connection(s.detach()) def close(self):
self._socket.close()[](#l13.20)
if self._unlink is not None:[](#l13.21)
self._unlink()[](#l13.22)
try:[](#l13.23)
self._socket.close()[](#l13.24)
finally:[](#l13.25)
unlink = self._unlink[](#l13.26)
if unlink is not None:[](#l13.27)
self._unlink = None[](#l13.28)
unlink()[](#l13.29)
--- a/Lib/multiprocessing/queues.py +++ b/Lib/multiprocessing/queues.py @@ -133,9 +133,13 @@ class Queue(object): def close(self): self._closed = True
self._reader.close()[](#l14.7)
if self._close:[](#l14.8)
self._close()[](#l14.9)
try:[](#l14.10)
self._reader.close()[](#l14.11)
finally:[](#l14.12)
close = self._close[](#l14.13)
if close:[](#l14.14)
self._close = None[](#l14.15)
close()[](#l14.16)
def join_thread(self): debug('Queue.join_thread()')
--- a/Lib/poplib.py +++ b/Lib/poplib.py @@ -276,18 +276,23 @@ class POP3: def close(self): """Close the connection without assuming anything about it."""
if self.file is not None:[](#l15.7)
self.file.close()[](#l15.8)
if self.sock is not None:[](#l15.9)
try:[](#l15.10)
self.sock.shutdown(socket.SHUT_RDWR)[](#l15.11)
except OSError as e:[](#l15.12)
# The server might already have closed the connection[](#l15.13)
if e.errno != errno.ENOTCONN:[](#l15.14)
raise[](#l15.15)
finally:[](#l15.16)
self.sock.close()[](#l15.17)
self.file = self.sock = None[](#l15.18)
try:[](#l15.19)
file = self.file[](#l15.20)
self.file = None[](#l15.21)
if file is not None:[](#l15.22)
file.close()[](#l15.23)
finally:[](#l15.24)
sock = self.sock[](#l15.25)
self.sock = None[](#l15.26)
if sock is not None:[](#l15.27)
try:[](#l15.28)
sock.shutdown(socket.SHUT_RDWR)[](#l15.29)
except OSError as e:[](#l15.30)
# The server might already have closed the connection[](#l15.31)
if e.errno != errno.ENOTCONN:[](#l15.32)
raise[](#l15.33)
finally:[](#l15.34)
sock.close()[](#l15.35)
--- a/Lib/selectors.py +++ b/Lib/selectors.py @@ -445,8 +445,10 @@ if hasattr(select, 'epoll'): return ready def close(self):
self._epoll.close()[](#l16.7)
super().close()[](#l16.8)
try:[](#l16.9)
self._epoll.close()[](#l16.10)
finally:[](#l16.11)
super().close()[](#l16.12)
if hasattr(select, 'kqueue'): @@ -517,8 +519,10 @@ if hasattr(select, 'kqueue'): return ready def close(self):
self._kqueue.close()[](#l16.20)
super().close()[](#l16.21)
try:[](#l16.22)
self._kqueue.close()[](#l16.23)
finally:[](#l16.24)
super().close()[](#l16.25)
Choose the best implementation: roughly, epoll|kqueue > poll > select.
--- a/Lib/shelve.py +++ b/Lib/shelve.py @@ -138,17 +138,21 @@ class Shelf(collections.MutableMapping): self.close() def close(self):
self.sync()[](#l17.7)
if self.dict is None:[](#l17.8)
return[](#l17.9) try:[](#l17.10)
self.dict.close()[](#l17.11)
except AttributeError:[](#l17.12)
pass[](#l17.13)
# Catch errors that may happen when close is called from __del__[](#l17.14)
# because CPython is in interpreter shutdown.[](#l17.15)
try:[](#l17.16)
self.dict = _ClosedDict()[](#l17.17)
except (NameError, TypeError):[](#l17.18)
self.dict = None[](#l17.19)
self.sync()[](#l17.20)
try:[](#l17.21)
self.dict.close()[](#l17.22)
except AttributeError:[](#l17.23)
pass[](#l17.24)
finally:[](#l17.25)
# Catch errors that may happen when close is called from __del__[](#l17.26)
# because CPython is in interpreter shutdown.[](#l17.27)
try:[](#l17.28)
self.dict = _ClosedDict()[](#l17.29)
except:[](#l17.30)
self.dict = None[](#l17.31)
def del(self): if not hasattr(self, 'writeback'):
--- a/Lib/smtplib.py +++ b/Lib/smtplib.py @@ -855,12 +855,16 @@ class SMTP: def close(self): """Close the connection to the SMTP server."""
if self.file:[](#l18.7)
self.file.close()[](#l18.8)
self.file = None[](#l18.9)
if self.sock:[](#l18.10)
self.sock.close()[](#l18.11)
self.sock = None[](#l18.12)
try:[](#l18.13)
file = self.file[](#l18.14)
self.file = None[](#l18.15)
if file:[](#l18.16)
file.close()[](#l18.17)
finally:[](#l18.18)
sock = self.sock[](#l18.19)
self.sock = None[](#l18.20)
if sock:[](#l18.21)
sock.close()[](#l18.22)
def quit(self): """Terminate the SMTP session."""
--- a/Lib/sunau.py +++ b/Lib/sunau.py @@ -295,9 +295,11 @@ class Au_read: self._soundpos = pos def close(self):
if self._opened and self._file:[](#l19.7)
self._file.close()[](#l19.8)
self._file = None[](#l19.9)
file = self._file[](#l19.10)
if file:[](#l19.11)
self._file = None[](#l19.12)
if self._opened:[](#l19.13)
file.close()[](#l19.14)
class Au_write: @@ -438,9 +440,10 @@ class Au_write: self._patchheader() self._file.flush() finally:
if self._opened and self._file:[](#l19.22)
self._file.close()[](#l19.23)
file = self._file[](#l19.24) self._file = None[](#l19.25)
if self._opened:[](#l19.26)
file.close()[](#l19.27)
--- a/Lib/tarfile.py +++ b/Lib/tarfile.py @@ -449,26 +449,26 @@ class _Stream: if self.closed: return
if self.mode == "w" and self.comptype != "tar":[](#l20.7)
self.buf += self.cmp.flush()[](#l20.8)
self.closed = True[](#l20.9)
try:[](#l20.10)
if self.mode == "w" and self.comptype != "tar":[](#l20.11)
self.buf += self.cmp.flush()[](#l20.12)
if self.mode == "w" and self.buf:[](#l20.14)
self.fileobj.write(self.buf)[](#l20.15)
self.buf = b""[](#l20.16)
if self.comptype == "gz":[](#l20.17)
# The native zlib crc is an unsigned 32-bit integer, but[](#l20.18)
# the Python wrapper implicitly casts that to a signed C[](#l20.19)
# long. So, on a 32-bit box self.crc may "look negative",[](#l20.20)
# while the same crc on a 64-bit box may "look positive".[](#l20.21)
# To avoid irksome warnings from the `struct` module, force[](#l20.22)
# it to look positive on all boxes.[](#l20.23)
self.fileobj.write(struct.pack("<L", self.crc & 0xffffffff))[](#l20.24)
self.fileobj.write(struct.pack("<L", self.pos & 0xffffFFFF))[](#l20.25)
if not self._extfileobj:[](#l20.27)
self.fileobj.close()[](#l20.28)
self.closed = True[](#l20.30)
if self.mode == "w" and self.buf:[](#l20.31)
self.fileobj.write(self.buf)[](#l20.32)
self.buf = b""[](#l20.33)
if self.comptype == "gz":[](#l20.34)
# The native zlib crc is an unsigned 32-bit integer, but[](#l20.35)
# the Python wrapper implicitly casts that to a signed C[](#l20.36)
# long. So, on a 32-bit box self.crc may "look negative",[](#l20.37)
# while the same crc on a 64-bit box may "look positive".[](#l20.38)
# To avoid irksome warnings from the `struct` module, force[](#l20.39)
# it to look positive on all boxes.[](#l20.40)
self.fileobj.write(struct.pack("<L", self.crc & 0xffffffff))[](#l20.41)
self.fileobj.write(struct.pack("<L", self.pos & 0xffffFFFF))[](#l20.42)
finally:[](#l20.43)
if not self._extfileobj:[](#l20.44)
self.fileobj.close()[](#l20.45)
def _init_read_gz(self): """Initialize for reading a gzip compressed fileobj. @@ -1705,18 +1705,19 @@ class TarFile(object): if self.closed: return
if self.mode in "aw":[](#l20.53)
self.fileobj.write(NUL * (BLOCKSIZE * 2))[](#l20.54)
self.offset += (BLOCKSIZE * 2)[](#l20.55)
# fill up the end with zero-blocks[](#l20.56)
# (like option -b20 for tar does)[](#l20.57)
blocks, remainder = divmod(self.offset, RECORDSIZE)[](#l20.58)
if remainder > 0:[](#l20.59)
self.fileobj.write(NUL * (RECORDSIZE - remainder))[](#l20.60)
if not self._extfileobj:[](#l20.62)
self.fileobj.close()[](#l20.63) self.closed = True[](#l20.64)
try:[](#l20.65)
if self.mode in "aw":[](#l20.66)
self.fileobj.write(NUL * (BLOCKSIZE * 2))[](#l20.67)
self.offset += (BLOCKSIZE * 2)[](#l20.68)
# fill up the end with zero-blocks[](#l20.69)
# (like option -b20 for tar does)[](#l20.70)
blocks, remainder = divmod(self.offset, RECORDSIZE)[](#l20.71)
if remainder > 0:[](#l20.72)
self.fileobj.write(NUL * (RECORDSIZE - remainder))[](#l20.73)
finally:[](#l20.74)
if not self._extfileobj:[](#l20.75)
self.fileobj.close()[](#l20.76)
def getmember(self, name):
"""Return a TarInfo object for member name'. If
name' can not be
--- a/Lib/telnetlib.py +++ b/Lib/telnetlib.py @@ -264,12 +264,13 @@ class Telnet: def close(self): """Close the connection."""
if self.sock:[](#l21.7)
self.sock.close()[](#l21.8)
sock = self.sock[](#l21.9) self.sock = 0[](#l21.10) self.eof = 1[](#l21.11) self.iacseq = b''[](#l21.12) self.sb = 0[](#l21.13)
if sock:[](#l21.14)
sock.close()[](#l21.15)
def get_socket(self): """Return the socket object used internally."""
--- a/Lib/tempfile.py +++ b/Lib/tempfile.py @@ -357,9 +357,11 @@ class _TemporaryFileCloser: def close(self, unlink=_os.unlink): if not self.close_called and self.file is not None: self.close_called = True
self.file.close()[](#l22.7)
if self.delete:[](#l22.8)
unlink(self.name)[](#l22.9)
try:[](#l22.10)
self.file.close()[](#l22.11)
finally:[](#l22.12)
if self.delete:[](#l22.13)
unlink(self.name)[](#l22.14)
# Need to ensure the file is deleted on del def del(self):
--- a/Lib/urllib/response.py +++ b/Lib/urllib/response.py @@ -43,11 +43,15 @@ class addclosehook(addbase): self.hookargs = hookargs def close(self):
if self.closehook:[](#l23.7)
self.closehook(*self.hookargs)[](#l23.8)
self.closehook = None[](#l23.9)
self.hookargs = None[](#l23.10)
super(addclosehook, self).close()[](#l23.11)
try:[](#l23.12)
closehook = self.closehook[](#l23.13)
hookargs = self.hookargs[](#l23.14)
if closehook:[](#l23.15)
self.closehook = None[](#l23.16)
self.hookargs = None[](#l23.17)
closehook(*hookargs)[](#l23.18)
finally:[](#l23.19)
super(addclosehook, self).close()[](#l23.20)
--- a/Lib/wave.py +++ b/Lib/wave.py @@ -186,10 +186,11 @@ class Wave_read: self._soundpos = 0 def close(self):
if self._i_opened_the_file:[](#l24.7)
self._i_opened_the_file.close()[](#l24.8)
self._file = None[](#l24.9)
file = self._i_opened_the_file[](#l24.10)
if file:[](#l24.11) self._i_opened_the_file = None[](#l24.12)
self._file = None[](#l24.13)
file.close()[](#l24.14)
def tell(self): return self._soundpos @@ -428,17 +429,18 @@ class Wave_write: self._patchheader() def close(self):
if self._file:[](#l24.22)
try:[](#l24.23)
try:[](#l24.24)
if self._file:[](#l24.25) self._ensure_header_written(0)[](#l24.26) if self._datalength != self._datawritten:[](#l24.27) self._patchheader()[](#l24.28) self._file.flush()[](#l24.29)
finally:[](#l24.30)
self._file = None[](#l24.31)
if self._i_opened_the_file:[](#l24.32)
self._i_opened_the_file.close()[](#l24.33)
self._i_opened_the_file = None[](#l24.34)
finally:[](#l24.35)
self._file = None[](#l24.36)
file = self._i_opened_the_file[](#l24.37)
if file:[](#l24.38)
self._i_opened_the_file = None[](#l24.39)
file.close()[](#l24.40)
--- a/Lib/xml/sax/expatreader.py +++ b/Lib/xml/sax/expatreader.py @@ -211,17 +211,19 @@ class ExpatParser(xmlreader.IncrementalP self._err_handler.fatalError(exc) def close(self):
if self._entity_stack:[](#l25.7)
if self._entity_stack or self._parser is None:[](#l25.8) # If we are completing an external entity, do nothing here[](#l25.9) return[](#l25.10)
self.feed("", isFinal = 1)[](#l25.11)
self._cont_handler.endDocument()[](#l25.12)
self._parsing = 0[](#l25.13)
# break cycle created by expat handlers pointing to our methods[](#l25.14)
self._parser = None[](#l25.15)
bs = self._source.getByteStream()[](#l25.16)
if bs is not None:[](#l25.17)
bs.close()[](#l25.18)
try:[](#l25.19)
self.feed("", isFinal = 1)[](#l25.20)
self._cont_handler.endDocument()[](#l25.21)
finally:[](#l25.22)
self._parsing = 0[](#l25.23)
# break cycle created by expat handlers pointing to our methods[](#l25.24)
self._parser = None[](#l25.25)
bs = self._source.getByteStream()[](#l25.26)
if bs is not None:[](#l25.27)
bs.close()[](#l25.28)
def _reset_cont_handler(self): self._parser.ProcessingInstructionHandler = [](#l25.31)
--- a/Lib/xmlrpc/client.py +++ b/Lib/xmlrpc/client.py @@ -446,8 +446,13 @@ class ExpatParser: self._parser.Parse(data, 0) def close(self):
self._parser.Parse("", 1) # end of data[](#l26.7)
del self._target, self._parser # get rid of circular references[](#l26.8)
try:[](#l26.9)
parser = self._parser[](#l26.10)
except AttributeError:[](#l26.11)
pass[](#l26.12)
else:[](#l26.13)
del self._target, self._parser # get rid of circular references[](#l26.14)
parser.Parse(b"", True) # end of data[](#l26.15)
--------------------------------------------------------------------
XML-RPC marshalling and unmarshalling code
@@ -1079,8 +1084,10 @@ class GzipDecodedResponse(gzip.GzipFile gzip.GzipFile.init(self, mode="rb", fileobj=self.io) def close(self):
gzip.GzipFile.close(self)[](#l26.23)
self.io.close()[](#l26.24)
try:[](#l26.25)
gzip.GzipFile.close(self)[](#l26.26)
finally:[](#l26.27)
self.io.close()[](#l26.28)
--------------------------------------------------------------------
@@ -1235,9 +1242,10 @@ class Transport: # Used in the event of socket errors. # def close(self):
if self._connection[1]:[](#l26.36)
self._connection[1].close()[](#l26.37)
host, connection = self._connection[](#l26.38)
if connection:[](#l26.39) self._connection = (None, None)[](#l26.40)
connection.close()[](#l26.41)
--- a/Misc/NEWS +++ b/Misc/NEWS @@ -24,6 +24,10 @@ Core and Builtins Library ------- +- Issue #23865: close() methods in multiple modules now are idempotent and more