summaryrefslogtreecommitdiff
path: root/scripts
diff options
context:
space:
mode:
authorgbrandl <devnull@localhost>2006-10-19 20:27:28 +0200
committergbrandl <devnull@localhost>2006-10-19 20:27:28 +0200
commitf4d019954468db777760d21f9243eca8b852c184 (patch)
tree328b8f8fac25338306b0e7b827686dcc7597df23 /scripts
downloadpygments-f4d019954468db777760d21f9243eca8b852c184.tar.gz
[svn] Name change, round 4 (rename SVN root folder).
Diffstat (limited to 'scripts')
-rwxr-xr-xscripts/check_sources.py231
-rw-r--r--scripts/count_loc.py48
-rw-r--r--scripts/epydoc.css280
-rwxr-xr-xscripts/find_codetags.py205
-rwxr-xr-xscripts/fix_epydoc_markup.py46
-rw-r--r--scripts/pylintrc355
-rwxr-xr-xscripts/reindent.py291
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('&', '&amp;'). \
+ replace('<', '&lt;'). \
+ replace('>', '&gt;'). \
+ replace('"', '&quot;')
+
+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">&nbsp;</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()