diff options
-rw-r--r-- | ChangeLog | 5 | ||||
-rw-r--r-- | __pkginfo__.py | 2 | ||||
-rw-r--r-- | adbh.py | 4 | ||||
-rw-r--r-- | clcommands.py | 4 | ||||
-rw-r--r-- | configuration.py | 84 | ||||
-rw-r--r-- | debian.hardy/control | 34 | ||||
-rwxr-xr-x | debian.hardy/rules | 78 | ||||
-rw-r--r-- | debian.intrepid/control | 39 | ||||
-rwxr-xr-x | debian.intrepid/rules | 82 | ||||
-rw-r--r-- | debian.jaunty/control | 34 | ||||
-rwxr-xr-x | debian.jaunty/rules | 81 | ||||
-rwxr-xr-x | debian.lenny/rules | 73 | ||||
-rw-r--r-- | debian.stable/control | 39 | ||||
-rwxr-xr-x | debian.stable/rules | 82 | ||||
-rw-r--r-- | debian/changelog | 6 | ||||
-rwxr-xr-x | debian/rules | 7 | ||||
-rw-r--r-- | decorators.py | 1 | ||||
-rw-r--r-- | doc/makefile | 2 | ||||
-rw-r--r-- | graph.py | 9 | ||||
-rw-r--r-- | html.py | 125 | ||||
-rw-r--r-- | logging_ext.py | 2 | ||||
-rw-r--r-- | optik_ext.py | 9 | ||||
-rw-r--r-- | setup.py | 8 | ||||
-rw-r--r-- | table.py | 7 | ||||
-rw-r--r-- | tasksqueue.py | 6 | ||||
-rw-r--r-- | test/unittest_configuration.py | 3 | ||||
-rw-r--r-- | test/unittest_html.py | 59 | ||||
-rw-r--r-- | test/unittest_testlib.py | 16 | ||||
-rw-r--r-- | textutils.py | 21 |
29 files changed, 319 insertions, 603 deletions
@@ -2,7 +2,10 @@ ChangeLog for logilab.common ============================ -- - * configuration: fix #8849 Using plugins, options and .pylintrc crashes PyLint + * configuration: + - proper bytes and time option types support + - make Method usable as 'callback' value + - fix #8849 Using plugins, options and .pylintrc crashes PyLint 2009-08-26 -- 0.45.0 * added function for parsing XML processing instructions diff --git a/__pkginfo__.py b/__pkginfo__.py index 148b3d8..6c8e06a 100644 --- a/__pkginfo__.py +++ b/__pkginfo__.py @@ -8,7 +8,7 @@ __docformat__ = "restructuredtext en" distname = 'logilab-common' modname = 'common' -numversion = (0, 45, 0) +numversion = (0, 45, 1) version = '.'.join([str(num) for num in numversion]) license = 'GPL' @@ -318,7 +318,7 @@ class _PGAdvFuncHelper(_GenericAdvFuncHelper): cmd.append('--username=%s' % dbuser) if not keepownership: cmd.append('--no-owner') - cmd.append('--file=%s' % backupfile) + cmd.append('--file="%s"' % backupfile) cmd.append(dbname) return ' '.join(cmd) @@ -337,7 +337,7 @@ class _PGAdvFuncHelper(_GenericAdvFuncHelper): cmd.append('--dbname %s' % dbname) if not keepownership: cmd.append('--no-owner') - cmd.append(backupfile) + cmd.append('"%s"' % backupfile) cmds.append(' '.join(cmd)) return cmds diff --git a/clcommands.py b/clcommands.py index 9f722b0..fe190f2 100644 --- a/clcommands.py +++ b/clcommands.py @@ -4,7 +4,7 @@ one command. e.g called as "tool command [options] args..." where <options> and <args> are command'specific -:copyright: 2003-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +:copyright: 2003-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved. :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr :license: General Public License version 2 - http://www.gnu.org/licenses """ @@ -18,7 +18,7 @@ from logilab.common.configuration import Configuration DEFAULT_COPYRIGHT = '''\ -Copyright (c) 2004-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +Copyright (c) 2004-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved. http://www.logilab.fr/ -- mailto:contact@logilab.fr''' diff --git a/configuration.py b/configuration.py index b56c841..539fa9c 100644 --- a/configuration.py +++ b/configuration.py @@ -103,13 +103,13 @@ from ConfigParser import ConfigParser, NoOptionError, NoSectionError, \ from logilab.common.compat import set from logilab.common.textutils import normalize_text, unquote -from logilab.common.optik_ext import OptionParser, OptionGroup, Values, \ - OptionValueError, OptionError, HelpFormatter, generate_manpage, check_date, \ - check_yn, check_csv, check_file, check_color, check_named, check_password,\ - NO_DEFAULT, OPTPARSE_FORMAT_DEFAULT +from logilab.common.deprecation import deprecated +from logilab.common import optik_ext as opt REQUIRED = [] +check_csv = deprecated('use lgc.optik_ext.check_csv')(opt.check_csv) + class UnsupportedAction(Exception): """raised by set_option when it doesn't know what to do for an action""" @@ -134,50 +134,58 @@ def choice_validator(opt_dict, name, value): """ if not value in opt_dict['choices']: msg = "option %s: invalid value: %r, should be in %s" - raise OptionValueError(msg % (name, value, opt_dict['choices'])) + raise opt.OptionValueError(msg % (name, value, opt_dict['choices'])) return value def multiple_choice_validator(opt_dict, name, value): """validate and return a converted value for option of type 'choice' """ choices = opt_dict['choices'] - values = check_csv(None, name, value) + values = opt.check_csv(None, name, value) for value in values: if not value in choices: msg = "option %s: invalid value: %r, should be in %s" - raise OptionValueError(msg % (name, value, choices)) + raise opt.OptionValueError(msg % (name, value, choices)) return values def csv_validator(opt_dict, name, value): """validate and return a converted value for option of type 'csv' """ - return check_csv(None, name, value) + return opt.check_csv(None, name, value) def yn_validator(opt_dict, name, value): """validate and return a converted value for option of type 'yn' """ - return check_yn(None, name, value) + return opt.check_yn(None, name, value) def named_validator(opt_dict, name, value): """validate and return a converted value for option of type 'named' """ - return check_named(None, name, value) + return opt.check_named(None, name, value) def file_validator(opt_dict, name, value): """validate and return a filepath for option of type 'file'""" - return check_file(None, name, value) + return opt.check_file(None, name, value) def color_validator(opt_dict, name, value): """validate and return a valid color for option of type 'color'""" - return check_color(None, name, value) + return opt.check_color(None, name, value) def password_validator(opt_dict, name, value): """validate and return a string for option of type 'password'""" - return check_password(None, name, value) + return opt.check_password(None, name, value) def date_validator(opt_dict, name, value): """validate and return a mx DateTime object for option of type 'date'""" - return check_date(None, name, value) + return opt.check_date(None, name, value) + +def time_validator(opt_dict, name, value): + """validate and return a time object for option of type 'time'""" + return opt.check_time(None, name, value) + +def bytes_validator(opt_dict, name, value): + """validate and return an integer for option of type 'bytes'""" + return opt.check_bytes(None, name, value) VALIDATORS = {'string' : unquote, @@ -193,6 +201,8 @@ VALIDATORS = {'string' : unquote, 'named': named_validator, 'password': password_validator, 'date': date_validator, + 'time': time_validator, + 'bytes': bytes_validator, 'choice': choice_validator, 'multiple_choice': multiple_choice_validator, } @@ -205,10 +215,10 @@ def _call_validator(opttype, optdict, option, value): except TypeError: try: return VALIDATORS[opttype](value) - except OptionValueError: + except opt.OptionValueError: raise except: - raise OptionValueError('%s value (%r) should be of type %s' % + raise opt.OptionValueError('%s value (%r) should be of type %s' % (option, value, opttype)) # user input functions ######################################################## @@ -234,7 +244,7 @@ def _make_input_function(opttype): return None try: return _call_validator(opttype, optdict, None, value) - except OptionValueError, ex: + except opt.OptionValueError, ex: msg = str(ex).split(':', 1)[-1].strip() print 'bad value: %s' % msg return input_validator @@ -264,7 +274,7 @@ def expand_default(self, option): optname = provider.option_name(optname, optdict) value = getattr(provider.config, optname, optdict) value = format_option_value(optdict, value) - if value is NO_DEFAULT or not value: + if value is opt.NO_DEFAULT or not value: value = self.NO_DEFAULT_VALUE return option.help.replace(self.default_tag, str(value)) @@ -299,6 +309,10 @@ def format_option_value(optdict, value): value = value and 'yes' or 'no' elif isinstance(value, (str, unicode)) and value.isspace(): value = "'%s'" % value + elif optdict.get('type') == 'time' and isinstance(value, (float, int, long)): + value = "%ss" % value + elif optdict.get('type') == 'bytes' and isinstance(value, (int, long)): + value = "%sB" % value return value def ini_format_section(stream, section, options, encoding=None, doc=None): @@ -367,7 +381,7 @@ class OptionsManagerMixIn(object): # configuration file parser self._config_parser = ConfigParser() # command line parser - self._optik_parser = OptionParser(usage=usage, version=version) + self._optik_parser = opt.OptionParser(usage=usage, version=version) self._optik_parser.options_manager = self def register_options_provider(self, provider, own_group=True): @@ -413,7 +427,7 @@ class OptionsManagerMixIn(object): self._mygroups.add(group_name) # add option group to the command line parser if options: - group = OptionGroup(self._optik_parser, + group = opt.OptionGroup(self._optik_parser, title=group_name.capitalize()) self._optik_parser.add_option_group(group) # add provider's specific options @@ -435,7 +449,7 @@ class OptionsManagerMixIn(object): # default is handled here and *must not* be given to optik if you # want the whole machinery to work if 'default' in opt_dict: - if (OPTPARSE_FORMAT_DEFAULT and 'help' in opt_dict and + if (opt.OPTPARSE_FORMAT_DEFAULT and 'help' in opt_dict and opt_dict.get('default') is not None and not opt_dict['action'] in ('store_true', 'store_false')): opt_dict['help'] += ' [current: %default]' @@ -502,8 +516,8 @@ class OptionsManagerMixIn(object): """ self._monkeypatch_expand_default() try: - generate_manpage(self._optik_parser, pkginfo, - section, stream=stream or sys.stdout) + opt.generate_manpage(self._optik_parser, pkginfo, + section, stream=stream or sys.stdout) finally: self._unmonkeypatch_expand_default() @@ -603,7 +617,7 @@ class OptionsManagerMixIn(object): def add_help_section(self, title, description): """add a dummy option section for help purpose """ - group = OptionGroup(self._optik_parser, + group = opt.OptionGroup(self._optik_parser, title=title.capitalize(), description=description) self._optik_parser.add_option_group(group) @@ -611,16 +625,16 @@ class OptionsManagerMixIn(object): def _monkeypatch_expand_default(self): # monkey patch optparse to deal with our default values try: - self.__expand_default_backup = HelpFormatter.expand_default - HelpFormatter.expand_default = expand_default + self.__expand_default_backup = opt.HelpFormatter.expand_default + opt.HelpFormatter.expand_default = expand_default except AttributeError: # python < 2.4: nothing to be done pass def _unmonkeypatch_expand_default(self): # remove monkey patch - if hasattr(HelpFormatter, 'expand_default'): + if hasattr(opt.HelpFormatter, 'expand_default'): # unpatch optparse to avoid side effects - HelpFormatter.expand_default = self.__expand_default_backup + opt.HelpFormatter.expand_default = self.__expand_default_backup def help(self): """return the usage string for available options """ @@ -644,9 +658,9 @@ class Method(object): if self._inst is None: self._inst = instance - def __call__(self): + def __call__(self, *args, **kwargs): assert self._inst, 'unbound method' - return getattr(self._inst, self.method)() + return getattr(self._inst, self.method)(*args, **kwargs) class OptionsProviderMixIn(object): @@ -658,7 +672,7 @@ class OptionsProviderMixIn(object): options = () def __init__(self): - self.config = Values() + self.config = opt.Values() for option in self.options: try: option, optdict = option @@ -666,6 +680,8 @@ class OptionsProviderMixIn(object): raise Exception('Bad option: %r' % option) if isinstance(optdict.get('default'), Method): optdict['default'].bind(self) + elif isinstance(optdict.get('callback'), Method): + optdict['callback'].bind(self) self.load_defaults() def load_defaults(self): @@ -768,7 +784,7 @@ class OptionsProviderMixIn(object): for opt in self.options: if opt[0] == opt_name: return opt[1] - raise OptionError('no such option in section %r' % self.name, opt_name) + raise opt.OptionError('no such option in section %r' % self.name, opt_name) def all_options(self): @@ -836,7 +852,7 @@ class ConfigurationMixIn(OptionsManagerMixIn, OptionsProviderMixIn): def __getitem__(self, key): try: return getattr(self.config, self.option_name(key)) - except (OptionValueError, AttributeError): + except (opt.OptionValueError, AttributeError): raise KeyError(key) def __setitem__(self, key, value): @@ -845,7 +861,7 @@ class ConfigurationMixIn(OptionsManagerMixIn, OptionsProviderMixIn): def get(self, key, default=None): try: return getattr(self.config, self.option_name(key)) - except (OptionError, AttributeError): + except (opt.OptionError, AttributeError): return default diff --git a/debian.hardy/control b/debian.hardy/control deleted file mode 100644 index 481c76f..0000000 --- a/debian.hardy/control +++ /dev/null @@ -1,34 +0,0 @@ -Source: logilab-common -Section: python -Priority: optional -Maintainer: Debian Python Modules Team <python-modules-team@lists.alioth.debian.org> -Uploaders: David Douard <david.douard@logilab.fr>, Fabrice Douchant <fabrice.douchant@logilab.fr>, Alexandre Fayolle <afayolle@debian.org>, Sandro Tosi <morph@debian.org>, Dorothe Sénéchal <dorothee.senechal@logilab.fr>, Adrien Di Mascio <Adrien.DiMascio@logilab.fr> -Build-Depends: debhelper (>= 5.0.38), python (>= 2.3.5-7) -Build-Depends-Indep: python-support, python-epydoc, graphviz -XS-Python-Version: all -Standards-Version: 3.7 -Homepage: http://www.logilab.org/project/logilab-common -Vcs-Svn: svn://svn.debian.org/svn/python-modules/packages/logilab-common/trunk/ -Vcs-Browser: http://svn.debian.org/viewsvn/python-modules/packages/logilab-common/trunk/ - -Package: python-logilab-common -Architecture: all -Provides: ${python:Provides} -Depends: ${python:Depends}, ${misc:Depends} -Recommends: python-egenix-mxdatetime -Conflicts: python-constraint ( <= 0.3.0-4), python-logilab-astng ( <= 0.16.0-1), pylint ( << 0.11.0-1), devtools ( <= 0.9.0-1), logilab-doctools ( <= 0.1.6-4), python-logilab-aspects ( <= 0.1.4-2), python2.3-logilab-common, python2.4-logilab-common -XB-Python-Version: ${python:Versions} -Description: useful miscellaneous modules used by Logilab projects - logilab-common is a collection of low-level Python packages and modules, - designed to ease: - . - * handling command line options and configuration files - * writing interactive command line tools - * manipulation files and character strings - * interfacing to OmniORB - * generating of SQL queries - * running unit tests - * manipulating tree structures - * accessing RDBMS (currently postgreSQL, MySQL and sqlite) - * generating text and HTML reports - * logging diff --git a/debian.hardy/rules b/debian.hardy/rules deleted file mode 100755 index 0ae2f44..0000000 --- a/debian.hardy/rules +++ /dev/null @@ -1,78 +0,0 @@ -#!/usr/bin/make -f -# Sample debian/rules that uses debhelper. -# GNU copyright 1997 to 1999 by Joey Hess. -# -# adapted by Logilab for automatic generation by debianize -# (part of the devtools project, http://www.logilab.org/projects/devtools) -# -# Copyright (c) 2003-2008 LOGILAB S.A. (Paris, FRANCE). -# http://www.logilab.fr/ -- mailto:contact@logilab.fr - -# Uncomment this to turn on verbose mode. -#export DH_VERBOSE=1 - -build: build-stamp -build-stamp: - dh_testdir - - # python module build - NO_SETUPTOOLS=1 python setup.py -q build - - # run tests -ifeq (,$(findstring nocheck,$(DEB_BUILD_OPTIONS))) - # we need this hack because we have to import "logilab.common.pytest" - # but since it's a namespace package, we need to "simulate" it - touch $(CURDIR)/build/lib/logilab/__init__.py - -PYTHONPATH=$(CURDIR)/build/lib/ $(CURDIR)/build/scripts-2.5/pytest - rm -f $(CURDIR)/build/lib/logilab/__init__.py -endif - - touch build-stamp - -clean: - dh_testdir - dh_testroot - - # clean doc - $(MAKE) -C doc clean - - NO_SETUPTOOLS=1 python setup.py clean - - [ ! -d build ] || rm -rf build - - find . -name "*.pyc" -delete - - dh_clean build-stamp - -install: build - dh_testdir - dh_testroot - dh_clean -k - dh_installdirs - - NO_SETUPTOOLS=1 python setup.py -q install --no-compile --root=debian/python-logilab-common/ --install-headers=usr/include/ --install-scripts=usr/bin/ - # remove test directory - rm -rf debian/python-logilab-common/usr/lib/python*/site-packages/logilab/common/test - -# Build architecture-independent files here. -binary-indep: build install - dh_testdir - dh_testroot - dh_install -i - dh_pysupport -i - dh_installchangelogs -i ChangeLog - dh_installexamples -i - dh_installdocs -i README - dh_installman -i - dh_link -i - dh_compress -i -X.py -X.ini -X.xml -Xtest/ - dh_fixperms -i - dh_installdeb -i - dh_gencontrol -i - dh_md5sums -i - dh_builddeb -i - -binary-arch: - -binary: binary-indep -.PHONY: build clean binary binary-indep binary-arch diff --git a/debian.intrepid/control b/debian.intrepid/control deleted file mode 100644 index 80fafda..0000000 --- a/debian.intrepid/control +++ /dev/null @@ -1,39 +0,0 @@ -Source: logilab-common -Section: python -Priority: optional -Maintainer: Debian Python Modules Team <python-modules-team@lists.alioth.debian.org> -Uploaders: David Douard <david.douard@logilab.fr>, - Alexandre Fayolle <afayolle@debian.org>, - Sandro Tosi <morph@debian.org>, - Dorothe Sénéchal <dorothee.senechal@logilab.fr>, - Adrien Di Mascio <Adrien.DiMascio@logilab.fr>, - Nicolas Chauvat <nicolas.chauvat@logilab.fr> -Build-Depends: debhelper (>= 5.0.37.2), python (>= 2.4) -Build-Depends-Indep: python-support, python-epydoc, graphviz -XS-Python-Version: all -Standards-Version: 3.8.1 -Homepage: http://www.logilab.org/project/logilab-common -Vcs-Svn: svn://svn.debian.org/svn/python-modules/packages/logilab-common/trunk/ -Vcs-Browser: http://svn.debian.org/viewsvn/python-modules/packages/logilab-common/trunk/ - -Package: python-logilab-common -Architecture: all -Provides: ${python:Provides} -Depends: ${python:Depends}, ${misc:Depends} -Recommends: python-egenix-mxdatetime -Conflicts: python-constraint ( <= 0.3.0-4), python-logilab-astng ( <= 0.16.0-1), pylint ( << 0.11.0-1), devtools ( <= 0.9.0-1), logilab-doctools ( <= 0.1.6-4), python-logilab-aspects ( <= 0.1.4-2), python2.3-logilab-common, python2.4-logilab-common -XB-Python-Version: ${python:Versions} -Description: useful miscellaneous modules used by Logilab projects - logilab-common is a collection of low-level Python packages and modules, - designed to ease: - . - * handling command line options and configuration files - * writing interactive command line tools - * manipulation files and character strings - * interfacing to OmniORB - * generating of SQL queries - * running unit tests - * manipulating tree structures - * accessing RDBMS (currently postgreSQL, MySQL and sqlite) - * generating text and HTML reports - * logging diff --git a/debian.intrepid/rules b/debian.intrepid/rules deleted file mode 100755 index 80739ef..0000000 --- a/debian.intrepid/rules +++ /dev/null @@ -1,82 +0,0 @@ -#!/usr/bin/make -f -# Sample debian/rules that uses debhelper. -# GNU copyright 1997 to 1999 by Joey Hess. -# -# adapted by Logilab for automatic generation by debianize -# (part of the devtools project, http://www.logilab.org/projects/devtools) -# -# Copyright (c) 2003-2008 LOGILAB S.A. (Paris, FRANCE). -# http://www.logilab.fr/ -- mailto:contact@logilab.fr - -# Uncomment this to turn on verbose mode. -#export DH_VERBOSE=1 - -build: build-stamp -build-stamp: - dh_testdir - - # python module build - NO_SETUPTOOLS=1 python setup.py -q build - - # run tests -ifeq (,$(findstring nocheck,$(DEB_BUILD_OPTIONS))) - # we need this hack because we have to import "logilab.common.pytest" - # but since it's a namespace package, we need to "simulate" it - touch $(CURDIR)/build/lib/logilab/__init__.py - PYTHONPATH=$(CURDIR)/build/lib/ $(CURDIR)/build/scripts-2.5/pytest - rm -f $(CURDIR)/build/lib/logilab/__init__.py -endif - - # build doc - $(MAKE) -C doc - - touch build-stamp - -clean: - dh_testdir - dh_testroot - - # clean doc - $(MAKE) -C doc clean - - NO_SETUPTOOLS=1 python setup.py clean - - [ ! -d build ] || rm -rf build - - find . -name "*.pyc" -delete - - dh_clean build-stamp - -install: build - dh_testdir - dh_testroot - dh_clean -k - dh_installdirs - - NO_SETUPTOOLS=1 python setup.py -q install --no-compile \ - --root=$(CURDIR)/debian/python-logilab-common/ - # remove test directory - rm -rf debian/python-logilab-common/usr/lib/python*/site-packages/logilab/common/test - -# Build architecture-independent files here. -binary-indep: build install - dh_testdir - dh_testroot - dh_install -i - dh_pysupport -i - dh_installchangelogs -i ChangeLog - dh_installexamples -i - dh_installdocs -i README doc/apidoc/ - dh_installman -i - dh_link -i - dh_compress -i -X.py -X.ini -X.xml -Xtest/ -Xapidoc/ - dh_fixperms -i - dh_installdeb -i - dh_gencontrol -i - dh_md5sums -i - dh_builddeb -i - -binary-arch: - -binary: binary-indep -.PHONY: build clean binary binary-indep binary-arch diff --git a/debian.jaunty/control b/debian.jaunty/control deleted file mode 100644 index 75cddac..0000000 --- a/debian.jaunty/control +++ /dev/null @@ -1,34 +0,0 @@ -Source: logilab-common -Section: python -Priority: optional -Maintainer: Debian Python Modules Team <python-modules-team@lists.alioth.debian.org> -Uploaders: David Douard <david.douard@logilab.fr>, Fabrice Douchant <fabrice.douchant@logilab.fr>, Alexandre Fayolle <afayolle@debian.org>, Sandro Tosi <morph@debian.org>, Dorothe Sénéchal <dorothee.senechal@logilab.fr>, Adrien Di Mascio <Adrien.DiMascio@logilab.fr> -Build-Depends: debhelper (>= 5.0.38), python (>= 2.3.5-7) -Build-Depends-Indep: python-support, python-epydoc, graphviz -XS-Python-Version: all -Standards-Version: 3.8.1 -Homepage: http://www.logilab.org/project/logilab-common -Vcs-Svn: svn://svn.debian.org/svn/python-modules/packages/logilab-common/trunk/ -Vcs-Browser: http://svn.debian.org/viewsvn/python-modules/packages/logilab-common/trunk/ - -Package: python-logilab-common -Architecture: all -Provides: ${python:Provides} -Depends: ${python:Depends}, ${misc:Depends} -Recommends: python-egenix-mxdatetime -Conflicts: python-constraint ( <= 0.3.0-4), python-logilab-astng ( <= 0.16.0-1), pylint ( << 0.11.0-1), devtools ( <= 0.9.0-1), logilab-doctools ( <= 0.1.6-4), python-logilab-aspects ( <= 0.1.4-2), python2.3-logilab-common, python2.4-logilab-common -XB-Python-Version: ${python:Versions} -Description: useful miscellaneous modules used by Logilab projects - logilab-common is a collection of low-level Python packages and modules, - designed to ease: - . - * handling command line options and configuration files - * writing interactive command line tools - * manipulation files and character strings - * interfacing to OmniORB - * generating of SQL queries - * running unit tests - * manipulating tree structures - * accessing RDBMS (currently postgreSQL, MySQL and sqlite) - * generating text and HTML reports - * logging diff --git a/debian.jaunty/rules b/debian.jaunty/rules deleted file mode 100755 index f1f9362..0000000 --- a/debian.jaunty/rules +++ /dev/null @@ -1,81 +0,0 @@ -#!/usr/bin/make -f -# Sample debian/rules that uses debhelper. -# GNU copyright 1997 to 1999 by Joey Hess. -# -# adapted by Logilab for automatic generation by debianize -# (part of the devtools project, http://www.logilab.org/projects/devtools) -# -# Copyright (c) 2003-2008 LOGILAB S.A. (Paris, FRANCE). -# http://www.logilab.fr/ -- mailto:contact@logilab.fr - -# Uncomment this to turn on verbose mode. -#export DH_VERBOSE=1 - -build: build-stamp -build-stamp: - dh_testdir - - # python module build - NO_SETUPTOOLS=1 python setup.py -q build --build-purelib build/lib - - # run tests -ifeq (,$(findstring nocheck,$(DEB_BUILD_OPTIONS))) - # we need this hack because we have to import "logilab.common.pytest" - # but since it's a namespace package, we need to "simulate" it - touch $(CURDIR)/build/lib/logilab/__init__.py - -PYTHONPATH=$(CURDIR)/build/lib/ $(CURDIR)/build/scripts-2.6/pytest - rm -f $(CURDIR)/build/lib/logilab/__init__.py -endif - - # build doc - $(MAKE) -C doc - - touch build-stamp - -clean: - dh_testdir - dh_testroot - - # clean doc - $(MAKE) -C doc clean - - NO_SETUPTOOLS=1 python setup.py clean - - [ ! -d build ] || rm -rf build - - find . -name "*.pyc" -delete - - dh_clean build-stamp - -install: build - dh_testdir - dh_testroot - dh_clean -k - dh_installdirs - - NO_SETUPTOOLS=1 python setup.py -q install --no-compile --root=debian/python-logilab-common/ --install-headers=usr/include/ --install-scripts=usr/bin/ - # remove test directory - rm -rf debian/python-logilab-common/usr/lib/python*/site-packages/logilab/common/test - -# Build architecture-independent files here. -binary-indep: build install - dh_testdir - dh_testroot - dh_install -i - dh_pysupport -i - dh_installchangelogs -i ChangeLog - dh_installexamples -i - dh_installdocs -i README doc/apidoc/ - dh_installman -i - dh_link -i - dh_compress -i -X.py -X.ini -X.xml -Xtest/ -Xapidoc/ - dh_fixperms -i - dh_installdeb -i - dh_gencontrol -i - dh_md5sums -i - dh_builddeb -i - -binary-arch: - -binary: binary-indep -.PHONY: build clean binary binary-indep binary-arch diff --git a/debian.lenny/rules b/debian.lenny/rules deleted file mode 100755 index f468406..0000000 --- a/debian.lenny/rules +++ /dev/null @@ -1,73 +0,0 @@ -#!/usr/bin/make -f -# Sample debian/rules that uses debhelper. -# GNU copyright 1997 to 1999 by Joey Hess. -# -# adapted by Logilab for automatic generation by debianize -# (part of the devtools project, http://www.logilab.org/projects/devtools) -# -# Copyright (c) 2003-2008 LOGILAB S.A. (Paris, FRANCE). -# http://www.logilab.fr/ -- mailto:contact@logilab.fr - -# Uncomment this to turn on verbose mode. -#export DH_VERBOSE=1 - -build: build-stamp -build-stamp: - dh_testdir - - # python module build - NO_SETUPTOOLS=1 python setup.py -q build - - # build doc - $(MAKE) -C doc - - touch build-stamp - -clean: - dh_testdir - dh_testroot - - # clean doc - $(MAKE) -C doc clean - - NO_SETUPTOOLS=1 python setup.py clean - - [ ! -d build ] || rm -rf build - - find . -name "*.pyc" -delete - - dh_clean build-stamp - -install: build - dh_testdir - dh_testroot - dh_clean -k - dh_installdirs - - NO_SETUPTOOLS=1 python setup.py -q install --no-compile \ - --root=$(CURDIR)/debian/python-logilab-common/ \ - # remove test directory - rm -rf debian/python-logilab-common/usr/lib/python*/site-packages/logilab/common/test - -# Build architecture-independent files here. -binary-indep: build install - dh_testdir - dh_testroot - dh_install -i - dh_pysupport -i - dh_installchangelogs -i ChangeLog - dh_installexamples -i - dh_installdocs -i README doc/apidoc/ - dh_installman -i - dh_link -i - dh_compress -i -X.py -X.ini -X.xml -Xtest/ -Xapidoc/ - dh_fixperms -i - dh_installdeb -i - dh_gencontrol -i - dh_md5sums -i - dh_builddeb -i - -binary-arch: - -binary: binary-indep -.PHONY: build clean binary binary-indep binary-arch diff --git a/debian.stable/control b/debian.stable/control deleted file mode 100644 index 80fafda..0000000 --- a/debian.stable/control +++ /dev/null @@ -1,39 +0,0 @@ -Source: logilab-common -Section: python -Priority: optional -Maintainer: Debian Python Modules Team <python-modules-team@lists.alioth.debian.org> -Uploaders: David Douard <david.douard@logilab.fr>, - Alexandre Fayolle <afayolle@debian.org>, - Sandro Tosi <morph@debian.org>, - Dorothe Sénéchal <dorothee.senechal@logilab.fr>, - Adrien Di Mascio <Adrien.DiMascio@logilab.fr>, - Nicolas Chauvat <nicolas.chauvat@logilab.fr> -Build-Depends: debhelper (>= 5.0.37.2), python (>= 2.4) -Build-Depends-Indep: python-support, python-epydoc, graphviz -XS-Python-Version: all -Standards-Version: 3.8.1 -Homepage: http://www.logilab.org/project/logilab-common -Vcs-Svn: svn://svn.debian.org/svn/python-modules/packages/logilab-common/trunk/ -Vcs-Browser: http://svn.debian.org/viewsvn/python-modules/packages/logilab-common/trunk/ - -Package: python-logilab-common -Architecture: all -Provides: ${python:Provides} -Depends: ${python:Depends}, ${misc:Depends} -Recommends: python-egenix-mxdatetime -Conflicts: python-constraint ( <= 0.3.0-4), python-logilab-astng ( <= 0.16.0-1), pylint ( << 0.11.0-1), devtools ( <= 0.9.0-1), logilab-doctools ( <= 0.1.6-4), python-logilab-aspects ( <= 0.1.4-2), python2.3-logilab-common, python2.4-logilab-common -XB-Python-Version: ${python:Versions} -Description: useful miscellaneous modules used by Logilab projects - logilab-common is a collection of low-level Python packages and modules, - designed to ease: - . - * handling command line options and configuration files - * writing interactive command line tools - * manipulation files and character strings - * interfacing to OmniORB - * generating of SQL queries - * running unit tests - * manipulating tree structures - * accessing RDBMS (currently postgreSQL, MySQL and sqlite) - * generating text and HTML reports - * logging diff --git a/debian.stable/rules b/debian.stable/rules deleted file mode 100755 index 937f9df..0000000 --- a/debian.stable/rules +++ /dev/null @@ -1,82 +0,0 @@ -#!/usr/bin/make -f -# Sample debian/rules that uses debhelper. -# GNU copyright 1997 to 1999 by Joey Hess. -# -# adapted by Logilab for automatic generation by debianize -# (part of the devtools project, http://www.logilab.org/projects/devtools) -# -# Copyright (c) 2003-2008 LOGILAB S.A. (Paris, FRANCE). -# http://www.logilab.fr/ -- mailto:contact@logilab.fr - -# Uncomment this to turn on verbose mode. -#export DH_VERBOSE=1 - -build: build-stamp -build-stamp: - dh_testdir - - # python module build - NO_SETUPTOOLS=1 python setup.py -q build - - # run tests -ifeq (,$(findstring nocheck,$(DEB_BUILD_OPTIONS))) - # we need this hack because we have to import "logilab.common.pytest" - # but since it's a namespace package, we need to "simulate" it - touch $(CURDIR)/build/lib/logilab/__init__.py - -PYTHONPATH=$(CURDIR)/build/lib/ $(CURDIR)/build/scripts-2.5/pytest - rm -f $(CURDIR)/build/lib/logilab/__init__.py -endif - - # build doc - $(MAKE) -C doc - - touch build-stamp - -clean: - dh_testdir - dh_testroot - - # clean doc - $(MAKE) -C doc clean - - NO_SETUPTOOLS=1 python setup.py clean - - [ ! -d build ] || rm -rf build - - find . -name "*.pyc" -delete - - dh_clean build-stamp - -install: build - dh_testdir - dh_testroot - dh_clean -k - dh_installdirs - - NO_SETUPTOOLS=1 python setup.py -q install --no-compile \ - --root=$(CURDIR)/debian/python-logilab-common/ - # remove test directory - rm -rf debian/python-logilab-common/usr/lib/python*/site-packages/logilab/common/test - -# Build architecture-independent files here. -binary-indep: build install - dh_testdir - dh_testroot - dh_install -i - dh_pysupport -i - dh_installchangelogs -i ChangeLog - dh_installexamples -i - dh_installdocs -i README doc/apidoc/ - dh_installman -i - dh_link -i - dh_compress -i -X.py -X.ini -X.xml -Xtest/ -Xapidoc/ - dh_fixperms -i - dh_installdeb -i - dh_gencontrol -i - dh_md5sums -i - dh_builddeb -i - -binary-arch: - -binary: binary-indep -.PHONY: build clean binary binary-indep binary-arch diff --git a/debian/changelog b/debian/changelog index 8460636..e48a1e0 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +logilab-common (0.45.1-1) unstable; urgency=low + + * new upstream release + + -- Sylvain Thénault <sylvain.thenault@logilab.fr> Wed, 07 Oct 2009 10:32:20 +0200 + logilab-common (0.45.0-1) unstable; urgency=low * new upstream release diff --git a/debian/rules b/debian/rules index c0bd75a..e1acbe2 100755 --- a/debian/rules +++ b/debian/rules @@ -10,20 +10,21 @@ # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 +PY_VERSION:=$(shell pyversions -vd) build: build-stamp build-stamp: dh_testdir # python module build - NO_SETUPTOOLS=1 python setup.py -q build + NO_SETUPTOOLS=1 python setup.py -q build --build-purelib=build/lib # run tests ifeq (,$(findstring nocheck,$(DEB_BUILD_OPTIONS))) # we need this hack because we have to import "logilab.common.pytest" # but since it's a namespace package, we need to "simulate" it touch $(CURDIR)/build/lib/logilab/__init__.py - -PYTHONPATH=$(CURDIR)/build/lib/ $(CURDIR)/build/scripts-2.5/pytest + -PYTHONPATH=$(CURDIR)/build/lib/ $(CURDIR)/build/scripts-$(PY_VERSION)/pytest rm -f $(CURDIR)/build/lib/logilab/__init__.py endif @@ -53,7 +54,7 @@ install: build dh_clean -k dh_installdirs - NO_SETUPTOOLS=1 python setup.py -q install --no-compile \ + NO_SETUPTOOLS=1 python setup.py -q install --prefix=/usr --no-compile \ --root=$(CURDIR)/debian/python-logilab-common/ \ --install-layout=deb # remove test directory diff --git a/decorators.py b/decorators.py index d7768a0..f5278cc 100644 --- a/decorators.py +++ b/decorators.py @@ -81,7 +81,6 @@ def copy_cache(obj, funcname, cacheobj): except KeyError: pass - class wproperty(object): """Simple descriptor expecting to take a modifier function as first argument and looking for a _<function name> to retrieve the attribute. diff --git a/doc/makefile b/doc/makefile index ddea5d5..2484e55 100644 --- a/doc/makefile +++ b/doc/makefile @@ -3,7 +3,7 @@ all: epydoc epydoc: #epydoc -o apidoc --html -v --graph all --no-private --exclude="__pkginfo__" --exclude="setup" -n "Logilab's common library" ../ mkdir apidoc - epydoc -o apidoc --html -v --no-private --exclude="__pkginfo__" --exclude="setup" -n "Logilab's common library" ../ + epydoc -o apidoc --html -v --no-private --exclude="__pkginfo__" --exclude="setup" -n "Logilab's common library" $(shell dirname $(CURDIR))/build/lib/logilab/common clean: rm -rf apidoc @@ -12,6 +12,8 @@ __metaclass__ = type import os.path as osp import os +import subprocess +import sys import tempfile def escape(value): @@ -80,12 +82,15 @@ class DotBackend: storedir, basename, target = target_info_from_filename(outputfile) if target != "dot": pdot, dot_sourcepath = tempfile.mkstemp(".dot", name) + os.close(pdot) else: dot_sourcepath = osp.join(storedir, dotfile) else: target = 'png' pdot, dot_sourcepath = tempfile.mkstemp(".dot", name) ppng, outputfile = tempfile.mkstemp(".png", name) + os.close(pdot) + os.close(ppng) pdot = open(dot_sourcepath,'w') if isinstance(self.source, unicode): pdot.write(self.source.encode('UTF8')) @@ -93,8 +98,8 @@ class DotBackend: pdot.write(self.source) pdot.close() if target != 'dot': - os.system('%s -T%s %s -o%s' % (self.renderer, target, - dot_sourcepath, outputfile)) + subprocess.call('%s -T%s %s -o%s' % (self.renderer, target, + dot_sourcepath, outputfile), shell=True) os.unlink(dot_sourcepath) return outputfile @@ -0,0 +1,125 @@ +"""render a tree in HTML. + +:copyright: 2000-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr +:license: General Public License version 2 - http://www.gnu.org/licenses +""" +__docformat__ = "restructuredtext en" + + +def render_HTML_tree(tree, selected_node=None, render_node=None, caption=None): + """ + Generate a pure HTML representation of a tree given as an instance + of a logilab.common.tree.Node + + selected_node is the currently selected node (if any) which will + have its surrounding <div> have id="selected" (which default + to a bold border libe with the default CSS). + + render_node is a function that should take a Node content (Node.id) + as parameter and should return a string (what will be displayed + in the cell). + + Warning: proper rendering of the generated html code depends on html_tree.css + """ + tree_depth = tree.depth_down() + if render_node is None: + render_node = str + + # helper function that build a matrix from the tree, like: + # +------+-----------+-----------+ + # | root | child_1_1 | child_2_1 | + # | root | child_1_1 | child_2_2 | + # | root | child_1_2 | | + # | root | child_1_3 | child_2_3 | + # | root | child_1_3 | child_2_4 | + # +------+-----------+-----------+ + # from: + # root -+- child_1_1 -+- child_2_1 + # | | + # | +- child_2_2 + # +- child_1_2 + # | + # +- child1_3 -+- child_2_3 + # | + # +- child_2_2 + def build_matrix(path, matrix): + if path[-1].is_leaf(): + matrix.append(path[:]) + else: + for child in path[-1].children: + build_matrix(path[:] + [child], matrix) + + matrix = [] + build_matrix([tree], matrix) + + # make all lines in the matrix have the same number of columns + for line in matrix: + line.extend([None]*(tree_depth-len(line))) + for i in range(len(matrix)-1, 0, -1): + prev_line, line = matrix[i-1:i+1] + for j in range(len(line)): + if line[j] == prev_line[j]: + line[j] = None + + # We build the matrix of link types (between 2 cells on a line of the matrix) + # link types are : + link_types = {(True, True, True ): 1, # T + (False, False, True ): 2, # | + (False, True, True ): 3, # + (actually, vert. bar with horiz. bar on the right) + (False, True, False): 4, # L + (True, True, False): 5, # - + } + links = [] + for i, line in enumerate(matrix): + links.append([]) + for j in range(tree_depth-1): + cell_11 = line[j] is not None + cell_12 = line[j+1] is not None + cell_21 = line[j+1] is not None and line[j+1].next_sibling() is not None + link_type = link_types.get((cell_11, cell_12, cell_21), 0) + if link_type == 0 and i > 0 and links[i-1][j] in (1, 2, 3): + link_type = 2 + links[-1].append(link_type) + + + # We can now generate the HTML code for the <table> + s = u'<table class="tree">\n' + if caption: + s += '<caption>%s</caption>\n' % caption + + for i, link_line in enumerate(links): + line = matrix[i] + + s += '<tr>' + for j, link_cell in enumerate(link_line): + cell = line[j] + if cell: + if cell.id == selected_node: + s += '<td class="tree_cell" rowspan="2"><div class="selected tree_cell">%s</div></td>' % (render_node(cell.id)) + else: + s += '<td class="tree_cell" rowspan="2"><div class="tree_cell">%s</div></td>' % (render_node(cell.id)) + else: + s += '<td rowspan="2"> </td>' + s += '<td class="tree_cell_%d_1"> </td>' % link_cell + s += '<td class="tree_cell_%d_2"> </td>' % link_cell + + cell = line[-1] + if cell: + if cell.id == selected_node: + s += '<td class="tree_cell" rowspan="2"><div class="selected tree_cell">%s</div></td>' % (render_node(cell.id)) + else: + s += '<td class="tree_cell" rowspan="2"><div class="tree_cell">%s</div></td>' % (render_node(cell.id)) + else: + s += '<td rowspan="2"> </td>' + + s += '</tr>\n' + if link_line: + s += '<tr>' + for j, link_cell in enumerate(link_line): + s += '<td class="tree_cell_%d_3"> </td>' % link_cell + s += '<td class="tree_cell_%d_4"> </td>' % link_cell + s += '</tr>\n' + + s += '</table>' + return s diff --git a/logging_ext.py b/logging_ext.py index 4fce3a7..5a27d05 100644 --- a/logging_ext.py +++ b/logging_ext.py @@ -118,7 +118,7 @@ def init_log(debug=False, syslog=False, logthreshold=None, logfile=None, # setHandler method, so do it this way :$ logger.handlers = [handler] isatty = hasattr(sys.__stdout__, 'isatty') and sys.__stdout__.isatty() - if debug and isatty: + if debug and isatty and sys.platform != 'win32': fmt = ColorFormatter(logformat, logdateformat) def col_fact(record): if 'XXX' in record.message: diff --git a/optik_ext.py b/optik_ext.py index 7966d36..7305b7b 100644 --- a/optik_ext.py +++ b/optik_ext.py @@ -141,6 +141,7 @@ def check_file(option, opt, value): msg = "option %s: file %r does not exist" raise OptionValueError(msg % (opt, value)) +# XXX use python datetime def check_date(option, opt, value): """check a file value return the filepath @@ -169,11 +170,15 @@ def check_color(option, opt, value): def check_time(option, opt, value): from logilab.common.textutils import TIME_UNITS, apply_units - apply_units(value, TIME_UNITS) + if isinstance(value, (int, long, float)): + return value + return apply_units(value, TIME_UNITS) def check_bytes(option, opt, value): from logilab.common.textutils import BYTE_UNITS, apply_units - apply_units(value, BYTE_UNITS) + if instance(value, (int, long)): + return value + return apply_units(value, BYTE_UNITS) import types @@ -174,6 +174,14 @@ def install(**kwargs): sys.argv.remove('--force-manifest') except: pass + try: + if not USE_SETUPTOOLS: + from distutils import __version__ as distutils_version + if distutils_version.split('.') <= (2, 5, 1): + sys.argv.remove('--install-layout=deb') + print "W: remove '--install-layout=deb' option" + except: + pass if subpackage_of: package = subpackage_of + '.' + modname kwargs['package_dir'] = {package : '.'} @@ -383,12 +383,17 @@ class Table(object): raise KeyError("Column (%s) not found in table" % (col_id)) return self.get_column(col_index, distinct) - def get_columns(self): """Returns all the columns in the table """ return [self[:,index] for index in range(len(self.col_names))] + def get_column(self, col_index, distinct=False): + """get a column by index""" + col = [row[col_index] for row in self.data] + if distinct: + col = list(set(col)) + return col def apply_stylesheet(self, stylesheet): """Applies the stylesheet to this table diff --git a/tasksqueue.py b/tasksqueue.py index 008a625..0165340 100644 --- a/tasksqueue.py +++ b/tasksqueue.py @@ -13,6 +13,12 @@ LOW = 0 MEDIUM = 10 HIGH = 100 +REVERSE_PRIORITY = { + 0: 'LOW', + 10: 'MEDIUM', + 100: 'HIGH' + } + class PrioritizedTasksQueue(Queue): diff --git a/test/unittest_configuration.py b/test/unittest_configuration.py index c68f0d3..828309f 100644 --- a/test/unittest_configuration.py +++ b/test/unittest_configuration.py @@ -4,7 +4,8 @@ from cStringIO import StringIO from sys import version_info from logilab.common.testlib import TestCase, unittest_main -from logilab.common.configuration import Configuration, OptionValueError, \ +from logilab.common.optik_ext import OptionValueError +from logilab.common.configuration import Configuration, \ OptionsManagerMixIn, OptionsProviderMixIn, Method, read_old_config options = [('dothis', {'type':'yn', 'action': 'store', 'default': True, 'metavar': '<y or n>'}), diff --git a/test/unittest_html.py b/test/unittest_html.py new file mode 100644 index 0000000..f83ce51 --- /dev/null +++ b/test/unittest_html.py @@ -0,0 +1,59 @@ +# -*- coding: utf-8 -*- +"""unittests for logilab.common.html + +:organization: Logilab +:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2. +:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr +:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses +""" + +__docformat__ = "restructuredtext en" + +from logilab.common.testlib import TestCase, unittest_main +from logilab.common.tree import Node + +from logilab.common import html + +tree = ('root', ( + ('child_1_1', ( + ('child_2_1', ()), ('child_2_2', ( + ('child_3_1', ()), + ('child_3_2', ()), + ('child_3_3', ()), + )))), + ('child_1_2', (('child_2_3', ()),)))) + +generated_html = """\ +<table class="tree"> +<tr><td class="tree_cell" rowspan="2"><div class="tree_cell">root</div></td><td class="tree_cell_1_1"> </td><td class="tree_cell_1_2"> </td><td class="tree_cell" rowspan="2"><div class="tree_cell">child_1_1</div></td><td class="tree_cell_1_1"> </td><td class="tree_cell_1_2"> </td><td class="tree_cell" rowspan="2"><div class="tree_cell">child_2_1</div></td><td class="tree_cell_0_1"> </td><td class="tree_cell_0_2"> </td><td rowspan="2"> </td></tr> +<tr><td class="tree_cell_1_3"> </td><td class="tree_cell_1_4"> </td><td class="tree_cell_1_3"> </td><td class="tree_cell_1_4"> </td><td class="tree_cell_0_3"> </td><td class="tree_cell_0_4"> </td></tr> +<tr><td rowspan="2"> </td><td class="tree_cell_2_1"> </td><td class="tree_cell_2_2"> </td><td rowspan="2"> </td><td class="tree_cell_4_1"> </td><td class="tree_cell_4_2"> </td><td class="tree_cell" rowspan="2"><div class="selected tree_cell">child_2_2</div></td><td class="tree_cell_1_1"> </td><td class="tree_cell_1_2"> </td><td class="tree_cell" rowspan="2"><div class="tree_cell">child_3_1</div></td></tr> +<tr><td class="tree_cell_2_3"> </td><td class="tree_cell_2_4"> </td><td class="tree_cell_4_3"> </td><td class="tree_cell_4_4"> </td><td class="tree_cell_1_3"> </td><td class="tree_cell_1_4"> </td></tr> +<tr><td rowspan="2"> </td><td class="tree_cell_2_1"> </td><td class="tree_cell_2_2"> </td><td rowspan="2"> </td><td class="tree_cell_0_1"> </td><td class="tree_cell_0_2"> </td><td rowspan="2"> </td><td class="tree_cell_3_1"> </td><td class="tree_cell_3_2"> </td><td class="tree_cell" rowspan="2"><div class="tree_cell">child_3_2</div></td></tr> +<tr><td class="tree_cell_2_3"> </td><td class="tree_cell_2_4"> </td><td class="tree_cell_0_3"> </td><td class="tree_cell_0_4"> </td><td class="tree_cell_3_3"> </td><td class="tree_cell_3_4"> </td></tr> +<tr><td rowspan="2"> </td><td class="tree_cell_2_1"> </td><td class="tree_cell_2_2"> </td><td rowspan="2"> </td><td class="tree_cell_0_1"> </td><td class="tree_cell_0_2"> </td><td rowspan="2"> </td><td class="tree_cell_4_1"> </td><td class="tree_cell_4_2"> </td><td class="tree_cell" rowspan="2"><div class="tree_cell">child_3_3</div></td></tr> +<tr><td class="tree_cell_2_3"> </td><td class="tree_cell_2_4"> </td><td class="tree_cell_0_3"> </td><td class="tree_cell_0_4"> </td><td class="tree_cell_4_3"> </td><td class="tree_cell_4_4"> </td></tr> +<tr><td rowspan="2"> </td><td class="tree_cell_4_1"> </td><td class="tree_cell_4_2"> </td><td class="tree_cell" rowspan="2"><div class="tree_cell">child_1_2</div></td><td class="tree_cell_5_1"> </td><td class="tree_cell_5_2"> </td><td class="tree_cell" rowspan="2"><div class="tree_cell">child_2_3</div></td><td class="tree_cell_0_1"> </td><td class="tree_cell_0_2"> </td><td rowspan="2"> </td></tr> +<tr><td class="tree_cell_4_3"> </td><td class="tree_cell_4_4"> </td><td class="tree_cell_5_3"> </td><td class="tree_cell_5_4"> </td><td class="tree_cell_0_3"> </td><td class="tree_cell_0_4"> </td></tr> +</table>\ +""" + +def make_tree(tupletree): + n = Node(tupletree[0]) + for child in tupletree[1]: + n.append(make_tree(child)) + return n + +class UIlibHTMLGenerationTC(TestCase): + """ a basic tree node, caracterised by an id""" + def setUp(self): + """ called before each test from this class """ + self.o = make_tree(tree) + + def test_generated_html(self): + s = html.render_HTML_tree(self.o, selected_node="child_2_2") + self.assertTextEqual(s, generated_html) + + +if __name__ == '__main__': + unittest_main() diff --git a/test/unittest_testlib.py b/test/unittest_testlib.py index eacb5b8..1bf5123 100644 --- a/test/unittest_testlib.py +++ b/test/unittest_testlib.py @@ -641,12 +641,14 @@ class DecoratorTC(TestCase): @with_tempdir def createfile(list): - tempfile.mkstemp() - tempfile.mkstemp() + fd1, fn1 = tempfile.mkstemp() + fd2, fn2 = tempfile.mkstemp() dir = tempfile.mkdtemp() - tempfile.mkstemp(dir=dir) + fd3, fn3 = tempfile.mkstemp(dir=dir) tempfile.mkdtemp() list.append(True) + for fd in (fd1, fd2, fd3): + os.close(fd) self.assertFalse(witness) createfile(witness) @@ -672,11 +674,13 @@ class DecoratorTC(TestCase): @with_tempdir def createfile(): - tempfile.mkstemp() - tempfile.mkstemp() + fd1, fn1 = tempfile.mkstemp() + fd2, fn2 = tempfile.mkstemp() dir = tempfile.mkdtemp() - tempfile.mkstemp(dir=dir) + fd3, fn3 = tempfile.mkstemp(dir=dir) tempfile.mkdtemp() + for fd in (fd1, fd2, fd3): + os.close(fd) raise WitnessException() self.assertRaises(WitnessException, createfile) diff --git a/textutils.py b/textutils.py index c25d8c8..3c40dda 100644 --- a/textutils.py +++ b/textutils.py @@ -30,6 +30,7 @@ unquote, colorize_ansi """ __docformat__ = "restructuredtext en" +import sys import re from unicodedata import normalize as _uninormalize try: @@ -279,8 +280,6 @@ def apply_units( string, units, inter=None, final=float, blank_reg=_BLANK_RE, """ if inter is None: inter = final - - string = _BLANK_RE.sub('',string) values = [] for match in value_reg.finditer(string): @@ -292,7 +291,6 @@ def apply_units( string, units, inter=None, final=float, blank_reg=_BLANK_RE, if unit is not None: value *= units[unit] values.append(value) - return final(sum(values)) _LINE_RGX = re.compile('\r\n|\r+|\n') @@ -434,3 +432,20 @@ def colorize_ansi(msg, color=None, style=None): return '%s%s%s' % (escape_code, msg, ANSI_RESET) return msg +DIFF_STYLE = {'separator': 'cyan', 'remove': 'red', 'add': 'green'} + +def diff_colorize_ansi(lines, out=sys.stdout, style=DIFF_STYLE): + for line in lines: + if line[:4] in ('--- ', '+++ '): + out.write(colorize_ansi(line, style['separator'])) + elif line[0] == '-': + out.write(colorize_ansi(line, style['remove'])) + elif line[0] == '+': + out.write(colorize_ansi(line, style['add'])) + elif line[:4] == '--- ': + out.write(colorize_ansi(line, style['separator'])) + elif line[:4] == '+++ ': + out.write(colorize_ansi(line, style['separator'])) + else: + out.write(line) + |