[jhbuild/release-team-commands: 1/6] new command to create the 2.99 report page
- From: Frederic Peters <fpeters src gnome org>
- To: svn-commits-list gnome org
- Subject: [jhbuild/release-team-commands: 1/6] new command to create the 2.99 report page
- Date: Mon, 4 May 2009 05:28:48 -0400 (EDT)
commit cbdb6b2cbfc976e764298d69810bdcc6f939b98c
Author: Frederic Peters <fpeters 0d be>
Date: Mon May 4 10:25:28 2009 +0200
new command to create the 2.99 report page
---
jhbuild/commands/goalreport.py | 719 ++++++++++++++++++++++++++++++++++++++++
1 files changed, 719 insertions(+), 0 deletions(-)
diff --git a/jhbuild/commands/goalreport.py b/jhbuild/commands/goalreport.py
new file mode 100644
index 0000000..a7258d6
--- /dev/null
+++ b/jhbuild/commands/goalreport.py
@@ -0,0 +1,719 @@
+# jhbuild - a build script for GNOME 2.x
+# Copyright (C) 2009 Frederic Peters
+#
+# goalreport.py: report GNOME modules status wrt various goals
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+import os
+import re
+import sys
+import subprocess
+import cPickle
+from optparse import make_option
+try:
+ from cStringIO import StringIO
+except ImportError:
+ from StringIO import StringIO
+
+try:
+ import elementtree.ElementTree as ET
+except ImportError:
+ import xml.etree.ElementTree as ET
+
+try:
+ import curses
+except ImportError:
+ curses = None
+
+from jhbuild.errors import FatalError
+import jhbuild.moduleset
+from jhbuild.commands import Command, register_command
+from jhbuild.utils import httpcache
+
+
+try: t_bold = cmds.get_output(['tput', 'bold'])
+except: t_bold = ''
+try: t_reset = cmds.get_output(['tput', 'sgr0'])
+except: t_reset = ''
+
+
+HTML_AT_TOP = '''<html>
+<head>
+<title>2.99</title>
+<style type="text/css">
+body {
+ font-family: sans-serif;
+}
+tfoot th, thead th {
+ font-weight: normal;
+}
+td.dunno { background: #aaa; }
+td.todo-low { background: #fce94f; }
+td.todo-average { background: #fcaf3e; }
+td.todo-complex { background: #ef2929; }
+td.ok { background: #8ae234; }
+td.heading {
+ text-align: center;
+ background: #555753;
+ color: white;
+ font-weight: bold;
+}
+tbody th {
+ background: #d3d7cf;
+ text-align: left;
+}
+tbody td {
+ text-align: center;
+}
+tfoot td {
+ padding-top: 1em;
+ vertical-align: top;
+}
+
+tbody tr:hover th {
+ background: #2e3436;
+ color: #d3d7cf;
+}
+
+a.bug-closed {
+ text-decoration: line-through;
+}
+
+a.warn-bug-status::after {
+ content: " \\26A0";
+ color: #ef2929;
+ font-weight: bold;
+}
+
+</style>
+</head>
+<body>
+
+<p style="font-size: small;">
+Disclaimer: Complexities are (mis)calculated arbitrary on the following basis:
+1) for libraries low/average/complex are relative to the number of includes
+of a library header (<5/<20/>=20); 2) for deprecated symbols thet are
+relative to the number of deprecated symbols in use (<5/<20/>=20).
+</p>
+
+'''
+
+class ExcludedModuleException(Exception):
+ pass
+
+class CouldNotPerformCheckException(Exception):
+ pass
+
+
+class Check:
+ complexity = 'average'
+ status = 'dunno'
+ result_comment = None
+
+ excluded_modules = []
+
+ def __init__(self, config, module):
+ if module.name in (self.excluded_modules or []):
+ raise ExcludedModuleException()
+ self.config = config
+ self.module = module
+
+ def fix_false_positive(self, false_positive):
+ if not false_positive:
+ return
+ self.status = 'ok'
+
+
+class ShellCheck(Check):
+ cmd = None
+ cmds = None
+
+ def run(self):
+ if not self.cmds:
+ self.cmds = [self.cmd]
+ outputs = []
+ for cmd in self.cmds:
+ outputs.append(subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True,
+ cwd=self.module.branch.srcdir).communicate()[0].strip())
+ nb_lines = sum([len(x.splitlines()) for x in outputs])
+ if nb_lines == 0:
+ self.status = 'ok'
+ elif nb_lines <= 5:
+ self.status = 'todo'
+ self.complexity = 'low'
+ elif nb_lines <= 20:
+ self.status = 'todo'
+ self.complexity = 'average'
+ else:
+ self.status = 'todo'
+ self.complexity = 'complex'
+
+FIND_C = "find -name '*.[ch]' -or -name '*.cpp' -or -name '*.cc'"
+
+class LibBonobo(ShellCheck):
+ cmds = (
+ FIND_C + " | xargs grep '#include <libbonobo'",
+ FIND_C + " | xargs grep '#include <bonobo'",
+ FIND_C + " | xargs grep 'BonoboObject'",
+ FIND_C + " | xargs grep 'BonoboApplication'",
+ "find -name '*.py' | xargs grep 'import .*bonobo'",
+ )
+
+class LibGnome(ShellCheck):
+ cmds = (
+ FIND_C + " | xargs grep '#include <libgnome/' | "\
+ "egrep -v 'gnome-desktop-item.h|gnome-desktop-utils.h'",
+ # gnome-desktop installs stuff under libgnome/
+ FIND_C + " | xargs grep '#include <gnome.h>'",
+ "find -name '*.cs' | xargs grep 'Gnome.Url.'", # as 'using ...' is not mandatory
+ )
+
+class LibGnomeUi(ShellCheck):
+ cmds = (
+ FIND_C + " | xargs grep '#include <libgnomeui/' | egrep -v '"\
+ "gnome-rr.h|"\
+ "gnome-rr-config.h|"\
+ "gnome-desktop-thumbnail.h|"\
+ "gnome-bg-crossfade.h|"\
+ "gnome-bg.h'", # gnome-desktop installs stuff under libgnomeui/
+ "find -name '*.py' | xargs grep 'import .*gnome\.ui'",
+ )
+
+class LibGnomeCanvas(ShellCheck):
+ cmds = (
+ FIND_C + " | xargs grep '#include <libgnomecanvas/'",
+ "find -name '*.py' | xargs grep 'import .*gnomecanvas'",
+ )
+
+class LibArtLgpl(ShellCheck):
+ cmds = (
+ FIND_C + " | xargs grep '#include <libart_lgpl/'",
+ "find -name '*.cs' | xargs grep '^using Art;'",
+ )
+
+class LibGnomeVfs(ShellCheck):
+ cmds = (
+ FIND_C + " | xargs grep '#include <libgnomevfs/'",
+ "find -name '*.py' | xargs grep 'import .*gnomevfs'",
+ "find -name '*.cs' | xargs grep '^using Gnome.Vfs'",
+ )
+
+class LibGnomePrint(ShellCheck):
+ cmds = (
+ FIND_C + " | xargs grep '#include <libgnomeprint'",
+ "find -name '*.py' | xargs grep 'import .*gnomeprint'",
+ )
+
+
+class Esound(ShellCheck):
+ cmd = FIND_C + " | xargs grep '#include <esd.h>'"
+
+class Orbit(ShellCheck):
+ cmds = (
+ FIND_C + " | xargs grep '#include <orbit'",
+ "find -name '*.py' | xargs grep 'import .*bonobo'",
+ )
+
+class LibGlade(ShellCheck):
+ excluded_modules = ('libglade',)
+ cmds = (
+ FIND_C + " | xargs grep '#include <glade/'",
+ "find -name '*.py' | xargs grep 'import .*glade'",
+ "find -name '*.cs' | xargs grep '^using Glade'",
+ )
+
+class GConf(ShellCheck):
+ excluded_modules = ('gconf',)
+ cmds = (
+ FIND_C + " | xargs grep '#include <gconf/'",
+ "find -name '*.py' | xargs grep 'import .*gconf'",
+ "find -name '*.cs' | xargs grep '^using GConf'",
+ )
+
+
+class DeprecatedSymbolsCheck(Check):
+ cached_symbols = {}
+
+ def run(self):
+ symbols = self.load_deprecated_symbols()
+ symbol_regex = re.compile(r'[\s\(\){}\+\|&-](%s)[\s\(\){}\+\|&-]' % '|'.join(symbols))
+ deprecated_and_used = {}
+ try:
+ for base, dirnames, filenames in os.walk(self.module.branch.srcdir):
+ filenames = [x for x in filenames if \
+ os.path.splitext(x)[-1] in ('.c', '.cc', '.cpp', '.h', '.glade')]
+ for filename in filenames:
+ for s in symbol_regex.findall(file(os.path.join(base, filename)).read()):
+ deprecated_and_used[s] = True
+ except UnicodeDecodeError:
+ raise ExcludedModuleException()
+ self.bad_symbols = deprecated_and_used.keys()
+ self.compute_status()
+
+ def compute_status(self):
+ nb_symbols = len(self.bad_symbols)
+ if nb_symbols == 0:
+ self.status = 'ok'
+ elif nb_symbols <= 5:
+ self.status = 'todo'
+ self.complexity = 'low'
+ elif nb_symbols <= 20:
+ self.status = 'todo'
+ self.complexity = 'average'
+ else:
+ self.status = 'todo'
+ self.complexity = 'complex'
+ if self.status == 'todo':
+ self.result_comment = ', '.join(sorted(self.bad_symbols))
+ else:
+ self.result_comment = None
+
+ def load_deprecated_symbols(self):
+ if self.cached_symbols.get(self.devhelp_filenames):
+ return self.cached_symbols.get(self.devhelp_filenames)
+ symbols = []
+ for devhelp_filename in self.devhelp_filenames:
+ try:
+ devhelp_path = os.path.join(self.config.devhelp_dirname, devhelp_filename)
+ tree = ET.parse(devhelp_path)
+ except:
+ raise CouldNotPerformCheckException()
+ for keyword in tree.findall('//{http://www.devhelp.net/book}keyword'):
+ if not keyword.attrib.has_key('deprecated'):
+ continue
+ name = keyword.attrib.get('name').replace('enum ', '').replace('()', '').strip()
+ symbols.append(name)
+ DeprecatedSymbolsCheck.cached_symbols[self.devhelp_filenames] = symbols
+ return symbols
+
+ def fix_false_positive(self, false_positive):
+ if not false_positive:
+ return
+ for symbol in false_positive.split(','):
+ symbol = symbol.strip()
+ if symbol in self.bad_symbols:
+ self.bad_symbols.remove(symbol)
+ self.compute_status()
+
+
+class GlibDeprecatedSymbols(DeprecatedSymbolsCheck):
+ devhelp_filenames = ('glib.devhelp2', 'gobject.devhelp2', 'gio.devhelp2')
+ excluded_modules = ('glib',)
+
+class GtkDeprecatedSymbols(DeprecatedSymbolsCheck):
+ devhelp_filenames = ('gdk.devhelp2', 'gdk-pixbuf.devhelp2', 'gtk.devhelp2')
+ excluded_modules = ('gtk+',)
+
+
+class GObjectIntrospectionSupport(Check):
+ def run(self):
+ pkg_config = False
+ gir_file = False
+ try:
+ for base, dirnames, filenames in os.walk(self.module.branch.srcdir):
+ if [x for x in filenames if x.endswith('.pc') or x.endswith('.pc.in')]:
+ pkg_config = True
+ if [x for x in filenames if x.endswith('.gir')]:
+ gir_file = True
+ if not gir_file and 'Makefile.am' in filenames:
+ # if there is no .gir, we may simply be in an unbuilt module,
+ # let's look up for a .gir target in the Makefile.am
+ makefile_am = file(os.path.join(base, 'Makefile.am')).read()
+ if re.findall(r'^[A-Za-z0-9.\-]+\.gir:', makefile_am, re.MULTILINE):
+ gir_file = True
+ if pkg_config and gir_file:
+ break
+ except UnicodeDecodeError:
+ raise ExcludedModuleException()
+
+ if not pkg_config:
+ raise ExcludedModuleException()
+
+ if gir_file:
+ self.status = 'ok'
+ else:
+ self.status = 'todo'
+ self.complexity = 'average'
+
+ def fix_false_positive(self, false_positive):
+ if not false_positive:
+ return
+ if false_positive == 'n/a':
+ raise ExcludedModuleException()
+ self.status = 'ok'
+
+
+checks = [LibBonobo, LibGnome, LibGnomeUi, LibGnomeCanvas, LibArtLgpl,
+ LibGnomeVfs, LibGnomePrint, Esound, Orbit, LibGlade, GConf,
+ GlibDeprecatedSymbols, GtkDeprecatedSymbols,
+ GObjectIntrospectionSupport]
+
+class cmd_goalreport(Command):
+ doc = _('Report GNOME modules status wrt various goals')
+ name = 'goalreport'
+
+ def __init__(self):
+ Command.__init__(self, [
+ make_option('-o', '--output', metavar='FILE',
+ action='store', dest='output', default=None),
+ make_option('--bugs-file', metavar='BUGFILE',
+ action='store', dest='bugfile', default=None),
+ make_option('--false-positives-file', metavar='FILE',
+ action='store', dest='falsepositivesfile', default=None),
+ make_option('--devhelp-dirname', metavar='DIR',
+ action='store', dest='devhelp_dirname', default=None),
+ make_option('--no-cache',
+ action='store_true', dest='nocache', default=False),
+ make_option('--all-modules',
+ action='store_true', dest='list_all_modules', default=False),
+ ])
+
+ def run(self, config, options, args):
+ if options.output:
+ output = StringIO()
+ global curses
+ if curses and config.progress_bar:
+ try:
+ curses.setupterm()
+ except:
+ curses = None
+ else:
+ output = sys.stdout
+
+ self.load_bugs(options.bugfile)
+ self.load_false_positives(options.falsepositivesfile)
+
+ config.devhelp_dirname = options.devhelp_dirname
+
+ module_set = jhbuild.moduleset.load(config)
+ if options.list_all_modules:
+ self.module_list = module_set.modules.values()
+ else:
+ self.module_list = module_set.get_module_list(args or config.modules, config.skip)
+
+ results = {}
+ try:
+ cachedir = os.path.join(os.environ['XDG_CACHE_HOME'], 'jhbuild')
+ except KeyError:
+ cachedir = os.path.join(os.environ['HOME'], '.cache','jhbuild')
+ if not options.nocache:
+ try:
+ results = cPickle.load(file(os.path.join(cachedir, 'twoninetynine.pck')))
+ except:
+ pass
+
+ for module_num, mod in enumerate(self.module_list):
+ if mod.type in ('meta', 'tarball'):
+ continue
+ if not mod.branch or not mod.branch.repository.__class__.__name__ in (
+ 'SubversionRepository', 'GitRepository'):
+ if not mod.moduleset_name.startswith('gnome-external-deps'):
+ continue
+
+ if not os.path.exists(mod.branch.srcdir):
+ continue
+
+ tree_id = mod.branch.tree_id()
+ valid_cache = (tree_id and results.get(mod.name, {}).get('tree-id') == tree_id)
+
+ if not mod.name in results:
+ results[mod.name] = {
+ 'results': {}
+ }
+ results[mod.name]['tree-id'] = tree_id
+ r = results[mod.name]['results']
+ for check in checks:
+ if valid_cache and check.__name__ in r:
+ continue
+ try:
+ c = check(config, mod)
+ except ExcludedModuleException:
+ continue
+
+ if output != sys.stdout and config.progress_bar:
+ progress_percent = 1.0 * (module_num-1) / len(self.module_list)
+ msg = '%s: %s' % (mod.name, check.__name__)
+ self.display_status_line(progress_percent, module_num, msg)
+
+ try:
+ c.run()
+ except CouldNotPerformCheckException:
+ continue
+ except ExcludedModuleException:
+ continue
+
+ try:
+ c.fix_false_positive(self.false_positives.get((mod.name, check.__name__)))
+ except ExcludedModuleException:
+ continue
+
+ r[check.__name__] = [c.status, c.complexity, c.result_comment]
+
+ if not os.path.exists(cachedir):
+ os.makedirs(cachedir)
+ cPickle.dump(results, file(os.path.join(cachedir, 'twoninetynine.pck'), 'w'))
+
+ print >> output, HTML_AT_TOP
+ print >> output, '<table>'
+ print >> output, '<thead>'
+ print >> output, '<tr><td></td>'
+ for check in checks:
+ print >> output, '<th>%s</th>' % check.__name__
+ print >> output, '<td></td></tr>'
+ print >> output, '</thead>'
+ print >> output, '<tbody>'
+
+ suites = [
+ ('meta-gnome-desktop-suite', 'Desktop'),
+ ('meta-gnome-devel-platform', 'Platform'),
+ ('meta-gnome-admin', 'Admin'),
+ ('meta-gnome-devtools-suite', 'Development Tools'),
+ ('meta-gnome-bindings-c++', 'Bindings (C++)'),
+ ('meta-gnome-bindings-python', 'Bindings (Python)'),
+ ('meta-gnome-bindings-mono', 'Bindings (Mono)'),
+ ]
+
+ processed_modules = {'gnome-common': True}
+
+ # mark deprecated modules as processed, so they don't show in "Others"
+ for meta_key in ('meta-gnome-devel-platform-upcoming-deprecations',
+ 'meta-gnome-desktop-upcoming-deprecations'):
+ metamodule = module_set.get_module(meta_key)
+ for module_name in metamodule.dependencies:
+ processed_modules[module_name] = True
+
+ not_other_module_names = []
+ for suite_key, suite_label in suites:
+ metamodule = module_set.get_module(suite_key)
+ module_names = [x for x in metamodule.dependencies if x in results]
+ if not module_names:
+ continue
+ print >> output, '<tr><td class="heading" colspan="%d">%s</td></tr>' % (
+ 2+len(checks), suite_label)
+ for module_name in module_names:
+ r = results[module_name].get('results')
+ print >> output, self.get_mod_line(module_name, r)
+ processed_modules[module_name] = True
+ not_other_module_names.extend(module_names)
+
+ external_deps = [x for x in results.keys() if \
+ not x in processed_modules and \
+ module_set.get_module(x).moduleset_name.startswith('gnome-external-deps')]
+ if external_deps:
+ print >> output, '<tr><td class="heading" colspan="%d">%s</td></tr>' % (
+ 2+len(checks), 'External Dependencies')
+ for module_name in sorted(external_deps):
+ if not module_name in results:
+ continue
+ r = results[module_name].get('results')
+ try:
+ version = module_set.get_module(module_name).branch.version
+ except:
+ version = None
+ print >> output, self.get_mod_line(module_name, r, version_number=version)
+
+ other_module_names = [x for x in results.keys() if \
+ not x in processed_modules and not x in external_deps]
+ if other_module_names:
+ print >> output, '<tr><td class="heading" colspan="%d">%s</td></tr>' % (
+ 2+len(checks), 'Others')
+ for module_name in sorted(other_module_names):
+ if not module_name in results:
+ continue
+ r = results[module_name].get('results')
+ print >> output, self.get_mod_line(module_name, r)
+ print >> output, '</tbody>'
+ print >> output, '<tfoot>'
+
+ print >> output, '<tr><td></td>'
+ for check in checks:
+ print >> output, '<th>%s</th>' % check.__name__
+ print >> output, '<td></td></tr>'
+
+ print >> output, self.get_stat_line(results, not_other_module_names)
+ print >> output, '</tfoot>'
+ print >> output, '</table>'
+
+ print >> output, '</body>'
+ print >> output, '</html>'
+
+ if output != sys.stdout:
+ file(options.output, 'w').write(output.getvalue())
+
+ if output != sys.stdout and config.progress_bar:
+ sys.stdout.write('\n')
+ sys.stdout.flush()
+
+
+ def get_mod_line(self, module_name, r, version_number=None):
+ s = []
+ s.append('<tr>')
+ if version_number:
+ s.append('<th>%s (%s)</th>' % (module_name, version_number))
+ else:
+ s.append('<th>%s</th>' % module_name)
+ for check in checks:
+ ri = r.get(check.__name__)
+ if not ri:
+ classname = 'n-a'
+ label = 'n/a'
+ comment = ''
+ else:
+ classname = ri[0]
+ if classname == 'todo':
+ classname += '-' + ri[1]
+ label = ri[1]
+ else:
+ label = ri[0]
+ comment = ri[2] or ''
+ if label == 'ok':
+ label = ''
+ s.append('<td class="%s" title="%s">' % (classname, comment))
+ k = (module_name, check.__name__)
+ if k in self.bugs:
+ bug_class = ''
+ if self.bug_status.get(self.bugs[k]):
+ if label == '':
+ bug_class = ' class="bug-closed"'
+ else:
+ bug_class = ' class="bug-closed warn-bug-status"'
+ if self.bugs[k].isdigit():
+ s.append('<a href="http://bugzilla.gnome.org/show_bug.cgi?id=%s"%s>' % (
+ self.bugs[k], bug_class))
+ else:
+ s.append('<a href="%s"%s>' % (self.bugs[k], bug_class))
+ if label == '':
+ label = 'done'
+ s.append(label)
+ if k in self.bugs:
+ s.append('</a>')
+ s.append('</td>')
+ s.append('<th>%s</th>' % module_name)
+ s.append('</tr>')
+ return '\n'.join(s)
+
+ def get_stat_line(self, results, module_names):
+ s = []
+ s.append('<tr>')
+ s.append('<td>Stats<br/>(excluding "Others")</td>')
+ for check in checks:
+ s.append('<td>')
+ for complexity in ('low', 'average', 'complex'):
+ nb_modules = len([x for x in module_names if \
+ results[x].get('results') and
+ results[x]['results'].get(check.__name__) and
+ results[x]['results'][check.__name__][0] == 'todo' and
+ results[x]['results'][check.__name__][1] == complexity])
+ s.append('%s: %s' % (complexity, nb_modules))
+ s.append('<br/>')
+ nb_with_bugs = 0
+ nb_with_bugs_done = 0
+ for module_name in module_names:
+ k = (module_name, check.__name__)
+ if not k in self.bugs or not check.__name__ in results[module_name]['results']:
+ continue
+ nb_with_bugs += 1
+ if results[module_name]['results'][check.__name__][0] == 'ok':
+ nb_with_bugs_done += 1
+ if nb_with_bugs:
+ s.append('<br/>')
+ s.append('fixed: %d%%' % (100.*nb_with_bugs_done/nb_with_bugs))
+ s.append('</td>')
+ s.append('<td></td>')
+ s.append('</tr>')
+ return '\n'.join(s)
+
+ def load_bugs(self, filename):
+ # Bug file format:
+ # $(module)/$(checkname) $(bugnumber)
+ # Sample bug file:
+ # evolution/LibGnomeCanvas 571742
+ if not filename:
+ filename = 'http://live.gnome.org/FredericPeters/Bugs299?action=raw'
+ if filename.startswith('http://'):
+ try:
+ filename = httpcache.load(filename, age=0)
+ except Exception, e:
+ raise FatalError(_('could not download %s: %s') % (filename, e))
+ self.bugs = {}
+ for line in file(filename):
+ line = line.strip()
+ if not line:
+ continue
+ if line.startswith('#'):
+ continue
+ part, bugnumber = line.split()
+ module_name, check = part.split('/')
+ self.bugs[(module_name, check)] = bugnumber
+
+ self.bug_status = {}
+
+ bug_status = httpcache.load(
+ 'http://bugzilla.gnome.org/show_bug.cgi?%s&'
+ 'ctype=xml&field=bug_id&field=bug_status&'
+ 'field=resolution' % '&'.join(['id=' + x for x in self.bugs.values() if x.isdigit()]))
+ tree = ET.parse(bug_status)
+ for bug in tree.findall('bug'):
+ bug_id = bug.find('bug_id').text
+ bug_resolved = (bug.find('resolution') is not None)
+ self.bug_status[bug_id] = bug_resolved
+
+ def load_false_positives(self, filename):
+ if not filename:
+ filename = 'http://live.gnome.org/FredericPeters/FalsePositives299?action=raw'
+ if filename.startswith('http://'):
+ try:
+ filename = httpcache.load(filename, age=0)
+ except Exception, e:
+ raise FatalError(_('could not download %s: %s') % (filename, e))
+ self.false_positives = {}
+ for line in file(filename):
+ line = line.strip()
+ if not line:
+ continue
+ if line.startswith('#'):
+ continue
+ if ' ' in line:
+ part, extra = line.split(' ', 1)
+ else:
+ part, extra = line, '-'
+ module_name, check = part.split('/')
+ self.false_positives[(module_name, check)] = extra
+
+
+ def display_status_line(self, progress, module_num, message):
+ if not curses:
+ return
+ columns = curses.tigetnum('cols')
+ width = columns / 2
+ num_hashes = int(round(progress * width))
+ progress_bar = '[' + (num_hashes * '=') + ((width - num_hashes) * '-') + ']'
+
+ module_no_digits = len(str(len(self.module_list)))
+ format_str = '%%%dd' % module_no_digits
+ module_pos = '[' + format_str % (module_num+1) + '/' + format_str % len(self.module_list) + ']'
+
+ output = '%s %s %s%s%s' % (progress_bar, module_pos, t_bold, message, t_reset)
+ if len(output) > columns:
+ output = output[:columns]
+ else:
+ output += ' ' * (columns-len(output))
+
+ sys.stdout.write(output + '\r')
+ sys.stdout.flush()
+
+
+register_command(cmd_goalreport)
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]