Skip to content

Commit

Permalink
Issue #23865: close() methods in multiple modules now are idempotent …
Browse files Browse the repository at this point in the history
…and more

robust at shutdown. If needs to release multiple resources, they are released
even if errors are occured.
  • Loading branch information
serhiy-storchaka committed Apr 10, 2015
2 parents fcbf8f3 + 7e7a3db commit 2116b12
Show file tree
Hide file tree
Showing 27 changed files with 316 additions and 210 deletions.
5 changes: 4 additions & 1 deletion Lib/aifc.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
42 changes: 26 additions & 16 deletions Lib/binhex.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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"""
Expand Down Expand Up @@ -435,11 +441,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"""
Expand Down
6 changes: 4 additions & 2 deletions Lib/chunk.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
6 changes: 4 additions & 2 deletions Lib/dbm/dumb.py
Original file line number Diff line number Diff line change
Expand Up @@ -258,8 +258,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

Expand Down
3 changes: 2 additions & 1 deletion Lib/distutils/text_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 = []
Expand Down
46 changes: 25 additions & 21 deletions Lib/fileinput.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -275,29 +277,31 @@ def __getitem__(self, i):

def nextfile(self):
savestdout = self._savestdout
self._savestdout = 0
self._savestdout = None
if savestdout:
sys.stdout = savestdout

output = self._output
self._output = 0
if output:
output.close()

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._output = None
try:
if output:
output.close()
finally:
file = self._file
self._file = None
try:
if file and not self._isstdin:
file.close()
finally:
backupfilename = self._backupfilename
self._backupfilename = None
if backupfilename and not self._backup:
try: os.unlink(backupfilename)
except OSError: pass

self._isstdin = False
self._buffer = []
self._bufindex = 0

def readline(self):
try:
Expand Down
15 changes: 10 additions & 5 deletions Lib/ftplib.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
26 changes: 14 additions & 12 deletions Lib/gzip.py
Original file line number Diff line number Diff line change
Expand Up @@ -503,19 +503,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()
Expand Down
24 changes: 15 additions & 9 deletions Lib/http/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -388,9 +388,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.

Expand Down Expand Up @@ -829,13 +831,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.
Expand Down
21 changes: 13 additions & 8 deletions Lib/logging/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1013,14 +1013,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()

Expand Down
23 changes: 14 additions & 9 deletions Lib/logging/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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):
"""
Expand Down Expand Up @@ -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):
Expand Down
20 changes: 13 additions & 7 deletions Lib/mailbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -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."""
Expand Down Expand Up @@ -1966,9 +1970,11 @@ def seek(self, offset, whence=0):
def close(self):
"""Close the file."""
if hasattr(self, '_file'):
if hasattr(self._file, 'close'):
self._file.close()
del self._file
try:
if hasattr(self._file, 'close'):
self._file.close()
finally:
del self._file

def _read(self, size, read_method):
"""Read size bytes using read_method."""
Expand Down
Loading

0 comments on commit 2116b12

Please sign in to comment.