[Python-checkins] bpo-22831: Use "with" to avoid possible fd leaks in tools (part 1). (GH-10926)

Serhiy Storchaka webhook-mailer at python.org
Sat Mar 30 02:32:23 EDT 2019


https://github.com/python/cpython/commit/afbb7a371fb44edc731344eab5b474ad8f7b57d7
commit: afbb7a371fb44edc731344eab5b474ad8f7b57d7
branch: master
author: Serhiy Storchaka <storchaka at gmail.com>
committer: GitHub <noreply at github.com>
date: 2019-03-30T08:32:18+02:00
summary:

bpo-22831: Use "with" to avoid possible fd leaks in tools (part 1). (GH-10926)

files:
M Tools/scripts/fixcid.py
M Tools/scripts/fixdiv.py
M Tools/scripts/fixheader.py
M Tools/scripts/gprof2html.py
M Tools/scripts/texi2html.py

diff --git a/Tools/scripts/fixcid.py b/Tools/scripts/fixcid.py
index c66a0ac093a7..8f35eaeeb4f6 100755
--- a/Tools/scripts/fixcid.py
+++ b/Tools/scripts/fixcid.py
@@ -281,36 +281,36 @@ def addsubst(substfile):
     except IOError as msg:
         err(substfile + ': cannot read substfile: ' + str(msg) + '\n')
         sys.exit(1)
-    lineno = 0
-    while 1:
-        line = fp.readline()
-        if not line: break
-        lineno = lineno + 1
-        try:
-            i = line.index('#')
-        except ValueError:
-            i = -1          # Happens to delete trailing \n
-        words = line[:i].split()
-        if not words: continue
-        if len(words) == 3 and words[0] == 'struct':
-            words[:2] = [words[0] + ' ' + words[1]]
-        elif len(words) != 2:
-            err(substfile + '%s:%r: warning: bad line: %r' % (substfile, lineno, line))
-            continue
-        if Reverse:
-            [value, key] = words
-        else:
-            [key, value] = words
-        if value[0] == '*':
-            value = value[1:]
-        if key[0] == '*':
-            key = key[1:]
-            NotInComment[key] = value
-        if key in Dict:
-            err('%s:%r: warning: overriding: %r %r\n' % (substfile, lineno, key, value))
-            err('%s:%r: warning: previous: %r\n' % (substfile, lineno, Dict[key]))
-        Dict[key] = value
-    fp.close()
+    with fp:
+        lineno = 0
+        while 1:
+            line = fp.readline()
+            if not line: break
+            lineno = lineno + 1
+            try:
+                i = line.index('#')
+            except ValueError:
+                i = -1          # Happens to delete trailing \n
+            words = line[:i].split()
+            if not words: continue
+            if len(words) == 3 and words[0] == 'struct':
+                words[:2] = [words[0] + ' ' + words[1]]
+            elif len(words) != 2:
+                err(substfile + '%s:%r: warning: bad line: %r' % (substfile, lineno, line))
+                continue
+            if Reverse:
+                [value, key] = words
+            else:
+                [key, value] = words
+            if value[0] == '*':
+                value = value[1:]
+            if key[0] == '*':
+                key = key[1:]
+                NotInComment[key] = value
+            if key in Dict:
+                err('%s:%r: warning: overriding: %r %r\n' % (substfile, lineno, key, value))
+                err('%s:%r: warning: previous: %r\n' % (substfile, lineno, Dict[key]))
+            Dict[key] = value
 
 if __name__ == '__main__':
     main()
diff --git a/Tools/scripts/fixdiv.py b/Tools/scripts/fixdiv.py
index 1213a4e39760..df7c481aa228 100755
--- a/Tools/scripts/fixdiv.py
+++ b/Tools/scripts/fixdiv.py
@@ -179,27 +179,27 @@ def usage(msg):
 
 def readwarnings(warningsfile):
     prog = re.compile(PATTERN)
+    warnings = {}
     try:
         f = open(warningsfile)
     except IOError as msg:
         sys.stderr.write("can't open: %s\n" % msg)
         return
