diff options
| author | gbrandl <devnull@localhost> | 2006-10-19 20:27:28 +0200 |
|---|---|---|
| committer | gbrandl <devnull@localhost> | 2006-10-19 20:27:28 +0200 |
| commit | f4d019954468db777760d21f9243eca8b852c184 (patch) | |
| tree | 328b8f8fac25338306b0e7b827686dcc7597df23 /scripts | |
| download | pygments-f4d019954468db777760d21f9243eca8b852c184.tar.gz | |
[svn] Name change, round 4 (rename SVN root folder).
Diffstat (limited to 'scripts')
| -rwxr-xr-x | scripts/check_sources.py | 231 | ||||
| -rw-r--r-- | scripts/count_loc.py | 48 | ||||
| -rw-r--r-- | scripts/epydoc.css | 280 | ||||
| -rwxr-xr-x | scripts/find_codetags.py | 205 | ||||
| -rwxr-xr-x | scripts/fix_epydoc_markup.py | 46 | ||||
| -rw-r--r-- | scripts/pylintrc | 355 | ||||
| -rwxr-xr-x | scripts/reindent.py | 291 |
7 files changed, 1456 insertions, 0 deletions
diff --git a/scripts/check_sources.py b/scripts/check_sources.py new file mode 100755 index 00000000..2c90df8b --- /dev/null +++ b/scripts/check_sources.py @@ -0,0 +1,231 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" + Checker for file headers + ~~~~~~~~~~~~~~~~~~~~~~~~ + + Make sure each Python file has a correct file header + including copyright and license information. + + :copyright: 2006 by Georg Brandl. + :license: GNU GPL, see LICENSE for more details. +""" + +import sys, os, re +import getopt +import cStringIO +from os.path import join, splitext, abspath + + +checkers = {} + +def checker(*suffixes, **kwds): + only_pkg = kwds.pop('only_pkg', False) + def deco(func): + for suffix in suffixes: + checkers.setdefault(suffix, []).append(func) + func.only_pkg = only_pkg + return func + return deco + + +copyright_re = re.compile(r'^ :copyright: 200\d(-200\d)? by (\w| )+(, (\w| )+)*\.$') +coding_re = re.compile(r'coding[:=]\s*([-\w.]+)') +not_ix_re = re.compile(r'\bnot\s+\S+?\s+i[sn]\s\S+') +is_const_re = re.compile(r'if.*?==\s+(None|False|True)\b') + +misspellings = ["developement", "adress", "verificate", # ALLOW-MISSPELLING + "informations"] # ALLOW-MISSPELLING + + +@checker('.py') +def check_syntax(fn, lines): + try: + compile(''.join(lines), fn, "exec") + except SyntaxError, err: + yield 0, "not compilable: %s" % err + + +@checker('.py') +def check_style_and_encoding(fn, lines): + encoding = 'ascii' + for lno, line in enumerate(lines): + if len(line) > 90: + yield lno+1, "line too long" + m = not_ix_re.search(line) + if m: + yield lno+1, '"' + m.group() + '"' + if is_const_re.search(line): + yield lno+1, 'using == None/True/False' + if lno < 2: + co = coding_re.search(line) + if co: + encoding = co.group(1) + try: + line.decode(encoding) + except UnicodeDecodeError, err: + yield lno+1, "not decodable: %s\n Line: %r" % (err, line) + except LookupError, err: + yield 0, "unknown encoding: %s" % encoding + encoding = 'latin1' + + +@checker('.py', only_pkg=True) +def check_fileheader(fn, lines): + # line number correction + c = 1 + if lines[0:1] == ['#!/usr/bin/env python\n']: + lines = lines[1:] + c = 2 + + llist = [] + docopen = False + for lno, l in enumerate(lines): + llist.append(l) + if lno == 0: + if l == '# -*- coding: rot13 -*-\n': + # special-case pony package + return + elif l != '# -*- coding: utf-8 -*-\n': + yield 1, "missing coding declaration" + elif lno == 1: + if l != '"""\n' and l != 'r"""\n': + yield 2, 'missing docstring begin (""")' + else: + docopen = True + elif docopen: + if l == '"""\n': + # end of docstring + if lno <= 4: + yield lno+c, "missing module name in docstring" + break + + if l != "\n" and l[:4] != ' ' and docopen: + yield lno+c, "missing correct docstring indentation" + + if lno == 2: + # if not in package, don't check the module name + modname = fn[:-3].replace('/', '.').replace('.__init__', '') + while modname: + if l.lower()[4:-1] == modname: + break + modname = '.'.join(modname.split('.')[1:]) + else: + yield 3, "wrong module name in docstring heading" + modnamelen = len(l.strip()) + elif lno == 3: + if l.strip() != modnamelen * "~": + yield 4, "wrong module name underline, should be ~~~...~" + + else: + yield 0, "missing end and/or start of docstring..." + + # check for copyright and license fields + license = llist[-2:-1] + if license != [" :license: GNU LGPL, see LICENSE for more details.\n"]: + yield 0, "no correct license info" + + copyright = llist[-3:-2] + if not copyright or not copyright_re.match(copyright[0]): + yield 0, "no correct copyright info" + + +@checker('.py', '.html', '.js') +def check_whitespace_and_spelling(fn, lines): + for lno, line in enumerate(lines): + if "\t" in line: + yield lno+1, "OMG TABS!!!1 " + if line[:-1].rstrip(' \t') != line[:-1]: + yield lno+1, "trailing whitespace" + for word in misspellings: + if word in line and 'ALLOW-MISSPELLING' not in line: + yield lno+1, '"%s" used' % word + + +bad_tags = ('<b>', '<i>', '<u>', '<s>', '<strike>' + '<center>', '<big>', '<small>', '<font') + +@checker('.html') +def check_xhtml(fn, lines): + for lno, line in enumerate(lines): + for bad_tag in bad_tags: + if bad_tag in line: + yield lno+1, "used " + bad_tag + + +def main(argv): + try: + gopts, args = getopt.getopt(argv[1:], "vi:") + except getopt.GetoptError: + print "Usage: %s [-v] [-i ignorepath]* [path]" % argv[0] + return 2 + opts = {} + for opt, val in gopts: + if opt == '-i': + val = abspath(val) + opts.setdefault(opt, []).append(val) + + if len(args) == 0: + path = '.' + elif len(args) == 1: + path = args[0] + else: + print "Usage: %s [-v] [-i ignorepath]* [path]" % argv[0] + return 2 + + verbose = '-v' in opts + + num = 0 + out = cStringIO.StringIO() + + for root, dirs, files in os.walk(path): + if '.svn' in dirs: + dirs.remove('.svn') + if '-i' in opts and abspath(root) in opts['-i']: + del dirs[:] + continue + # XXX: awkward: for the Makefile call: don't check non-package + # files for file headers + in_pocoo_pkg = root.startswith('./pygments') + for fn in files: + + fn = join(root, fn) + if fn[:2] == './': fn = fn[2:] + + if '-i' in opts and abspath(fn) in opts['-i']: + continue + + ext = splitext(fn)[1] + checkerlist = checkers.get(ext, None) + if not checkerlist: + continue + + if verbose: + print "Checking %s..." % fn + + try: + f = open(fn, 'r') + lines = list(f) + except (IOError, OSError), err: + print "%s: cannot open: %s" % (fn, err) + num += 1 + continue + + for checker in checkerlist: + if not in_pocoo_pkg and checker.only_pkg: + continue + for lno, msg in checker(fn, lines): + print >>out, "%s:%d: %s" % (fn, lno, msg) + num += 1 + if verbose: + print + if num == 0: + print "No errors found." + else: + print out.getvalue().rstrip('\n') + print "%d error%s found." % (num, num > 1 and "s" or "") + return int(num > 0) + + +if __name__ == '__main__': + sys.exit(main(sys.argv)) diff --git a/scripts/count_loc.py b/scripts/count_loc.py new file mode 100644 index 00000000..5804cd6f --- /dev/null +++ b/scripts/count_loc.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" + Source line counter + ~~~~~~~~~~~~~~~~~~~ + + Count the lines of code in pocoo, colubrid and jinja. + Requires an svn checkout. + + :copyright: 2006 by Armin Ronacher. + :license: GNU GPL, see LICENSE for more details. +""" +from pocoo.utils.path import Path + +def main(root, search): + LOC = 0 + + root = Path(root).realpath() + offset = len(root) + 1 + + print '+%s+' % ('=' * 78) + print '| Lines of Code %s |' % (' ' * 62) + print '+%s+' % ('=' * 78) + + for folder in search: + folder = Path(root).joinpath(folder).realpath() + for fn in folder.walk(): + if fn.endswith('.py') or fn.endswith('.js'): + try: + fp = file(fn) + lines = sum(1 for l in fp.read().splitlines() if l.strip()) + except: + print '%-70sskipped' % fn + else: + LOC += lines + print '| %-68s %7d |' % (fn[offset:], lines) + fp.close() + + print '+%s+' % ('-' * 78) + print '| Total Lines of Code: %55d |' % LOC + print '+%s+' % ('-' * 78) + +if __name__ == '__main__': + main('../../../', [ + 'pocoo/trunk', + 'colubrid/trunk', + 'jinja/trunk' + ]) diff --git a/scripts/epydoc.css b/scripts/epydoc.css new file mode 100644 index 00000000..16d5b0df --- /dev/null +++ b/scripts/epydoc.css @@ -0,0 +1,280 @@ + + +/* Epydoc CSS Stylesheet + * + * This stylesheet can be used to customize the appearance of epydoc's + * HTML output. + * + */ + +/* Adapted for Pocoo API docs by Georg Brandl */ + +/* Default Colors & Styles + * - Set the default foreground & background color with 'body'; and + * link colors with 'a:link' and 'a:visited'. + * - Use bold for decision list terms. + * - The heading styles defined here are used for headings *within* + * docstring descriptions. All headings used by epydoc itself use + * either class='epydoc' or class='toc' (CSS styles for both + * defined below). + */ +body { background: #ffffff; color: #000000; + font-family: Trebuchet MS,Tahoma,sans-serif; + font-size: 0.9em; line-height: 140%; + margin: 0; padding: 0 1.2em 1.2em 1.2em; } +a:link { color: #C87900; text-decoration: none; + border-bottom: 1px solid #C87900; } +a:visited { color: #C87900; text-decoration: none; + border-bottom: 1px dotted #C87900; } +a:hover { color: #F8A900; border-bottom-color: #F8A900; } +dt { font-weight: bold; } +h1 { font-size: +180%; font-style: italic; + font-weight: bold; margin-top: 1.5em; } +h2 { font-size: +140%; font-style: italic; + font-weight: bold; } +h3 { font-size: +110%; font-style: italic; + font-weight: normal; } +p { margin-top: .5em; margin-bottom: .5em; } +hr { margin-top: 1.5em; margin-bottom: 1.5em; + border: 1px solid #BBB; } +tt.literal { background: #F5FFD0; padding: 2px; + font-size: 110%; } +table.rst-docutils { border: 0; } +table.rst-docutils td { border: 0; padding: 5px 20px 5px 0px; } + +/* Page Header & Footer + * - The standard page header consists of a navigation bar (with + * pointers to standard pages such as 'home' and 'trees'); a + * breadcrumbs list, which can be used to navigate to containing + * classes or modules; options links, to show/hide private + * variables and to show/hide frames; and a page title (using + * <h1>). The page title may be followed by a link to the + * corresponding source code (using 'span.codelink'). + * - The footer consists of a navigation bar, a timestamp, and a + * pointer to epydoc's homepage. + */ +h1.epydoc { margin-top: .4em; margin-bottom: .4em; + font-size: +180%; font-weight: bold; + font-style: normal; } +h2.epydoc { font-size: +130%; font-weight: bold; + font-style: normal; } +h3.epydoc { font-size: +115%; font-weight: bold; + font-style: normal; } +table.navbar { background: #E6F8A0; color: #000000; + border-top: 1px solid #c0d0d0; + border-bottom: 1px solid #c0d0d0; + margin: -1px -1.2em 1em -1.2em; } +table.navbar th { padding: 2px 7px 2px 0px; } +th.navbar-select { background-color: transparent; } +th.navbar-select:before { content: ">" } +th.navbar-select:after { content: "<" } +table.navbar a { border: 0; } +span.breadcrumbs { font-size: 95%; font-weight: bold; } +span.options { font-size: 80%; } +span.codelink { font-size: 85%; } +td.footer { font-size: 85%; } + +/* Table Headers + * - Each summary table and details section begins with a 'header' + * row. This row contains a section title (marked by + * 'span.table-header') as well as a show/hide private link + * (marked by 'span.options', defined above). + * - Summary tables that contain user-defined groups mark those + * groups using 'group header' rows. + */ +td.table-header { background: #B6C870; color: #000000; + border-bottom: 1px solid #FFF; } +span.table-header { font-size: 110%; font-weight: bold; } +th.group-header { text-align: left; font-style: italic; + font-size: 110%; } +td.spacer { width: 5%; } + +/* Summary Tables (functions, variables, etc) + * - Each object is described by a single row of the table with + * two cells. The left cell gives the object's type, and is + * marked with 'code.summary-type'. The right cell gives the + * object's name and a summary description. + * - CSS styles for the table's header and group headers are + * defined above, under 'Table Headers' + */ +table.summary { border-collapse: collapse; + background: #E6F8A0; color: #000000; + margin: 1em 0 .5em 0; + border: 0; } +table.summary tr { border-bottom: 1px solid #BBB; } +td.summary a { font-weight: bold; } +code.summary-type { font-size: 85%; } + +/* Details Tables (functions, variables, etc) + * - Each object is described in its own single-celled table. + * - A single-row summary table w/ table-header is used as + * a header for each details section (CSS style for table-header + * is defined above, under 'Table Headers'). + */ + +table.detsummary { margin-top: 2em; } + +table.details { border-collapse: collapse; + background: #E6F8A0; color: #000000; + border-bottom: 1px solid #BBB; + margin: 0; } +table.details td { padding: .2em .2em .2em .5em; } +table.details table td { padding: 0; } +table.details h3 { margin: 5px 0 5px 0; font-size: 105%; + font-style: normal; } + +table.details dd { display: inline; margin-left: 5px; } +table.details dl { margin-left: 5px; } + + +/* Index tables (identifier index, term index, etc) + * - link-index is used for indices containing lists of links + * (namely, the identifier index & term index). + * - index-where is used in link indices for the text indicating + * the container/source for each link. + * - metadata-index is used for indices containing metadata + * extracted from fields (namely, the bug index & todo index). + */ +table.link-index { border-collapse: collapse; + background: #F6FFB0; color: #000000; + border: 1px solid #608090; } +td.link-index { border-width: 0px; } +span.index-where { font-size: 70%; } +table.metadata-index { border-collapse: collapse; + background: #F6FFB0; color: #000000; + border: 1px solid #608090; + margin: .2em 0 0 0; } +td.metadata-index { border-width: 1px; border-style: solid; } + +/* Function signatures + * - sig* is used for the signature in the details section. + * - .summary-sig* is used for the signature in the summary + * table, and when listing property accessor functions. + * */ +.sig-name { color: #006080; } +.sig-arg { color: #008060; } +.sig-default { color: #602000; } +.summary-sig-name { font-weight: bold; } +.summary-sig-arg { color: #006040; } +.summary-sig-default { color: #501800; } + +/* Variable values + * - In the 'variable details' sections, each varaible's value is + * listed in a 'pre.variable' box. The width of this box is + * restricted to 80 chars; if the value's repr is longer than + * this it will be wrapped, using a backslash marked with + * class 'variable-linewrap'. If the value's repr is longer + * than 3 lines, the rest will be ellided; and an ellipsis + * marker ('...' marked with 'variable-ellipsis') will be used. + * - If the value is a string, its quote marks will be marked + * with 'variable-quote'. + * - If the variable is a regexp, it is syntax-highlighted using + * the re* CSS classes. + */ +pre.variable { padding: .5em; margin: 0; + background-color: #dce4ec; + border: 1px solid #708890; } +.variable-linewrap { display: none; } +.variable-ellipsis { color: #604000; font-weight: bold; } +.variable-quote { color: #604000; font-weight: bold; } +.re { color: #000000; } +.re-char { color: #006030; } +.re-op { color: #600000; } +.re-group { color: #003060; } +.re-ref { color: #404040; } + +/* Base tree + * - Used by class pages to display the base class hierarchy. + */ +pre.base-tree { font-size: 90%; margin: 1em 0 2em 0; + line-height: 100%;} + +/* Frames-based table of contents headers + * - Consists of two frames: one for selecting modules; and + * the other listing the contents of the selected module. + * - h1.toc is used for each frame's heading + * - h2.toc is used for subheadings within each frame. + */ +h1.toc { text-align: center; font-size: 105%; + margin: 0; font-weight: bold; + padding: 0; } +h2.toc { font-size: 100%; font-weight: bold; + margin: 0.5em 0 0 -0.3em; } + +/* Syntax Highlighting for Source Code + * - doctest examples are displayed in a 'pre.py-doctest' block. + * If the example is in a details table entry, then it will use + * the colors specified by the 'table pre.py-doctest' line. + * - Source code listings are displayed in a 'pre.py-src' block. + * Each line is marked with 'span.py-line' (used to draw a line + * down the left margin, separating the code from the line + * numbers). Line numbers are displayed with 'span.py-lineno'. + * The expand/collapse block toggle button is displayed with + * 'a.py-toggle' (Note: the CSS style for 'a.py-toggle' should not + * modify the font size of the text.) + * - If a source code page is opened with an anchor, then the + * corresponding code block will be highlighted. The code + * block's header is highlighted with 'py-highlight-hdr'; and + * the code block's body is highlighted with 'py-highlight'. + * - The remaining py-* classes are used to perform syntax + * highlighting (py-string for string literals, py-name for names, + * etc.) + */ +pre.rst-literal-block, +pre.py-doctest { margin-left: 1em; margin-right: 1.5em; + line-height: 150%; + background-color: #F5FFD0; padding: .5em; + border: 1px solid #B6C870; + font-size: 110%; } +pre.py-src { border: 1px solid #BBB; margin-top: 3em; + background: #f0f0f0; color: #000000; + line-height: 150%; } +span.py-line { margin-left: .2em; padding-left: .4em; } +span.py-lineno { border-right: 1px solid #BBB; + padding: .3em .5em .3em .5em; + font-style: italic; font-size: 90%; } +a.py-toggle { text-decoration: none; } +div.py-highlight-hdr { border-top: 1px solid #BBB; + background: #d0e0e0; } +div.py-highlight { border-bottom: 1px solid #BBB; + background: #d0e0e0; } +.py-prompt { color: #005050; font-weight: bold;} +.py-string { color: #006030; } +.py-comment { color: #003060; } +.py-keyword { color: #600000; } +.py-output { color: #404040; } +.py-name { color: #000050; } +.py-name:link { color: #000050; } +.py-name:visited { color: #000050; } +.py-number { color: #005000; } +.py-def-name { color: #000060; font-weight: bold; } +.py-base-class { color: #000060; } +.py-param { color: #000060; } +.py-docstring { color: #006030; } +.py-decorator { color: #804020; } +/* Use this if you don't want links to names underlined: */ +/*a.py-name { text-decoration: none; }*/ + +/* Graphs & Diagrams + * - These CSS styles are used for graphs & diagrams generated using + * Graphviz dot. 'img.graph-without-title' is used for bare + * diagrams (to remove the border created by making the image + * clickable). + */ +img.graph-without-title { border: none; } +img.graph-with-title { border: 1px solid #000000; } +span.graph-title { font-weight: bold; } +span.graph-caption { } + +/* General-purpose classes + * - 'p.indent-wrapped-lines' defines a paragraph whose first line + * is not indented, but whose subsequent lines are. + * - The 'nomargin-top' class is used to remove the top margin (e.g. + * from lists). The 'nomargin' class is used to remove both the + * top and bottom margin (but not the left or right margin -- + * for lists, that would cause the bullets to disappear.) + */ +p.indent-wrapped-lines { padding: 0 0 0 7em; text-indent: -7em; + margin: 0; } +.nomargin-top { margin-top: 0; } +.nomargin { margin-top: 0; margin-bottom: 0; } diff --git a/scripts/find_codetags.py b/scripts/find_codetags.py new file mode 100755 index 00000000..54d66b84 --- /dev/null +++ b/scripts/find_codetags.py @@ -0,0 +1,205 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" + Codetags finder + ~~~~~~~~~~~~~~~ + + Find code tags in specified files and/or directories + and create a report in HTML format. + + :copyright: 2006 by Georg Brandl. + :license: GNU GPL, see LICENSE for more details. +""" + +import sys, os, re +import getopt +from os.path import join, abspath, isdir, isfile + + +TAGS = set(('XXX', 'TODO', 'FIXME', 'HACK')) + +tag_re = re.compile( + r'(?P<tag>\b' + r'\b|\b'.join(TAGS) + r'\b)\s*' + r'(?: \( (?P<who> .*? ) \) )?' + r'\s*:?\s* (?P<what> .*? ) \s* $', + re.X) + +binary_re = re.compile('[\x00-\x06\x0E-\x1F]') + + +def escape_html(text): + return text.replace('&', '&'). \ + replace('<', '<'). \ + replace('>', '>'). \ + replace('"', '"') + +def process_file(store, filename): + try: + f = open(filename, 'r') + except (IOError, OSError): + return False + llmatch = 0 + try: + for lno, line in enumerate(f): + # just some random heuristics to filter out binary files + if lno < 100 and binary_re.search(line): + return False + m = tag_re.search(line) + if m: + store.setdefault(filename, []).append({ + 'lno': lno+1, + 'tag': m.group('tag'), + 'who': m.group('who') or '', + 'what': escape_html(m.group('what')), + }) + # 'what' cannot start at column 0 + llmatch = m.start('what') + elif llmatch: + # continuation lines + # XXX: this is Python centric, doesn't work for + # JavaScript, for example. + if line[:llmatch].replace('#', '').isspace(): + cont = line[llmatch:].strip() + if cont: + store[filename][-1]['what'] += ' ' + escape_html(cont) + continue + llmatch = 0 + return True + finally: + f.close() + + +def main(): + try: + gopts, args = getopt.getopt(sys.argv[1:], "vo:i:") + except getopt.GetoptError: + print ("Usage: %s [-v] [-i ignoredir]* [-o reportfile.html] " + "path ..." % sys.argv[0]) + return 2 + opts = {} + for opt, val in gopts: + if opt == '-i': + val = abspath(val) + opts.setdefault(opt, []).append(val) + + if not args: + args = ['.'] + + if '-o' in opts: + output = abspath(opts['-o'][-1]) + else: + output = abspath('tags.html') + + verbose = '-v' in opts + + store = {} + gnum = 0 + num = 0 + + for path in args: + print "Searching for code tags in %s, please wait." % path + + if isfile(path): + gnum += 1 + if process_file(store, path): + if verbose: + print path + ": found %d tags" % \ + (path in store and len(store[path]) or 0) + num += 1 + else: + if verbose: + print path + ": binary or not readable" + continue + elif not isdir(path): + continue + + for root, dirs, files in os.walk(path): + if '-i' in opts and abspath(root) in opts['-i']: + del dirs[:] + continue + if '.svn' in dirs: + dirs.remove('.svn') + for fn in files: + gnum += 1 + if gnum % 50 == 0 and not verbose: + sys.stdout.write('.') + sys.stdout.flush() + + fn = join(root, fn) + + if fn.endswith('.pyc') or fn.endswith('.pyo'): + continue + elif '-i' in opts and abspath(fn) in opts['-i']: + continue + elif abspath(fn) == output: + continue + + if fn[:2] == './': fn = fn[2:] + if process_file(store, fn): + if verbose: + print fn + ": found %d tags" % \ + (fn in store and len(store[fn]) or 0) + num += 1 + else: + if verbose: + print fn + ": binary or not readable" + print + + print "Processed %d of %d files. Found %d tags in %d files." % ( + num, gnum, sum(len(fitem) for fitem in store.itervalues()), len(store)) + + if not store: + return 0 + + HTML = '''\ +<html> +<head> +<title>Code tags report</title> +<style type="text/css"> +body { font-family: Trebuchet MS,Verdana,sans-serif; + width: 80%%; margin-left: auto; margin-right: auto; } +table { width: 100%%; border-spacing: 0; + border: 1px solid #CCC; } +th { font-weight: bold; background-color: #DDD } +td { padding: 2px 5px 2px 5px; + vertical-align: top; } +.tr0 { background-color: #EEEEEE; } +.tr1 { background-color: #F6F6F6; } +.tag { text-align: center; font-weight: bold; } +.tr0 .tag { background-color: #FFEEEE; } +.tr1 .tag { background-color: #FFDDDD; } +.head { padding-top: 10px; font-size: 100%%; font-weight: bold } +.XXX { color: #500; } +.FIXME { color: red; } +.TODO { color: #880; } +</style> +</head> +<body> +<h1>Code tags report for %s</h1> +<table> +<tr><th>Line</th><th>Tag</th><th>Who</th><th>Description</th></tr> +%s +</table> +</body> +</html> +''' + + TABLE = '\n<tr><td class="head" colspan="4">File: %s</td>\n' + + TR = ('<tr class="tr%d"><td class="lno">%%(lno)d</td>' + '<td class="tag %%(tag)s">%%(tag)s</td>' + '<td class="who">%%(who)s</td><td class="what">%%(what)s</td></tr>') + + f = file(output, 'w') + table = '\n'.join(TABLE % fname + + '\n'.join(TR % (no % 2,) % entry + for no, entry in enumerate(store[fname])) + for fname in sorted(store)) + f.write(HTML % (', '.join(map(abspath, args)), table)) + f.close() + + print "Report written to %s." % output + return 0 + +if __name__ == '__main__': + sys.exit(main()) diff --git a/scripts/fix_epydoc_markup.py b/scripts/fix_epydoc_markup.py new file mode 100755 index 00000000..76bbdaed --- /dev/null +++ b/scripts/fix_epydoc_markup.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" + fix_epydoc_tables + ~~~~~~~~~~~~~~~~~ + + Fix epydoc "summary" tables. + + :copyright: 2006 by Georg Brandl. + :license: GNU GPL, see LICENSE for more details. +""" + +import sys, os +from os.path import join + +path = sys.argv[1] + +for fn in os.listdir(path): + fn = join(path, fn) + if not fn.endswith(".html"): + continue + + ll = list(file(fn)) + c = False + d = False + n = False + + for i, l in enumerate(ll): + if "<!-- ===" in l: + d = ("DETAILS" in l) + continue + if l.startswith('<table class="summary"') and d: + ll[i] = '<table class="detsummary"' + l[len('<table class="summary"'):] + c = True + continue + if l.startswith('<table class="navbar"'): + if not n: + n = True + else: + ll[i] = '<div style="height: 20px"> </div>\n' + l + c = True + + if c: + f = file(fn, "w") + f.write(''.join(ll)) + f.close() diff --git a/scripts/pylintrc b/scripts/pylintrc new file mode 100644 index 00000000..94e5ea00 --- /dev/null +++ b/scripts/pylintrc @@ -0,0 +1,355 @@ +# lint Python modules using external checkers. +# +# This is the main checker controling the other ones and the reports +# generation. It is itself both a raw checker and an astng checker in order +# to: +# * handle message activation / deactivation at the module level +# * handle some basic but necessary stats'data (number of classes, methods...) +# +# This checker also defines the following reports: +# * R0001: Total errors / warnings +# * R0002: % errors / warnings by module +# * R0003: Messages +# * R0004: Global evaluation +[MASTER] + +# Profiled execution. +profile=no + +# Add <file or directory> to the black list. It should be a base name, not a +# path. You may set this option multiple times. +ignore=.svn + +# Pickle collected data for later comparisons. +persistent=yes + +# Set the cache size for astng objects. +cache-size=500 + +# List of plugins (as comma separated values of python modules names) to load, +# usually to register additional checkers. +load-plugins= + + +[REPORTS] + +# Tells wether to display a full report or only the messages +reports=no + +# Use HTML as output format instead of text +html=no + +# Use a parseable text output format, so your favorite text editor will be able +# to jump to the line corresponding to a message. +parseable=no + +# Colorizes text output using ansi escape codes +color=yes + +# Put messages in a separate file for each module / package specified on the +# command line instead of printing them on stdout. Reports (if any) will be +# written in a file name "pylint_global.[txt|html]". +files-output=no + +# Python expression which should return a note less than 10 (10 is the highest +# note).You have access to the variables errors warning, statement which +# respectivly contain the number of errors / warnings messages and the total +# number of statements analyzed. This is used by the global evaluation report +# (R0004). +evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) + +# Add a comment according to your evaluation note. This is used by the global +# evaluation report (R0004). +comment=no + +# Include message's id in output +include-ids=no + +# Disable some irrelevant messages. +disable-msg=W0142,W0613,R0201,W0704,C0111,W0703,W0603,I0011,W0122,C0321,W0631,C0203,W0511,W0221,R0401 + + +# checks for +# * unused variables / imports +# * undefined variables +# * redefinition of variable from builtins or from an outer scope +# * use of variable before assigment +# +[VARIABLES] + +# Enable / disable this checker +enable-variables=yes + +# Tells wether we should check for unused import in __init__ files. +init-import=no + +# A regular expression matching names used for dummy variables (i.e. not used). +dummy-variables-rgx=_|dummy + +# List of additional names supposed to be defined in builtins. Remember that +# you should avoid to define new builtins when possible. +additional-builtins= + + +# try to find bugs in the code using type inference +# +[TYPECHECK] + +# Enable / disable this checker +enable-typecheck=yes + +# Tells wether missing members accessed in mixin class should be ignored. A +# mixin class is detected if its name ends with "mixin" (case insensitive). +ignore-mixin-members=yes + +# When zope mode is activated, consider the acquired-members option to ignore +# access to some undefined attributes. +zope=yes + +# List of members which are usually get through zope's acquisition mecanism and +# so shouldn't trigger E0201 when accessed (need zope=yes to be considered). +acquired-members=__subclasses__,__dict__ + + +# checks for : +# * doc strings +# * modules / classes / functions / methods / arguments / variables name +# * number of arguments, local variables, branchs, returns and statements in +# functions, methods +# * required module attributes +# * dangerous default values as arguments +# * redefinition of function / method / class +# * uses of the global statement +# +# This checker also defines the following reports: +# * R0101: Statistics by type +[BASIC] + +# Enable / disable this checker +enable-basic=yes + +# Required attributes for module, separated by a comma +required-attributes= +#__revision__ + +# Regular expression which should only match functions or classes name which do +# not require a docstring +no-docstring-rgx=__.*__ + +# Regular expression which should only match correct module names +module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ + +# Regular expression which should only match correct module level names +const-rgx=(([A-Z_][A-Z1-9_]*)|[A-Za-z_][a-z1-9_]*|(__.*__))$ + +# Regular expression which should only match correct class names +class-rgx=([A-Z_][a-zA-Z0-9]+|[a-z_]+|__metaclass__)$ + +# Regular expression which should only match correct function names +function-rgx=[a-z_][a-z0-9_]{1,30}$ + +# Regular expression which should only match correct method names +method-rgx=[a-z_][a-z0-9_]{1,30}$ + +# Regular expression which should only match correct instance attribute names +attr-rgx=[a-z_][a-z0-9_]{0,30}$ + +# Regular expression which should only match correct argument names +argument-rgx=[a-z_][a-z0-9_]{0,30}$ + +# Regular expression which should only match correct variable names +variable-rgx=[a-z_][a-z0-9_]{0,30}$ + +# Regular expression which should only match correct list comprehension / +# generator expression variable names +inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ + +# Good variable names which should always be accepted, separated by a comma +good-names=i,j,k,ex,Run,_ + +# Bad variable names which should always be refused, separated by a comma +bad-names=foo,bar,baz,toto,tutu,tata + +# List of builtins function names that should not be used, separated by a comma +bad-functions=apply,input + + +# checks for sign of poor/misdesign: +# * number of methods, attributes, local variables... +# * size, complexity of functions, methods +# +[DESIGN] + +# Enable / disable this checker +enable-design=yes + +# Maximum number of arguments for function / method +max-args=12 + +# Maximum number of locals for function / method body +max-locals=30 + +# Maximum number of return / yield for function / method body +max-returns=15 + +# Maximum number of branch for function / method body +max-branchs=50 + +# Maximum number of statements in function / method body +max-statements=120 + +# Maximum number of parents for a class (see R0901). +max-parents=7 + +# Maximum number of attributes for a class (see R0902). +max-attributes=25 + +# Minimum number of public methods for a class (see R0903). +min-public-methods=0 + +# Maximum number of public methods for a class (see R0904). +max-public-methods=30 + + +# checks for usage of new style capabilities on old style classes and +# other new/old styles conflicts problems +# * use of property, __slots__, super +# * "super" usage +# * raising a new style class as exception +# +[NEWSTYLE] + +# Enable / disable this checker +enable-newstyle=yes + + +# checks for +# * external modules dependencies +# * relative / wildcard imports +# * cyclic imports +# * uses of deprecated modules +# +# This checker also defines the following reports: +# * R0401: External dependencies +# * R0402: Modules dependencies graph +[IMPORTS] + +# Enable / disable this checker +enable-imports=yes + +# Deprecated modules which should not be used, separated by a comma +deprecated-modules=regsub,string,TERMIOS,Bastion,rexec + +# Create a graph of every (i.e. internal and external) dependencies in the +# given file (report R0402 must not be disabled) +import-graph= + +# Create a graph of external dependencies in the given file (report R0402 must +# not be disabled) +ext-import-graph= + +# Create a graph of internal dependencies in the given file (report R0402 must +# not be disabled) +int-import-graph= + + +# checks for : +# * methods without self as first argument +# * overriden methods signature +# * access only to existant members via self +# * attributes not defined in the __init__ method +# * supported interfaces implementation +# * unreachable code +# +[CLASSES] + +# Enable / disable this checker +enable-classes=yes + +# List of interface methods to ignore, separated by a comma. This is used for +# instance to not check methods defines in Zope's Interface base class. +ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by + +# List of method names used to declare (i.e. assign) instance attributes. +defining-attr-methods=__init__,__new__,setUp + + +# checks for +# * excepts without exception filter +# * string exceptions +# +[EXCEPTIONS] + +# Enable / disable this checker +enable-exceptions=yes + + +# does not check anything but gives some raw metrics : +# * total number of lines +# * total number of code lines +# * total number of docstring lines +# * total number of comments lines +# * total number of empty lines +# +# This checker also defines the following reports: +# * R0701: Raw metrics +[METRICS] + +# Enable / disable this checker +enable-metrics=yes + + +# checks for similarities and duplicated code. This computation may be +# memory / CPU intensive, so you should disable it if you experiments some +# problems. +# +# This checker also defines the following reports: +# * R0801: Duplication +[SIMILARITIES] + +# Enable / disable this checker +enable-similarities=yes + +# Minimum lines number of a similarity. +min-similarity-lines=10 + +# Ignore comments when computing similarities. +ignore-comments=yes + +# Ignore docstrings when computing similarities. +ignore-docstrings=yes + + +# checks for: +# * warning notes in the code +# * PEP 263: source code with non ascii character but no encoding declaration +# +[MISCELLANEOUS] + +# Enable / disable this checker +enable-miscellaneous=yes + +# List of note tags to take in consideration, separated by a comma. +notes=FIXME,XXX,TODO,HACK + + +# checks for : +# * unauthorized constructions +# * strict indentation +# * line length +# * use of <> instead of != +# +[FORMAT] + +# Enable / disable this checker +enable-format=yes + +# Maximum number of characters on a single line. +max-line-length=90 + +# Maximum number of lines in a module +max-module-lines=1000 + +# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 +# tab). +indent-string=' ' diff --git a/scripts/reindent.py b/scripts/reindent.py new file mode 100755 index 00000000..e6ee8287 --- /dev/null +++ b/scripts/reindent.py @@ -0,0 +1,291 @@ +#! /usr/bin/env python + +# Released to the public domain, by Tim Peters, 03 October 2000. +# -B option added by Georg Brandl, 2006. + +"""reindent [-d][-r][-v] [ path ... ] + +-d (--dryrun) Dry run. Analyze, but don't make any changes to files. +-r (--recurse) Recurse. Search for all .py files in subdirectories too. +-B (--no-backup) Don't write .bak backup files. +-v (--verbose) Verbose. Print informative msgs; else only names of changed files. +-h (--help) Help. Print this usage information and exit. + +Change Python (.py) files to use 4-space indents and no hard tab characters. +Also trim excess spaces and tabs from ends of lines, and remove empty lines +at the end of files. Also ensure the last line ends with a newline. + +If no paths are given on the command line, reindent operates as a filter, +reading a single source file from standard input and writing the transformed +source to standard output. In this case, the -d, -r and -v flags are +ignored. + +You can pass one or more file and/or directory paths. When a directory +path, all .py files within the directory will be examined, and, if the -r +option is given, likewise recursively for subdirectories. + +If output is not to standard output, reindent overwrites files in place, +renaming the originals with a .bak extension. If it finds nothing to +change, the file is left alone. If reindent does change a file, the changed +file is a fixed-point for future runs (i.e., running reindent on the +resulting .py file won't change it again). + +The hard part of reindenting is figuring out what to do with comment +lines. So long as the input files get a clean bill of health from +tabnanny.py, reindent should do a good job. +""" + +__version__ = "1" + +import tokenize +import os +import sys + +verbose = 0 +recurse = 0 +dryrun = 0 +no_backup = 0 + +def usage(msg=None): + if msg is not None: + print >> sys.stderr, msg + print >> sys.stderr, __doc__ + +def errprint(*args): + sep = "" + for arg in args: + sys.stderr.write(sep + str(arg)) + sep = " " + sys.stderr.write("\n") + +def main(): + import getopt + global verbose, recurse, dryrun, no_backup + + try: + opts, args = getopt.getopt(sys.argv[1:], "drvhB", + ["dryrun", "recurse", "verbose", "help", + "no-backup"]) + except getopt.error, msg: + usage(msg) + return + for o, a in opts: + if o in ('-d', '--dryrun'): + dryrun += 1 + elif o in ('-r', '--recurse'): + recurse += 1 + elif o in ('-v', '--verbose'): + verbose += 1 + elif o in ('-B', '--no-backup'): + no_backup += 1 + elif o in ('-h', '--help'): + usage() + return + if not args: + r = Reindenter(sys.stdin) + r.run() + r.write(sys.stdout) + return + for arg in args: + check(arg) + +def check(file): + if os.path.isdir(file) and not os.path.islink(file): + if verbose: + print "listing directory", file + names = os.listdir(file) + for name in names: + fullname = os.path.join(file, name) + if ((recurse and os.path.isdir(fullname) and + not os.path.islink(fullname)) + or name.lower().endswith(".py")): + check(fullname) + return + + if verbose: + print "checking", file, "...", + try: + f = open(file) + except IOError, msg: + errprint("%s: I/O Error: %s" % (file, str(msg))) + return + + r = Reindenter(f) + f.close() + if r.run(): + if verbose: + print "changed." + if dryrun: + print "But this is a dry run, so leaving it alone." + else: + print "reindented", file, (dryrun and "(dry run => not really)" or "") + if not dryrun: + if not no_backup: + bak = file + ".bak" + if os.path.exists(bak): + os.remove(bak) + os.rename(file, bak) + if verbose: + print "renamed", file, "to", bak + f = open(file, "w") + r.write(f) + f.close() + if verbose: + print "wrote new", file + else: + if verbose: + print "unchanged." + + +class Reindenter: + + def __init__(self, f): + self.find_stmt = 1 # next token begins a fresh stmt? + self.level = 0 # current indent level + + # Raw file lines. + self.raw = f.readlines() + + # File lines, rstripped & tab-expanded. Dummy at start is so + # that we can use tokenize's 1-based line numbering easily. + # Note that a line is all-blank iff it's "\n". + self.lines = [line.rstrip('\n \t').expandtabs() + "\n" + for line in self.raw] + self.lines.insert(0, None) + self.index = 1 # index into self.lines of next line + + # List of (lineno, indentlevel) pairs, one for each stmt and + # comment line. indentlevel is -1 for comment lines, as a + # signal that tokenize doesn't know what to do about them; + # indeed, they're our headache! + self.stats = [] + + def run(self): + tokenize.tokenize(self.getline, self.tokeneater) + # Remove trailing empty lines. + lines = self.lines + while lines and lines[-1] == "\n": + lines.pop() + # Sentinel. + stats = self.stats + stats.append((len(lines), 0)) + # Map count of leading spaces to # we want. + have2want = {} + # Program after transformation. + after = self.after = [] + # Copy over initial empty lines -- there's nothing to do until + # we see a line with *something* on it. + i = stats[0][0] + after.extend(lines[1:i]) + for i in range(len(stats)-1): + thisstmt, thislevel = stats[i] + nextstmt = stats[i+1][0] + have = getlspace(lines[thisstmt]) + want = thislevel * 4 + if want < 0: + # A comment line. + if have: + # An indented comment line. If we saw the same + # indentation before, reuse what it most recently + # mapped to. + want = have2want.get(have, -1) + if want < 0: + # Then it probably belongs to the next real stmt. + for j in xrange(i+1, len(stats)-1): + jline, jlevel = stats[j] + if jlevel >= 0: + if have == getlspace(lines[jline]): + want = jlevel * 4 + break + if want < 0: # Maybe it's a hanging + # comment like this one, + # in which case we should shift it like its base + # line got shifted. + for j in xrange(i-1, -1, -1): + jline, jlevel = stats[j] + if jlevel >= 0: + want = have + getlspace(after[jline-1]) - \ + getlspace(lines[jline]) + break + if want < 0: + # Still no luck -- leave it alone. + want = have + else: + want = 0 + assert want >= 0 + have2want[have] = want + diff = want - have + if diff == 0 or have == 0: + after.extend(lines[thisstmt:nextstmt]) + else: + for line in lines[thisstmt:nextstmt]: + if diff > 0: + if line == "\n": + after.append(line) + else: + after.append(" " * diff + line) + else: + remove = min(getlspace(line), -diff) + after.append(line[remove:]) + return self.raw != self.after + + def write(self, f): + f.writelines(self.after) + + # Line-getter for tokenize. + def getline(self): + if self.index >= len(self.lines): + line = "" + else: + line = self.lines[self.index] + self.index += 1 + return line + + # Line-eater for tokenize. + def tokeneater(self, type, token, (sline, scol), end, line, + INDENT=tokenize.INDENT, + DEDENT=tokenize.DEDENT, + NEWLINE=tokenize.NEWLINE, + COMMENT=tokenize.COMMENT, + NL=tokenize.NL): + + if type == NEWLINE: + # A program statement, or ENDMARKER, will eventually follow, + # after some (possibly empty) run of tokens of the form + # (NL | COMMENT)* (INDENT | DEDENT+)? + self.find_stmt = 1 + + elif type == INDENT: + self.find_stmt = 1 + self.level += 1 + + elif type == DEDENT: + self.find_stmt = 1 + self.level -= 1 + + elif type == COMMENT: + if self.find_stmt: + self.stats.append((sline, -1)) + # but we're still looking for a new stmt, so leave + # find_stmt alone + + elif type == NL: + pass + + elif self.find_stmt: + # This is the first "real token" following a NEWLINE, so it + # must be the first token of the next program statement, or an + # ENDMARKER. + self.find_stmt = 0 + if line: # not endmarker + self.stats.append((sline, self.level)) + +# Count number of leading blanks. +def getlspace(line): + i, n = 0, len(line) + while i < n and line[i] == " ": + i += 1 + return i + +if __name__ == '__main__': + main() |
