[GRASS-SVN] r60678 - sandbox/wenzeslaus/gunittest

svn_grass at osgeo.org svn_grass at osgeo.org
Mon Jun 2 20:51:54 PDT 2014


Author: wenzeslaus
Date: 2014-06-02 20:51:54 -0700 (Mon, 02 Jun 2014)
New Revision: 60678

Added:
   sandbox/wenzeslaus/gunittest/grass_py_static_check.py
Log:
gunittest: script to run Python source code static analysis and generate a HTML report (first prototype)

Added: sandbox/wenzeslaus/gunittest/grass_py_static_check.py
===================================================================
--- sandbox/wenzeslaus/gunittest/grass_py_static_check.py	                        (rev 0)
+++ sandbox/wenzeslaus/gunittest/grass_py_static_check.py	2014-06-03 03:51:54 UTC (rev 60678)
@@ -0,0 +1,214 @@
+#!/usr/bin/env python
+#
+############################################################################
+#
+# MODULE:       grass_py_static_check
+# AUTHOR(S):    Vaclav Petras <wenzeslaus gmail com>
+# PURPOSE:      Static source code analysis of Python code for GRASS GIS
+# COPYRIGHT:    (C) 2014 by Vaclav Petras and the GRASS Development Team
+#
+#               This program is free software under the GNU General Public
+#               License (>=v2). Read the file COPYING that comes with GRASS
+#               for details.
+#
+#############################################################################
+
+"""
+Can run only from the source code top level directory.
+"""
+
+import sys
+import os
+from subprocess import Popen, PIPE
+import re
+
+
+def ensure_dir(directory):
+    if not os.path.exists(directory):
+        os.makedirs(directory)
+
+
+def pylint(target, output_dir):
+    cmd = ['pylint', '--rcfile=tools/pylintrc.txt', '--output-format=html',
+           '--ignore=.svn']
+    cmd = cmd + [target]
+    # result
+    ensure_dir(output_dir)
+    output_file = open(output_dir + '/pylint_report.html', 'w')
+    # Path to the directory where the persistent for the run will be stored.
+    env = os.environ.copy()
+    env['PYLINTHOME'] = output_dir
+    proc = Popen(cmd, stdout=output_file, env=env)
+    ret = proc.wait()
+    if ret == 32:
+        raise RuntimeError("pylint usage error")
+    elif ret & 1:
+        cmd = ['pylint', '--rcfile=tools/pylintrc.txt', '--ignore=.svn']
+        cmd = cmd + [target]
+        proc = Popen(cmd, stdout=PIPE, env=env)
+        stdout, stderr = proc.communicate()
+        pylint_status = "pylint fatal message was issued and prevented pytlint from working"
+        msg = "{target}: {stat}\n{o}".format(stat=pylint_status, target=target, o=stdout)
+        raise RuntimeError(msg)
+    return get_num_of_messages_from_pylint_file(output_dir + '/pylint_report.html')
+
+
+def pylint_errors(target, output_dir):
+    # this ignores rcfile?
+    cmd = ['pylint', '--errors-only', '--rcfile=tools/pylintrc.txt', '--output-format=html',
+           '--ignore=.svn']
+    cmd = cmd + [target]
+    # result
+    ensure_dir(output_dir)
+    output_file = open(output_dir + '/pylint_errors_report.html', 'w')
+    # Path to the directory where the persistent for the run will be stored.
+    env = os.environ.copy()
+    env['PYLINTHOME'] = output_dir
+    proc = Popen(cmd, stdout=output_file, env=env)
+    ret = proc.wait()
+    if ret == 32:
+        raise RuntimeError("pylint usage error")
+    elif ret & 1:
+        cmd = ['pylint', '--rcfile=tools/pylintrc.txt', '--ignore=.svn']
+        cmd = cmd + [target]
+        proc = Popen(cmd, stdout=PIPE, env=env)
+        stdout, stderr = proc.communicate()
+        pylint_status = "pylint fatal message was issued and prevented pytlint from working"
+        msg = "{target}: {stat}\n{o}".format(stat=pylint_status, target=target, o=stdout)
+        raise RuntimeError(msg)
+    return get_num_of_messages_from_pylint_file(output_dir + '/pylint_errors_report.html')
+
+
+def remove_absolute_path_from_pep8(txt_file):
+    txt = open(txt_file, 'r')
+    content = txt.read()
+    txt.close()
+
+    # this would need to be more sophisticated for being crossplatform
+    # and to be a general function
+    gisbase = os.environ['GISBASE']
+    assert gisbase
+    exp = re.compile('%s/?' % (gisbase))
+    content = exp.sub('', content)
+
+    txt = open(txt_file, 'w')
+    txt.write(content)
+    txt.close()
+
+
+def get_num_of_messages_from_pep8_file(txt_file):
+    count = 0
+    txt = open(txt_file, 'r')
+    exp = re.compile(r'^.+.py:[0-9]+: \[[EW][0-9]{1,3}\] .+$')
+    for line in txt.readlines():
+        if exp.match(line):
+            count += 1
+    txt.close()
+    return count
+
+
+def get_num_of_messages_from_pylint_file(html_file):
+    count = 0
+    txt = open(html_file, 'r')
+    exp = re.compile(r'<td>(error|warning|refactor|convention)</td>')
+    for line in txt.readlines():
+        if exp.match(line):
+            count += 1
+        #print count, line, re.template
+    txt.close()
+    return count
+
+
+def pep8txt_to_html(txt_file, html_file):
+    txt = open(txt_file, 'r')
+    html = open(html_file, 'w')
+    html.write('<html><body>')
+    html.write('<pre>')
+    html.write(txt.read())
+    html.write('</pre>')
+    html.write('</body></html>')
+    txt.close()
+    html.close()
+
+
+def pep8(target, output_dir):
+    cmd = ['pep8', '--config=tools/pep8config.txt', '--format=pylint']
+    cmd = cmd + [target]
+    # result
+    ensure_dir(output_dir)
+    output_file = open(output_dir + '/pep8_report.txt', 'w')
+    # Path to the directory where the persistent for the run will be stored.
+    env = os.environ.copy()
+    proc = Popen(cmd, stdout=output_file, env=env)
+    proc.wait()
+    # pep8 always returns 1 when some error occurred
+    output_file.close()
+    remove_absolute_path_from_pep8(output_dir + '/pep8_report.txt')
+    pep8txt_to_html(output_dir + '/pep8_report.txt', output_dir + '/pep8_report.html')
+    return get_num_of_messages_from_pep8_file(output_dir + '/pep8_report.txt')
+
+
+def main():
+    gisbase = os.environ['GISBASE']
+    ensure_dir('pylint_report')
+
+    main_index = open('pylint_report/index.html', 'w')
+
+    # a lot of special treatment is needed to get packages nicely shown
+
+    grass_packages = ['script', 'pygrass', 'temporal']
+                     #, 'imaging', 'pydispatch', 'lib']
+    ensure_dir('pylint_report/grass')
+    index = open('pylint_report/grass/index.html', 'w')
+    for package in grass_packages:
+        num_pylint = pylint('grass.' + package, 'pylint_report/grass/' + package)
+        num_errors = pylint_errors('grass.' + package, 'pylint_report/grass/' + package)
+        num_pep8 = pep8(gisbase + '/etc/python/grass/' + package, 'pylint_report/grass/' + package)
+        index.write('{pkg}'.format(pkg=package))
+        index.write(' <a href="{pkg}/pylint_report.html">pylint</a> ({num})'.format(pkg=package, num=num_pylint))
+        index.write(' <a href="{pkg}/pylint_errors_report.html">pylint errors only</a> ({num})'.format(pkg=package, num=num_errors))
+        index.write(' <a href="{pkg}/pep8_report.html">pep8</a> ({num})<br>'.format(pkg=package, num=num_pep8))
+    index.close()
+    main_index.write('<a href="grass/index.html">grass</a><br>'.format(pkg=package))
+
+    dirlist = os.listdir(gisbase + '/gui/wxpython')
+    packages = []
+    directories = []
+    for f in dirlist:
+        if os.path.isdir('gui/wxpython/' + f):
+            if os.path.isfile('gui/wxpython/' + f + '/__init__.py'):
+                packages.append(f)
+            else:
+                directories.append(f)
+
+    # this is generated but not used
+    modules = [f for f in dirlist if f.endswith('.py')]
+    subdirs_with_modules = {}
+    for d in directories:
+        subdirlist = os.listdir(gisbase + '/gui/wxpython/' + d)
+        subdir_modules = [f for f in dirlist if os.path.isfile(gisbase + '/gui/wxpython/' + d + '/' + f) and f.endswith('.py')]
+        if subdir_modules:
+            subdirs_with_modules[d] = subdir_modules
+
+    grass_wxgui_package = ['gui']
+    ensure_dir('pylint_report/gui/wxpython')
+    gui_dir = gisbase + '/gui/wxpython/'
+    index = open('pylint_report/gui/wxpython/index.html', 'w')
+    for package in packages:
+        num_pylint = pylint(gui_dir + package, 'pylint_report/gui/wxpython/' + package)
+        num_errors = pylint_errors(gui_dir + package, 'pylint_report/gui/wxpython/' + package)
+        num_pep8 = pep8(gisbase + '/gui/wxpython/' + package, 'pylint_report/gui/wxpython/' + package)
+        index.write('{pkg}'.format(pkg=package))
+        index.write(' <a href="{pkg}/pylint_report.html">pylint</a> ({num})'.format(pkg=package, num=num_pylint))
+        index.write(' <a href="{pkg}/pylint_errors_report.html">pylint errors only</a> ({num})'.format(pkg=package, num=num_errors))
+        index.write(' <a href="{pkg}/pep8_report.html">pep8</a> ({num})<br>'.format(pkg=package, num=num_pep8))
+    index.close()
+    main_index.write('<a href="gui/wxpython/index.html">GRASS wxGUI</a><br>'.format(pkg=package))
+
+    #pylint(grass_wxgui_packages_path, 'pylint_report/gui/scripts')
+
+    return 0
+
+
+if __name__ == '__main__':
+    sys.exit(main())


Property changes on: sandbox/wenzeslaus/gunittest/grass_py_static_check.py
___________________________________________________________________
Added: svn:executable
   + *
Added: svn:mime-type
   + text/x-python
Added: svn:eol-style
   + native



More information about the grass-commit mailing list