-    warnings = {}
-    while 1:
-        line = f.readline()
-        if not line:
-            break
-        m = prog.match(line)
-        if not m:
-            if line.find("division") >= 0:
-                sys.stderr.write("Warning: ignored input " + line)
-            continue
-        filename, lineno, what = m.groups()
-        list = warnings.get(filename)
-        if list is None:
-            warnings[filename] = list = []
-        list.append((int(lineno), sys.intern(what)))
-    f.close()
+    with f:
+        while 1:
+            line = f.readline()
+            if not line:
+                break
+            m = prog.match(line)
+            if not m:
+                if line.find("division") >= 0:
+                    sys.stderr.write("Warning: ignored input " + line)
+                continue
+            filename, lineno, what = m.groups()
+            list = warnings.get(filename)
+            if list is None:
+                warnings[filename] = list = []
+            list.append((int(lineno), sys.intern(what)))
     return warnings
 
 def process(filename, list):
@@ -210,84 +210,84 @@ def process(filename, list):
     except IOError as msg:
         sys.stderr.write("can't open: %s\n" % msg)
         return 1
-    print("Index:", filename)
-    f = FileContext(fp)
-    list.sort()
-    index = 0 # list[:index] has been processed, list[index:] is still to do
-    g = tokenize.generate_tokens(f.readline)
-    while 1:
-        startlineno, endlineno, slashes = lineinfo = scanline(g)
-        if startlineno is None:
-            break
-        assert startlineno <= endlineno is not None
-        orphans = []
-        while index < len(list) and list[index][0] < startlineno:
-            orphans.append(list[index])
-            index += 1
-        if orphans:
-            reportphantomwarnings(orphans, f)
-        warnings = []
-        while index < len(list) and list[index][0] <= endlineno:
-            warnings.append(list[index])
-            index += 1
-        if not slashes and not warnings:
-            pass
-        elif slashes and not warnings:
-            report(slashes, "No conclusive evidence")
-        elif warnings and not slashes:
-            reportphantomwarnings(warnings, f)
-        else:
-            if len(slashes) > 1:
-                if not multi_ok:
-                    rows = []
-                    lastrow = None
-                    for (row, col), line in slashes:
-                        if row == lastrow:
-                            continue
-                        rows.append(row)
-                        lastrow = row
-                    assert rows
-                    if len(rows) == 1:
-                        print("*** More than one / operator in line", rows[0])
+    with fp:
+        print("Index:", filename)
+        f = FileContext(fp)
+        list.sort()
+        index = 0 # list[:index] has been processed, list[index:] is still to do
+        g = tokenize.generate_tokens(f.readline)
+        while 1:
+            startlineno, endlineno, slashes = lineinfo = scanline(g)
+            if startlineno is None:
+                break
+            assert startlineno <= endlineno is not None
+            orphans = []
+            while index < len(list) and list[index][0] < startlineno:
+                orphans.append(list[index])
+                index += 1
+            if orphans:
+                reportphantomwarnings(orphans, f)
+            warnings = []
+            while index < len(list) and list[index][0] <= endlineno:
+                warnings.append(list[index])
+                index += 1
+            if not slashes and not warnings:
+                pass
+            elif slashes and not warnings:
+                report(slashes, "No conclusive evidence")
+            elif warnings and not slashes:
+                reportphantomwarnings(warnings, f)
+            else:
+                if len(slashes) > 1:
+                    if not multi_ok:
+                        rows = []
+                        lastrow = None
+                        for (row, col), line in slashes:
+                            if row == lastrow:
+                                continue
+                            rows.append(row)
+                            lastrow = row
+                        assert rows
+                        if len(rows) == 1:
+                            print("*** More than one / operator in line", rows[0])
+                        else:
+                            print("*** More than one / operator per statement", end=' ')
+                            print("in lines %d-%d" % (rows[0], rows[-1]))
+                intlong = []
+                floatcomplex = []
+                bad = []
+                for lineno, what in warnings:
+                    if what in ("int", "long"):
+                        intlong.append(what)
+                    elif what in ("float", "complex"):
+                        floatcomplex.append(what)
                     else:
