From 5df756a9097b1f1e99c2251841338c523329b8fb 2019-01-06 11:37:53
From: Mickaël Schoentgen <contact@tiger-222.fr>
Date: 2019-01-06 11:37:53
Subject: [PATCH] Fix ResourceWarning: unclosed file

Also uniformize usage of the 'with' context manager to prevent resource leaks.

Signed-off-by: Mickaël Schoentgen <contact@tiger-222.fr>

---

diff --git a/IPython/core/crashhandler.py b/IPython/core/crashhandler.py
index f3abc1c..2117edb 100644
--- a/IPython/core/crashhandler.py
+++ b/IPython/core/crashhandler.py
@@ -179,13 +179,14 @@ class CrashHandler(object):
             print('Could not create crash report on disk.', file=sys.stderr)
             return
 
-        # Inform user on stderr of what happened
-        print('\n'+'*'*70+'\n', file=sys.stderr)
-        print(self.message_template.format(**self.info), file=sys.stderr)
+        with report:
+            # Inform user on stderr of what happened
+            print('\n'+'*'*70+'\n', file=sys.stderr)
+            print(self.message_template.format(**self.info), file=sys.stderr)
+
+            # Construct report on disk
+            report.write(self.make_report(traceback))
 
-        # Construct report on disk
-        report.write(self.make_report(traceback))
-        report.close()
         input("Hit <Enter> to quit (your terminal may close):")
 
     def make_report(self,traceback):
diff --git a/IPython/core/debugger.py b/IPython/core/debugger.py
index 4ece380..dc4a03c 100644
--- a/IPython/core/debugger.py
+++ b/IPython/core/debugger.py
@@ -206,9 +206,8 @@ def _file_lines(fname):
     except IOError:
         return []
     else:
-        out = outfile.readlines()
-        outfile.close()
-        return out
+        with out:
+            return outfile.readlines()
 
 
 class Pdb(OldPdb):
diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py
index f98e49c..df33144 100644
--- a/IPython/core/interactiveshell.py
+++ b/IPython/core/interactiveshell.py
@@ -3469,9 +3469,8 @@ class InteractiveShell(SingletonConfigurable):
         self.tempfiles.append(filename)
 
         if data:
-            tmp_file = open(filename,'w')
-            tmp_file.write(data)
-            tmp_file.close()
+            with open(filename, 'w') as tmp_file:
+                tmp_file.write(data)
         return filename
 
     @undoc
diff --git a/IPython/core/magics/code.py b/IPython/core/magics/code.py
index 8a718ae..41aa37c 100644
--- a/IPython/core/magics/code.py
+++ b/IPython/core/magics/code.py
@@ -722,7 +722,8 @@ class CodeMagics(Magics):
 
         if is_temp:
             try:
-                return open(filename).read()
+                with open(filename) as f:
+                    return f.read()
             except IOError as msg:
                 if msg.filename == filename:
                     warn('File not found. Did you forget to save?')
