From af86e5c72b3157112648d878a03a5fde2f508319 Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Thu, 30 Nov 2017 14:15:08 +0000 Subject: [PATCH] Various fixes intended to ensure that opened files are closed explictly where necessary in order to avoid ResourceWarnings. --- src/sage/doctest/control.py | 17 +++++++++++++---- src/sage/doctest/forker.py | 24 ++++++++++++++++++++++-- 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/src/sage/doctest/control.py b/src/sage/doctest/control.py index 871092d3021..4236fd05e09 100644 --- a/src/sage/doctest/control.py +++ b/src/sage/doctest/control.py @@ -366,20 +366,29 @@ def __init__(self, options, args): # write to the actual standard output, regardless of # redirections. if options.serial: - real_stdout = os.fdopen(os.dup(sys.stdout.fileno()), "a") + self._real_stdout = os.fdopen(os.dup(sys.stdout.fileno()), "w") + self._close_stdout = True else: # Parallel mode: no special tricks needed - real_stdout = sys.stdout + self._real_stdout = sys.stdout + self._close_stdout = False if self.logfile is None: - self.logger = real_stdout + self.logger = self._real_stdout else: - self.logger = Logger(real_stdout, self.logfile) + self.logger = Logger(self._real_stdout, self.logfile) self.stats = {} self.load_stats(options.stats_path) self._init_warn_long() + def __del__(self): + if getattr(self, 'logfile', None) is not None: + self.logfile.close() + + if getattr(self, '_close_stdout', False): + self._real_stdout.close() + def _init_warn_long(self): """ Pick a suitable default for the ``--warn-long`` option if not specified. diff --git a/src/sage/doctest/forker.py b/src/sage/doctest/forker.py index bcfce48d1d2..11a5386b8de 100644 --- a/src/sage/doctest/forker.py +++ b/src/sage/doctest/forker.py @@ -249,16 +249,20 @@ def __init__(self, outfile=None, infile=None): """ if infile is None: self.infile = open(os.devnull) + self._close_infile = True else: self.infile = infile + self._close_infile = False if outfile is None: self.outfile = tempfile.TemporaryFile() + self._close_outfile = True else: self.outfile = outfile + self._close_outfile = False self.spoofing = False self.real_stdin = os.fdopen(os.dup(sys.stdin.fileno()), "r") - self.real_stdout = os.fdopen(os.dup(sys.stdout.fileno()), "a") - self.real_stderr = os.fdopen(os.dup(sys.stderr.fileno()), "a") + self.real_stdout = os.fdopen(os.dup(sys.stdout.fileno()), "w") + self.real_stderr = os.fdopen(os.dup(sys.stderr.fileno()), "w") self.position = 0 def __del__(self): @@ -276,6 +280,12 @@ def __del__(self): Not spoofed! """ self.stop_spoofing() + if self._close_infile: + self.infile.close() + if self._close_outfile: + self.outfile.close() + for stream in ('stdin', 'stdout', 'stderr'): + getattr(self, 'real_' + stream).close() def start_spoofing(self): r""" @@ -1974,6 +1984,10 @@ def run(self): task(self.options, self.outtmpfile, msgpipe, self.result_queue) finally: msgpipe.close() + # Note: This closes the tempfile in the child process, but in the + # parent process self.outtmpfile will not be closed yet, and can + # still be accessed in save_result_output + self.outtmpfile.close() def start(self): """ @@ -2072,6 +2086,11 @@ def save_result_output(self): ['cputime', 'err', 'failures', 'optionals', 'walltime'] sage: len(W.output) > 0 True + + .. NOTE:: + + This method is called from the parent process, not from the + subprocess. """ from six.moves.queue import Empty try: @@ -2135,6 +2154,7 @@ def kill(self): sage: W.is_alive() False """ + if self.rmessages is not None: os.close(self.rmessages) self.rmessages = None