-                        print("*** More than one / operator per statement", end=' ')
-                        print("in lines %d-%d" % (rows[0], rows[-1]))
-            intlong = []
-            floatcomplex = []
-            bad = []
-            for lineno, what in warnings:
-                if what in ("int", "long"):
-                    intlong.append(what)
-                elif what in ("float", "complex"):
-                    floatcomplex.append(what)
-                else:
-                    bad.append(what)
-            lastrow = None
-            for (row, col), line in slashes:
-                if row == lastrow:
-                    continue
-                lastrow = row
-                line = chop(line)
-                if line[col:col+1] != "/":
-                    print("*** Can't find the / operator in line %d:" % row)
-                    print("*", line)
-                    continue
-                if bad:
-                    print("*** Bad warning for line %d:" % row, bad)
-                    print("*", line)
-                elif intlong and not floatcomplex:
-                    print("%dc%d" % (row, row))
-                    print("<", line)
-                    print("---")
-                    print(">", line[:col] + "/" + line[col:])
-                elif floatcomplex and not intlong:
-                    print("True division / operator at line %d:" % row)
-                    print("=", line)
-                elif intlong and floatcomplex:
-                    print("*** Ambiguous / operator (%s, %s) at line %d:" % (
-                        "|".join(intlong), "|".join(floatcomplex), row))
-                    print("?", line)
-    fp.close()
+                        bad.append(what)
+                lastrow = None
+                for (row, col), line in slashes:
+                    if row == lastrow:
+                        continue
+                    lastrow = row
+                    line = chop(line)
+                    if line[col:col+1] != "/":
+                        print("*** Can't find the / operator in line %d:" % row)
+                        print("*", line)
+                        continue
+                    if bad:
+                        print("*** Bad warning for line %d:" % row, bad)
+                        print("*", line)
+                    elif intlong and not floatcomplex:
+                        print("%dc%d" % (row, row))
+                        print("<", line)
+                        print("---")
+                        print(">", line[:col] + "/" + line[col:])
+                    elif floatcomplex and not intlong:
+                        print("True division / operator at line %d:" % row)
+                        print("=", line)
+                    elif intlong and floatcomplex:
+                        print("*** Ambiguous / operator (%s, %s) at line %d:" %
+                            ("|".join(intlong), "|".join(floatcomplex), row))
+                        print("?", line)
 
 def reportphantomwarnings(warnings, f):
     blocks = []
diff --git a/Tools/scripts/fixheader.py b/Tools/scripts/fixheader.py
index ec840575b205..c834eec1e20e 100755
--- a/Tools/scripts/fixheader.py
+++ b/Tools/scripts/fixheader.py
@@ -15,8 +15,8 @@ def process(filename):
     except IOError as msg:
         sys.stderr.write('%s: can\'t open: %s\n' % (filename, str(msg)))
         return
-    data = f.read()
-    f.close()
+    with f:
+        data = f.read()
     if data[:2] != '/*':
         sys.stderr.write('%s does not begin with C comment\n' % filename)
         return
@@ -25,25 +25,25 @@ def process(filename):
     except IOError as msg:
         sys.stderr.write('%s: can\'t write: %s\n' % (filename, str(msg)))
         return
-    sys.stderr.write('Processing %s ...\n' % filename)
-    magic = 'Py_'
-    for c in filename:
-        if ord(c)<=0x80 and c.isalnum():
-            magic = magic + c.upper()
-        else: magic = magic + '_'
-    sys.stdout = f
-    print('#ifndef', magic)
-    print('#define', magic)
-    print('#ifdef __cplusplus')
-    print('extern "C" {')
-    print('#endif')
-    print()
-    f.write(data)
-    print()
-    print('#ifdef __cplusplus')
-    print('}')
-    print('#endif')
-    print('#endif /*', '!'+magic, '*/')
+    with f:
+        sys.stderr.write('Processing %s ...\n' % filename)
+        magic = 'Py_'
+        for c in filename:
+            if ord(c)<=0x80 and c.isalnum():
+                magic = magic + c.upper()
+            else: magic = magic + '_'
+        print('#ifndef', magic, file=f)
+        print('#define', magic, file=f)
+        print('#ifdef __cplusplus', file=f)
+        print('extern "C" {', file=f)
+        print('#endif', file=f)
+        print(file=f)
+        f.write(data)
+        print(file=f)
+        print('#ifdef __cplusplus', file=f)
+        print('}', file=f)
+        print('#endif', file=f)
+        print('#endif /*', '!'+magic, '*/', file=f)
 
 if __name__ == '__main__':
     main()
diff --git a/Tools/scripts/gprof2html.py b/Tools/scripts/gprof2html.py
index 4ca705c3c61c..b14def4ef848 100755
--- a/Tools/scripts/gprof2html.py
+++ b/Tools/scripts/gprof2html.py
@@ -28,14 +28,7 @@ def add_escapes(filename):
         for line in fp:
             yield html.escape(line)
 
-
-def main():
-    filename = "gprof.out"
-    if sys.argv[1:]:
-        filename = sys.argv[1]
-    outputfilename = filename + ".html"
-    input = add_escapes(filename)
-    output = open(outputfilename, "w")
+def gprof2html(input, output, filename):
     output.write(header % filename)
     for line in input:
         output.write(line)