diff --git a/IPython/core/magics/execution.py b/IPython/core/magics/execution.py
index aa664a3..d58fa3c 100644
--- a/IPython/core/magics/execution.py
+++ b/IPython/core/magics/execution.py
@@ -370,9 +370,8 @@ python-profiler package from non-free.""")
             print('\n*** Profile stats marshalled to file',\
                   repr(dump_file)+'.',sys_exit)
         if text_file:
-            pfile = open(text_file,'w')
-            pfile.write(output)
-            pfile.close()
+            with open(text_file, 'w') as pfile:
+                pfile.write(output)
             print('\n*** Profile printout saved to text file',\
                   repr(text_file)+'.',sys_exit)
 
diff --git a/IPython/core/magics/packaging.py b/IPython/core/magics/packaging.py
index 0f052b4..6477c7d 100644
--- a/IPython/core/magics/packaging.py
+++ b/IPython/core/magics/packaging.py
@@ -35,10 +35,11 @@ def _get_conda_executable():
     # Otherwise, attempt to extract the executable from conda history.
     # This applies in any conda environment.
     R = re.compile(r"^#\s*cmd:\s*(?P<command>.*conda)\s[create|install]")
-    for line in open(os.path.join(sys.prefix, 'conda-meta', 'history')):
-        match = R.match(line)
-        if match:
-            return match.groupdict()['command']
+    with open(os.path.join(sys.prefix, 'conda-meta', 'history')) as f:
+        for line in f:
+            match = R.match(line)
+            if match:
+                return match.groupdict()['command']
     
     # Fallback: assume conda is available on the system path.
     return "conda"
diff --git a/IPython/extensions/storemagic.py b/IPython/extensions/storemagic.py
index 9a203ff..f2e5931 100644
--- a/IPython/extensions/storemagic.py
+++ b/IPython/extensions/storemagic.py
@@ -172,20 +172,19 @@ class StoreMagics(Magics):
                     fil = open(fnam, 'a')
                 else:
                     fil = open(fnam, 'w')
-                obj = ip.ev(args[0])
-                print("Writing '%s' (%s) to file '%s'." % (args[0],
-                  obj.__class__.__name__, fnam))
-
-
-                if not isinstance (obj, str):
-                    from pprint import pprint
-                    pprint(obj, fil)
-                else:
-                    fil.write(obj)
-                    if not obj.endswith('\n'):
-                        fil.write('\n')
+                with fil:
+                    obj = ip.ev(args[0])
+                    print("Writing '%s' (%s) to file '%s'." % (args[0],
+                        obj.__class__.__name__, fnam))
+
+                    if not isinstance (obj, str):
+                        from pprint import pprint
+                        pprint(obj, fil)
+                    else:
+                        fil.write(obj)
+                        if not obj.endswith('\n'):
+                            fil.write('\n')
 
-                fil.close()
                 return
 
             # %store foo
diff --git a/IPython/extensions/tests/test_autoreload.py b/IPython/extensions/tests/test_autoreload.py
index a942c5e..74e0125 100644
--- a/IPython/extensions/tests/test_autoreload.py
+++ b/IPython/extensions/tests/test_autoreload.py
@@ -109,19 +109,13 @@ class Fixture(object):
         time.sleep(1.05)
 
         # Write
-        f = open(filename, 'w')
-        try:
+        with open(filename, 'w') as f:
             f.write(content)
-        finally:
-            f.close()
 
     def new_module(self, code):
         mod_name, mod_fn = self.get_module()
-        f = open(mod_fn, 'w')
-        try:
+        with open(mod_fn, 'w') as f:
             f.write(code)
-        finally:
-            f.close()
         return mod_name, mod_fn
 
 #-----------------------------------------------------------------------------
diff --git a/IPython/testing/plugin/ipdoctest.py b/IPython/testing/plugin/ipdoctest.py
index 70da416..4babb2c 100644
--- a/IPython/testing/plugin/ipdoctest.py
+++ b/IPython/testing/plugin/ipdoctest.py
@@ -688,11 +688,8 @@ class ExtensionDoctest(doctests.Doctest):
         else:
             if self.extension and anyp(filename.endswith, self.extension):
                 name = os.path.basename(filename)
-                dh = open(filename)
-                try:
+                with open(filename) as dh:
                     doc = dh.read()
-                finally:
-                    dh.close()
                 test = self.parser.get_doctest(
                     doc, globs={'__file__': filename}, name=name,
                     filename=filename, lineno=0)
diff --git a/IPython/testing/tools.py b/IPython/testing/tools.py
index 59dc15b..1d16f11 100644
--- a/IPython/testing/tools.py
+++ b/IPython/testing/tools.py
@@ -422,8 +422,7 @@ def mute_warn():
 def make_tempfile(name):
     """ Create an empty, named, temporary file for the duration of the context.
     """
-    f = open(name, 'w')
-    f.close()
+    open(name, 'w').close()
     try:
         yield
     finally:
diff --git a/IPython/utils/tests/test_module_paths.py b/IPython/utils/tests/test_module_paths.py
index 8456ee7..38551cf 100644
--- a/IPython/utils/tests/test_module_paths.py
+++ b/IPython/utils/tests/test_module_paths.py
@@ -37,8 +37,7 @@ TMP_TEST_DIR = tempfile.mkdtemp(suffix='with.dot')
 old_syspath = sys.path
 
 def make_empty_file(fname):
-    f = open(fname, 'w')
-    f.close()
+    open(fname, 'w').close()
 
 
 def setup():
diff --git a/IPython/utils/tests/test_openpy.py b/IPython/utils/tests/test_openpy.py
index 352e993..5a01ac4 100644
--- a/IPython/utils/tests/test_openpy.py
+++ b/IPython/utils/tests/test_openpy.py
@@ -8,8 +8,8 @@ mydir = os.path.dirname(__file__)
 nonascii_path = os.path.join(mydir, '../../core/tests/nonascii.py')
 
 def test_detect_encoding():
-    f = open(nonascii_path, 'rb')
-    enc, lines = openpy.detect_encoding(f.readline)
+    with open(nonascii_path, 'rb') as f:
+        enc, lines = openpy.detect_encoding(f.readline)
     nt.assert_equal(enc, 'iso-8859-5')
 
 def test_read_file():
diff --git a/docs/sphinxext/apigen.py b/docs/sphinxext/apigen.py
index 0b3c80d..3db1525 100644
--- a/docs/sphinxext/apigen.py
+++ b/docs/sphinxext/apigen.py
@@ -392,9 +392,8 @@ class ApiDocWriter(object):
             # write out to file
             outfile = os.path.join(outdir,
                                    m + self.rst_extension)
-            fileobj = open(outfile, 'wt')
-            fileobj.write(api_str)
-            fileobj.close()
+            with open(outfile, 'wt') as fileobj:
+                fileobj.write(api_str)
             written_modules.append(m)
         self.written_modules = written_modules
 
@@ -445,11 +444,10 @@ class ApiDocWriter(object):
             relpath = outdir.replace(relative_to + os.path.sep, '')
         else:
             relpath = outdir
-        idx = open(path,'wt')
-        w = idx.write
-        w('.. AUTO-GENERATED FILE -- DO NOT EDIT!\n\n')
-        w('.. autosummary::\n'
-          '   :toctree: %s\n\n' % relpath)
-        for mod in self.written_modules:
-            w('   %s\n' % mod)
-        idx.close()
+        with open(path,'wt') as idx:
+            w = idx.write
+            w('.. AUTO-GENERATED FILE -- DO NOT EDIT!\n\n')
+            w('.. autosummary::\n'
+            '   :toctree: %s\n\n' % relpath)
+            for mod in self.written_modules:
+                w('   %s\n' % mod)
diff --git a/examples/IPython Kernel/Rich Output.ipynb b/examples/IPython Kernel/Rich Output.ipynb
index 7b0caed..28c0520 100644
--- a/examples/IPython Kernel/Rich Output.ipynb
+++ b/examples/IPython Kernel/Rich Output.ipynb
@@ -3023,7 +3023,8 @@
    "source": [
     "from IPython.display import HTML\n",
     "from base64 import b64encode\n",
-    "video = open(\"../images/animation.m4v\", \"rb\").read()\n",
+    "with open(\"../images/animation.m4v\", \"rb\") as f:\n",
+    "    video = f.read()\n",
     "video_encoded = b64encode(video).decode('ascii')\n",
     "video_tag = '<video controls alt=\"test\" src=\"data:video/x-m4v;base64,{0}\">'.format(video_encoded)\n",
     "HTML(data=video_tag)"
diff --git a/examples/IPython Kernel/ipython-get-history.py b/examples/IPython Kernel/ipython-get-history.py
index 3768aea..5615842 100755
--- a/examples/IPython Kernel/ipython-get-history.py
+++ b/examples/IPython Kernel/ipython-get-history.py
@@ -27,11 +27,13 @@ if len(sys.argv) > 2:
 else:
     dest = sys.stdout
     raw = True
-dest.write("# coding: utf-8\n")
 
-# Profiles other than 'default' can be specified here with a profile= argument:
-hist = HistoryAccessor()
+with dest:
+    dest.write("# coding: utf-8\n")
 
-for session, lineno, cell in hist.get_range(session=session_number, raw=raw):
-    cell = cell.encode('utf-8')  # This line is only needed on Python 2.
-    dest.write(cell + '\n')
+    # Profiles other than 'default' can be specified here with a profile= argument:
+    hist = HistoryAccessor()
+
+    for session, lineno, cell in hist.get_range(session=session_number, raw=raw):
+      cell = cell.encode('utf-8')  # This line is only needed on Python 2.
+      dest.write(cell + '\n')