From: Ed Page Date: Sat, 31 Oct 2009 03:28:25 +0000 (-0500) Subject: More hildonizing prep X-Git-Url: http://git.maemo.org/git/?p=gonvert;a=commitdiff_plain;h=99de0afb81d7972694d0a0f714cd189504cd7be1 More hildonizing prep --- diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..cfefc13 --- /dev/null +++ b/Makefile @@ -0,0 +1,101 @@ +PROJECT_NAME=gonvert +SOURCE_PATH=src +SOURCE=$(shell find $(SOURCE_PATH) -iname "*.py") +PROGRAM=$(SOURCE_PATH)/$(PROJECT_NAME).py +DATA_PATH=data +DATA_TYPES=*.ini *.map *.glade *.png +DATA=$(foreach type, $(DATA_TYPES), $(shell find $(DATA_PATH) -iname "$(type)")) +OBJ=$(SOURCE:.py=.pyc) +BUILD_PATH=./build +TAG_FILE=~/.ctags/$(PROJECT_NAME).tags +TODO_FILE=./TODO + +DEBUGGER=winpdb +UNIT_TEST=nosetests --with-doctest -w . +SYNTAX_TEST=support/test_syntax.py +STYLE_TEST=../../Python/tools/pep8.py --ignore=W191,E501 +LINT_RC=./support/pylint.rc +LINT=pylint --rcfile=$(LINT_RC) +PROFILE_GEN=python -m cProfile -o .profile +PROFILE_VIEW=python -m pstats .profile +TODO_FINDER=support/todo.py +CTAGS=ctags-exuberant + +.PHONY: all run profile debug test build lint tags todo clean distclean + +all: test + +run: $(OBJ) + $(SOURCE_PATH)/$(PROJECT_NAME)_glade.py + +profile: $(OBJ) + $(PROFILE_GEN) $(PROGRAM) + $(PROFILE_VIEW) + +debug: $(OBJ) + $(DEBUGGER) $(PROGRAM) + +test: $(OBJ) + $(UNIT_TEST) + +package: $(OBJ) + rm -Rf $(BUILD_PATH) + + mkdir -p $(BUILD_PATH)/generic + cp $(SOURCE_PATH)/constants.py $(BUILD_PATH)/generic + cp $(SOURCE_PATH)/$(PROJECT_NAME).py $(BUILD_PATH)/generic + $(foreach file, $(DATA), cp $(file) $(BUILD_PATH)/generic/$(subst /,-,$(file)) ; ) + $(foreach file, $(SOURCE), cp $(file) $(BUILD_PATH)/generic/$(subst /,-,$(file)) ; ) + #$(foreach file, $(OBJ), cp $(file) $(BUILD_PATH)/generic/$(subst /,-,$(file)) ; ) + cp support/$(PROJECT_NAME).desktop $(BUILD_PATH)/generic + cp support/builddeb.py $(BUILD_PATH)/generic + cp support/py2deb.py $(BUILD_PATH)/generic + + mkdir -p $(BUILD_PATH)/chinook + cp -R $(BUILD_PATH)/generic/* $(BUILD_PATH)/chinook + cd $(BUILD_PATH)/chinook ; python builddeb.py chinook + mkdir -p $(BUILD_PATH)/diablo + cp -R $(BUILD_PATH)/generic/* $(BUILD_PATH)/diablo + cd $(BUILD_PATH)/diablo ; python builddeb.py diablo + mkdir -p $(BUILD_PATH)/fremantle + cp -R $(BUILD_PATH)/generic/* $(BUILD_PATH)/fremantle + cd $(BUILD_PATH)/fremantle ; python builddeb.py fremantle + mkdir -p $(BUILD_PATH)/mer + cp -R $(BUILD_PATH)/generic/* $(BUILD_PATH)/mer + cd $(BUILD_PATH)/mer ; python builddeb.py mer + +lint: $(OBJ) + $(foreach file, $(SOURCE), $(LINT) $(file) ; ) + +tags: $(TAG_FILE) + +todo: $(TODO_FILE) + +clean: + rm -Rf $(OBJ) + rm -Rf $(BUILD_PATH) + rm -Rf $(TODO_FILE) + +distclean: + rm -Rf $(OBJ) + rm -Rf $(BUILD_PATH) + rm -Rf $(TAG_FILE) + find $(SOURCE_PATH) -name "*.*~" | xargs rm -f + find $(SOURCE_PATH) -name "*.swp" | xargs rm -f + find $(SOURCE_PATH) -name "*.bak" | xargs rm -f + find $(SOURCE_PATH) -name ".*.swp" | xargs rm -f + +$(TAG_FILE): $(OBJ) + mkdir -p $(dir $(TAG_FILE)) + $(CTAGS) -o $(TAG_FILE) $(SOURCE) + +$(TODO_FILE): $(SOURCE) + @- $(TODO_FINDER) $(SOURCE) > $(TODO_FILE) + +%.pyc: %.py + $(SYNTAX_TEST) $< + +#Makefile Debugging +#Target to print any variable, can be added to the dependencies of any other target +#Userfule flags for make, -d, -p, -n +print-%: ; @$(error $* is $($*) ($(value $*)) (from $(origin $*))) diff --git a/support/builddeb.py b/support/builddeb.py new file mode 100755 index 0000000..8685250 --- /dev/null +++ b/support/builddeb.py @@ -0,0 +1,259 @@ +#!/usr/bin/python2.5 + +import os +import sys + +import py2deb + +import constants + + +__appname__ = constants.__app_name__ +__description__ = """Unit Conversions +A conversion utility that allows conversion between many units like CGS, Ancient, Imperial with many categories like length, mass, numbers, etc. All units converted values shown at once as you type +. +Homepage: http://www.unihedron.com/projects/gonvert/index.php +""" +__author__ = "Ed Page (Maemo Porter)" +__email__ = "eopage@byu.net" +__version__ = constants.__version__ +__build__ = constants.__build__ +__changelog__ = """ +0.2.24 +* Added shortcuts for fullscreen +* Switched to Find being brought up by CTRL+F +* Added Find Previous and Find Next shortcuts (CTRL+P, CTRL+N) +* Debugging: Added logging support +* Internal: Massive cleanup of code + +0.2.23 - Added UK currency category and other UK measurements thanks to Dale Hair +0.2.22 - Restore previously used window size +0.2.21 - Category column widened. Maximize on start. +0.2.20 - correction in micron pressure conversion +0.2.19 - viscosity cP conversion correction +0.2.18 - addition of magnitudes per square arcsecond to Luminance category +0.2.17 - updated baud definitions + - fixed homepath location because new debian version changed +0.2.16 - fixed icon locating for display in about + - added alternate icon gonvert-icon_alernative.png (copy over gonvert.png) +0.2.15 - updated mainloop to main as discovered by Alexander Skwar +0.2.14 - added Calgary energy and volume suggestions per Kim Lux +0.2.13 - new more easily understandable icon + - nanotesla definition (nT). + - added shortlist feature. +0.2.12 - removed inoperable books feature. + - fixed up acre accuracy. +0.2.11 - miodified descriprion for silver, newton, sadzhens. +0.2.10 - \x90 changed to \u00C9 for Emile and similar for Reaumur utf-8 text. + - Added translation for "All" book text. + - The write units text is translatable. + - The pl_messages.po file has been updated +0.2.09 - Added utf-8 coding to all text strings in preparation for complete language translation. +0.2.08 - Added language translation for menus and labels. +0.2.07 - Added language translation changes and messages.pot. +0.2.06 - Fixed category list size to show preselected categorys on startup, + scroll window H&Vpolicy set to always. +0.2.05 - Spelling of Luminance category fixed. +0.2.04 - Modified unit clicking to force focus on value entry. + Modified Makefile to remove /share/share bug for desktop entry. +0.2.03 - Modified Makefile to allow better integration on other platforms. +0.2.01 - Added saved selections feature, creates ~/.gonvert/ and file. +0.1.11 - fixed packaging for RPM +0.1.10 - added Current Loop category for PLCs and 4-20mA instrumentation. +0.1.9 - added kilobit, and more density units. +0.1.8 - Added Torque units +0.1.7 - Added many more pressure units + - Added thermal categories + - Added fuel consumption category + - Program extension to .pyw so that Windows startup without console +0.1.6 - add more frequency units + - fixed computer number bases nums was bad near "h" and "v" + - fixed error: + "GtkTextBuffer.insert_at_cursor() takes exactly 1 argument (2 given)" + thanks to Riccardo Galli +0.1.5 - put packages into /usr instead of /usr/local + - add gnome menu item back in +0.1.4 - remove dependency on gnome-config from Makefile, RPM, binary. +0.1.3 - touched up computer numbers units for better sorting + - limited up resizing of windows to prevent dissapearing areas + - fixed find bug that some users might notice (TreeViewColumn/None) +0.1.2 - Added description box when writing units +0.1.1 - Added help/about box + - fixed bug that sets focus on line 2480 + - fixed temperature difference units labels + - all scroll bars only show when needed + - Added RPM distribution +0.1.0 - Major modifications for GTK2 (RedHat 8.0) + - addition of units column in display + - sorting for all units columns with sort pointer +0.0.15 - added Electromagnetic Radiation category +0.0.14 - fixed window close bug, attempt to fix libglade XML startup bug for + some machines +0.0.13 - changes for python2.2, had to remove gnome dependencies +0.0.12 - change contact information address +0.0.11 - addition of ppm to "find" utility +0.0.10 - addition of petabyte to computer data +0.0.9 - addition of cesium atom vibrations to Time category +0.0.8 - more accurate calculation of degrees F +0.0.7 - added 'Find unit' feature + - changed Category list to clist for ease of moveto (focus) after find +0.0.6 - added description for Amperes + - added DENSITY category + - added 4 new categories 101 new units + - added shoe size converter + - add a function to convert custom formulas (like area from diameter) + example: area = pi * (D/2)^2 + base value = pi* (x/2)^2 #metres in diameter metres, cm, inch, foot. +0.0.5 - Tool for listing all categories and units to STDOUT. + - re-organization of project files. + - addition of suffixes between duodecillion and centillion. + - addition of Makefile to install onto Gnome based systems. + - sort Units or Value columns (ascending or descending) + by clicking on column. +0.0.4 - Prefixes and Suffixes addition of; + ppm, %, Marx brothers, various descriptions. + - addition of microgram to mass category. + - replaced base 63 with 62 from computer numbers since + only 62 characters can be represented. + - fixed error if second line has nothing it wouldn't get + updated. +0.0.3 - fix bug in labelling of base 36 (was base 37) + all numbering systems past 23 were at fault due + to improper nums string (fixed). +0.0.2 - Completion of second row data entry so that changes + to text are not cyclicly causing changes to all + values. +0.0.1 - Initial release. +""" + + +__postinstall__ = """#!/bin/sh -e + +gtk-update-icon-cache -f /usr/share/icons/hicolor +rm -f ~/.gonvert/gonvert.log +""" + +__preremove__ = """#!/bin/sh -e +""" + + +def find_files(prefix, path): + for root, dirs, files in os.walk(path): + for file in files: + if file.startswith(prefix+"-"): + fileParts = file.split("-") + unused, relPathParts, newName = fileParts[0], fileParts[1:-1], fileParts[-1] + assert unused == prefix + relPath = os.sep.join(relPathParts) + yield relPath, file, newName + + +def unflatten_files(files): + d = {} + for relPath, oldName, newName in files: + if relPath not in d: + d[relPath] = [] + d[relPath].append((oldName, newName)) + return d + + +def build_package(distribution): + try: + os.chdir(os.path.dirname(sys.argv[0])) + except: + pass + + py2deb.Py2deb.SECTIONS = py2deb.SECTIONS_BY_POLICY[distribution] + p = py2deb.Py2deb(__appname__) + p.prettyName = constants.__pretty_app_name__ + p.description = __description__ + p.bugTracker = "" + p.upgradeDescription = __changelog__.split("\n\n", 1)[0] + p.author = __author__ + p.mail = __email__ + p.license = "lgpl" + p.depends = ", ".join([ + "python2.6 | python2.5", + "python-gtk2 | python2.5-gtk2", + "python-xml | python2.5-xml", + "python-dbus | python2.5-dbus", + ]) + maemoSpecificDepends = ", python-osso | python2.5-osso, python-hildon | python2.5-hildon" + p.depends += { + "debian": ", python-glade2", + "chinook": maemoSpecificDepends, + "diablo": maemoSpecificDepends, + "fremantle": maemoSpecificDepends + ", python-glade2", + "mer": maemoSpecificDepends + ", python-glade2", + }[distribution] + p.recommends = ", ".join([ + ]) + p.section = { + "debian": "science", + "chinook": "other", + "diablo": "user/science", + "fremantle": "user/science", + "mer": "user/science", + }[distribution] + p.arch = "all" + p.urgency = "low" + p.distribution = "chinook diablo fremantle mer debian" + p.repository = "extras" + p.changelog = __changelog__ + p.postinstall = __postinstall__ + p.preremove = __preremove__ + p.icon = { + "debian": "data-pixmaps-gonvert.png", + "chinook": "data-pixmaps-gonvert.png", + "diablo": "data-pixmaps-gonvert.png", + "fremantle": "data-pixmaps-gonvert.png", # Fremantle natively uses 48x48 + "mer": "data-pixmaps-gonvert.png", + }[distribution] + p["/usr/bin"] = [ "gonvert.py" ] + for relPath, files in unflatten_files(find_files("src", ".")).iteritems(): + fullPath = "/usr/lib/gonvert" + if relPath: + fullPath += os.sep+relPath + p[fullPath] = list( + "|".join((oldName, newName)) + for (oldName, newName) in files + ) + for relPath, files in unflatten_files(find_files("data", ".")).iteritems(): + fullPath = "/usr/lib/gonvert" + if relPath: + fullPath += os.sep+relPath + p[fullPath] = list( + "|".join((oldName, newName)) + for (oldName, newName) in files + ) + p["/usr/share/applications/hildon"] = ["gonvert.desktop"] + p["/usr/share/icons/hicolor/26x26/hildon"] = ["data-pixmaps-gonvert.png|gonvert.png"] + p["/usr/share/icons/hicolor/64x64/hildon"] = ["data-pixmaps-gonvert.png|gonvert.png"] + p["/usr/share/icons/hicolor/scalable/hildon"] = ["data-pixmaps-gonvert.png|gonvert.png"] + + print p + print p.generate( + version="%s-%s" % (__version__, __build__), + changelog=__changelog__, + build=False, + tar=True, + changes=True, + dsc=True, + ) + print "Building for %s finished" % distribution + + +if __name__ == "__main__": + if len(sys.argv) > 1: + try: + import optparse + except ImportError: + optparse = None + + if optparse is not None: + parser = optparse.OptionParser() + (commandOptions, commandArgs) = parser.parse_args() + else: + commandArgs = None + commandArgs = ["diablo"] + build_package(commandArgs[0]) diff --git a/support/pylint.rc b/support/pylint.rc new file mode 100644 index 0000000..2a371a1 --- /dev/null +++ b/support/pylint.rc @@ -0,0 +1,305 @@ +# 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...) +# +[MASTER] + +# Specify a configuration file. +#rcfile= + +# Python code to execute, usually for sys.path manipulation such as +# pygtk.require(). +#init-hook= + +# Profiled execution. +profile=no + +# Add to the black list. It should be a base name, not a +# path. You may set this option multiple times. +ignore=CVS + +# 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= + + +[MESSAGES CONTROL] + +# Enable only checker(s) with the given id(s). This option conflicts with the +# disable-checker option +#enable-checker= + +# Enable all checker(s) except those with the given id(s). This option +# conflicts with the enable-checker option +#disable-checker= + +# Enable all messages in the listed categories. +#enable-msg-cat= + +# Disable all messages in the listed categories. +#disable-msg-cat= + +# Enable the message(s) with the given id(s). +#enable-msg= + +# Disable the message(s) with the given id(s). +disable-msg=W0403,W0612,W0613,C0103,C0111,C0301,R0903,W0142,W0603,R0904,R0921,R0201 + +[REPORTS] + +# set the output format. Available formats are text, parseable, colorized, msvs +# (visual studio) and html +output-format=colorized + +# Include message's id in output +include-ids=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 + +# Tells wether to display a full report or only the messages +reports=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 + +# Enable the report(s) with the given id(s). +#enable-report= + +# Disable the report(s) with the given id(s). +#disable-report= + + +# checks for +# * unused variables / imports +# * undefined variables +# * redefinition of variable from builtins or from an outer scope +# * use of variable before assigment +# +[VARIABLES] + +# 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= + + +# 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 +# +[BASIC] + +# Required attributes for module, separated by a comma +required-attributes= + +# 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_]*)|(__.*__))$ + +# Regular expression which should only match correct class names +class-rgx=[A-Z_][a-zA-Z0-9]+$ + +# Regular expression which should only match correct function names +function-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression which should only match correct method names +method-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression which should only match correct instance attribute names +attr-rgx=[a-z_][a-zA-Z0-9_]{2,30}$ + +# Regular expression which should only match correct argument names +argument-rgx=[a-z_][a-zA-Z0-9_]{2,30}$ + +# Regular expression which should only match correct variable names +variable-rgx=[a-z_][a-zA-Z0-9_]{2,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=map,filter,apply,input + + +# try to find bugs in the code using type inference +# +[TYPECHECK] + +# 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=no + +# 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=REQUEST,acl_users,aq_parent + + +# checks for sign of poor/misdesign: +# * number of methods, attributes, local variables... +# * size, complexity of functions, methods +# +[DESIGN] + +# Maximum number of arguments for function / method +max-args=5 + +# Maximum number of locals for function / method body +max-locals=15 + +# Maximum number of return / yield for function / method body +max-returns=6 + +# Maximum number of branch for function / method body +max-branchs=12 + +# Maximum number of statements in function / method body +max-statements=50 + +# Maximum number of parents for a class (see R0901). +max-parents=7 + +# Maximum number of attributes for a class (see R0902). +max-attributes=15 + +# Minimum number of public methods for a class (see R0903). +min-public-methods=1 + +# Maximum number of public methods for a class (see R0904). +max-public-methods=20 + + +# checks for : +# * methods without self as first argument +# * overridden methods signature +# * access only to existant members via self +# * attributes not defined in the __init__ method +# * supported interfaces implementation +# * unreachable code +# +[CLASSES] + +# 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 +# * external modules dependencies +# * relative / wildcard imports +# * cyclic imports +# * uses of deprecated modules +# +[IMPORTS] + +# 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 similarities and duplicated code. This computation may be +# memory / CPU intensive, so you should disable it if you experiments some +# problems. +# +[SIMILARITIES] + +# Minimum lines number of a similarity. +min-similarity-lines=4 + +# Ignore comments when computing similarities. +ignore-comments=yes + +# Ignore docstrings when computing similarities. +ignore-docstrings=yes + + +# checks for: +# * warning notes in the code like FIXME, XXX +# * PEP 263: source code with non ascii character but no encoding declaration +# +[MISCELLANEOUS] + +# List of note tags to take in consideration, separated by a comma. +notes=FIXME,XXX,TODO + + +# checks for : +# * unauthorized constructions +# * strict indentation +# * line length +# * use of <> instead of != +# +[FORMAT] + +# Maximum number of characters on a single line. +# @note Limiting this to the most extreme cases +max-line-length=100 + +# 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='\t' diff --git a/support/test_syntax.py b/support/test_syntax.py new file mode 100755 index 0000000..65a373c --- /dev/null +++ b/support/test_syntax.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python + +import commands + + +verbose = False + + +def syntax_test(file): + commandTemplate = """ + python -t -t -W all -c "import py_compile; py_compile.compile ('%(filename)s', doraise=False)" """ + compileCommand = commandTemplate % {"filename": file} + (status, text) = commands.getstatusoutput (compileCommand) + text = text.rstrip() + passed = len(text) == 0 + + if passed: + output = ("Syntax is correct for "+file) if verbose else "" + else: + output = ("Syntax is invalid for %s\n" % file) if verbose else "" + output += text + return (passed, output) + + +if __name__ == "__main__": + import sys + import os + import optparse + + opar = optparse.OptionParser() + opar.add_option("-v", "--verbose", dest="verbose", help="Toggle verbosity", action="store_true", default=False) + options, args = opar.parse_args(sys.argv[1:]) + verbose = options.verbose + + completeOutput = [] + allPassed = True + for filename in args: + passed, output = syntax_test(filename) + if not passed: + allPassed = False + if output.strip(): + completeOutput.append(output) + print "\n".join(completeOutput) + + sys.exit(0 if allPassed else 1); diff --git a/support/todo.py b/support/todo.py new file mode 100755 index 0000000..90cbd04 --- /dev/null +++ b/support/todo.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python + +from __future__ import with_statement +import itertools + + +verbose = False + + +def tag_parser(file, tag): + """ + >>> nothing = [] + >>> for todo in tag_parser(nothing, "@todo"): + ... print todo + ... + >>> one = ["@todo Help!"] + >>> for todo in tag_parser(one, "@todo"): + ... print todo + ... + 1: @todo Help! + >>> mixed = ["one", "@todo two", "three"] + >>> for todo in tag_parser(mixed, "@todo"): + ... print todo + ... + 2: @todo two + >>> embedded = ["one @todo two", "three"] + >>> for todo in tag_parser(embedded, "@todo"): + ... print todo + ... + 1: @todo two + >>> continuation = ["one", "@todo two", " three"] + >>> for todo in tag_parser(continuation, "@todo"): + ... print todo + ... + 2: @todo two three + >>> series = ["one", "@todo two", "@todo three"] + >>> for todo in tag_parser(series, "@todo"): + ... print todo + ... + 2: @todo two + 3: @todo three + """ + currentTodo = [] + prefix = None + for lineNumber, line in enumerate(file): + column = line.find(tag) + if column != -1: + if currentTodo: + yield "\n".join (currentTodo) + prefix = line[0:column] + currentTodo = ["%d: %s" % (lineNumber+1, line[column:].strip())] + elif prefix is not None and len(prefix)+1 < len(line) and line.startswith(prefix) and line[len(prefix)].isspace(): + currentTodo.append (line[len(prefix):].rstrip()) + elif currentTodo: + yield "\n".join (currentTodo) + currentTodo = [] + prefix = None + if currentTodo: + yield "\n".join (currentTodo) + + +def tag_finder(filename, tag): + todoList = [] + + with open(filename) as file: + body = "\n".join (tag_parser(file, tag)) + passed = not body + if passed: + output = "No %s's for %s" % (tag, filename) if verbose else "" + else: + header = "%s's for %s:\n" % (tag, filename) if verbose else "" + output = header + body + output += "\n" if verbose else "" + + return (passed, output) + + +if __name__ == "__main__": + import sys + import os + import optparse + + opar = optparse.OptionParser() + opar.add_option("-v", "--verbose", dest="verbose", help="Toggle verbosity", action="store_true", default=False) + options, args = opar.parse_args(sys.argv[1:]) + verbose = options.verbose + + bugsAsError = True + todosAsError = False + + completeOutput = [] + allPassed = True + for filename in args: + bugPassed, bugOutput = tag_finder(filename, "@bug") + todoPassed, todoOutput = tag_finder(filename, "@todo") + output = "\n".join ([bugOutput, todoOutput]) + if (not bugPassed and bugsAsError) or (not todoPassed and todosAsError): + allPassed = False + output = output.strip() + if output: + completeOutput.append(filename+":\n"+output+"\n\n") + print "\n".join(completeOutput) + + sys.exit(0 if allPassed else 1);