@@ -78,7 +71,16 @@ def main():
                 part = '<a href="#call:%s">%s</a>' % (part, part)
             output.write(part)
     output.write(trailer)
-    output.close()
+
+
+def main():
+    filename = "gprof.out"
+    if sys.argv[1:]:
+        filename = sys.argv[1]
+    outputfilename = filename + ".html"
+    input = add_escapes(filename)
+    with open(outputfilename, "w") as output:
+        gprof2html(input, output, filename)
     webbrowser.open("file:" + os.path.abspath(outputfilename))
 
 if __name__ == '__main__':
diff --git a/Tools/scripts/texi2html.py b/Tools/scripts/texi2html.py
index 5565c210dabb..c06d812ab3fb 100755
--- a/Tools/scripts/texi2html.py
+++ b/Tools/scripts/texi2html.py
@@ -118,11 +118,10 @@ def write(self, *lines):
             self.lines.append(line)
 
     def flush(self):
-        fp = open(self.dirname + '/' + makefile(self.name), 'w')
-        fp.write(self.prologue)
-        fp.write(self.text)
-        fp.write(self.epilogue)
-        fp.close()
+        with open(self.dirname + '/' + makefile(self.name), 'w') as fp:
+            fp.write(self.prologue)
+            fp.write(self.text)
+            fp.write(self.epilogue)
 
     def link(self, label, nodename, rel=None, rev=None):
         if nodename:
@@ -558,14 +557,14 @@ def do_include(self, args):
         except IOError as msg:
             print('*** Can\'t open include file', repr(file))
             return
-        print('!'*self.debugging, '--> file', repr(file))
-        save_done = self.done
-        save_skip = self.skip
-        save_stack = self.stack
-        self.includedepth = self.includedepth + 1
-        self.parserest(fp, 0)
-        self.includedepth = self.includedepth - 1
-        fp.close()
+        with fp:
+            print('!'*self.debugging, '--> file', repr(file))
+            save_done = self.done
+            save_skip = self.skip
+            save_stack = self.stack
+            self.includedepth = self.includedepth + 1
+            self.parserest(fp, 0)
+            self.includedepth = self.includedepth - 1
         self.done = save_done
         self.skip = save_skip
         self.stack = save_stack
@@ -1770,78 +1769,75 @@ def finalize(self):
 
         # PROJECT FILE
         try:
-            fp = open(projectfile,'w')
-            print('[OPTIONS]', file=fp)
-            print('Auto Index=Yes', file=fp)
-            print('Binary TOC=No', file=fp)
-            print('Binary Index=Yes', file=fp)
-            print('Compatibility=1.1', file=fp)
-            print('Compiled file=' + resultfile + '', file=fp)
-            print('Contents file=' + contentfile + '', file=fp)
-            print('Default topic=' + defaulttopic + '', file=fp)
-            print('Error log file=ErrorLog.log', file=fp)
-            print('Index file=' + indexfile + '', file=fp)
-            print('Title=' + title + '', file=fp)
-            print('Display compile progress=Yes', file=fp)
-            print('Full-text search=Yes', file=fp)
-            print('Default window=main', file=fp)
-            print('', file=fp)
-            print('[WINDOWS]', file=fp)
-            print('main=,"' + contentfile + '","' + indexfile
-                        + '","","",,,,,0x23520,222,0x1046,[10,10,780,560],'
-                        '0xB0000,,,,,,0', file=fp)
-            print('', file=fp)
-            print('[FILES]', file=fp)
-            print('', file=fp)
-            self.dumpfiles(fp)
-            fp.close()
+            with open(projectfile, 'w') as fp:
+                print('[OPTIONS]', file=fp)
+                print('Auto Index=Yes', file=fp)
+                print('Binary TOC=No', file=fp)
+                print('Binary Index=Yes', file=fp)
+                print('Compatibility=1.1', file=fp)
+                print('Compiled file=' + resultfile + '', file=fp)
+                print('Contents file=' + contentfile + '', file=fp)
+                print('Default topic=' + defaulttopic + '', file=fp)
+                print('Error log file=ErrorLog.log', file=fp)
+                print('Index file=' + indexfile + '', file=fp)
+                print('Title=' + title + '', file=fp)
+                print('Display compile progress=Yes', file=fp)
+                print('Full-text search=Yes', file=fp)
+                print('Default window=main', file=fp)
+                print('', file=fp)
+                print('[WINDOWS]', file=fp)
+                print('main=,"' + contentfile + '","' + indexfile
+                            + '","","",,,,,0x23520,222,0x1046,[10,10,780,560],'
+                            '0xB0000,,,,,,0', file=fp)
+                print('', file=fp)
+                print('[FILES]', file=fp)
+                print('', file=fp)
+                self.dumpfiles(fp)
         except IOError as msg:
             print(projectfile, ':', msg)
             sys.exit(1)
 
         # CONTENT FILE
         try:
-            fp = open(contentfile,'w')
-            print('<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">', file=fp)
-            print('<!-- This file defines the table of contents -->', file=fp)
-            print('<HTML>', file=fp)
-            print('<HEAD>', file=fp)
-            print('<meta name="GENERATOR" '
-                        'content="Microsoft® HTML Help Workshop 4.1">', file=fp)
-            print('<!-- Sitemap 1.0 -->', file=fp)
-            print('</HEAD>', file=fp)
-            print('<BODY>', file=fp)
-            print('   <OBJECT type="text/site properties">', file=fp)
-            print('     <param name="Window Styles" value="0x800025">', file=fp)
-            print('     <param name="comment" value="title:">', file=fp)
-            print('     <param name="comment" value="base:">', file=fp)
-            print('   </OBJECT>', file=fp)
-            self.dumpnodes(fp)
-            print('</BODY>', file=fp)
-            print('</HTML>', file=fp)
-            fp.close()
+            with open(contentfile, 'w') as fp:
+                print('<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">', file=fp)
+                print('<!-- This file defines the table of contents -->', file=fp)
+                print('<HTML>', file=fp)
+                print('<HEAD>', file=fp)
+                print('<meta name="GENERATOR" '
+                            'content="Microsoft® HTML Help Workshop 4.1">', file=fp)
+                print('<!-- Sitemap 1.0 -->', file=fp)
+                print('</HEAD>', file=fp)
+                print('<BODY>', file=fp)
+                print('   <OBJECT type="text/site properties">', file=fp)
+                print('     <param name="Window Styles" value="0x800025">', file=fp)
+                print('     <param name="comment" value="title:">', file=fp)
+                print('     <param name="comment" value="base:">', file=fp)
+                print('   </OBJECT>', file=fp)
+                self.dumpnodes(fp)
+                print('</BODY>', file=fp)
+                print('</HTML>', file=fp)
         except IOError as msg:
             print(contentfile, ':', msg)
             sys.exit(1)
 
         # INDEX FILE
         try:
-            fp = open(indexfile  ,'w')
-            print('<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">', file=fp)
-            print('<!-- This file defines the index -->', file=fp)
-            print('<HTML>', file=fp)
-            print('<HEAD>', file=fp)
-            print('<meta name="GENERATOR" '
-                        'content="Microsoft® HTML Help Workshop 4.1">', file=fp)
-            print('<!-- Sitemap 1.0 -->', file=fp)
-            print('</HEAD>', file=fp)
-            print('<BODY>', file=fp)
-            print('<OBJECT type="text/site properties">', file=fp)
-            print('</OBJECT>', file=fp)
-            self.dumpindex(fp)
-            print('</BODY>', file=fp)
-            print('</HTML>', file=fp)
-            fp.close()
+            with open(indexfile, 'w') as fp:
+                print('<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">', file=fp)
+                print('<!-- This file defines the index -->', file=fp)
+                print('<HTML>', file=fp)
+                print('<HEAD>', file=fp)
+                print('<meta name="GENERATOR" '
+                            'content="Microsoft® HTML Help Workshop 4.1">', file=fp)
+                print('<!-- Sitemap 1.0 -->', file=fp)
+                print('</HEAD>', file=fp)
+                print('<BODY>', file=fp)
+                print('<OBJECT type="text/site properties">', file=fp)
+                print('</OBJECT>', file=fp)
+                self.dumpindex(fp)
+                print('</BODY>', file=fp)
+                print('</HTML>', file=fp)
         except IOError as msg:
             print(indexfile  , ':', msg)
             sys.exit(1)
@@ -2064,8 +2060,8 @@ def test():
         print(file, ':', msg)
         sys.exit(1)
 
-    parser.parse(fp)
-    fp.close()
+    with fp:
+        parser.parse(fp)
     parser.report()
 
     htmlhelp.finalize()



More information about the Python-checkins mailing list