summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog5
-rw-r--r--__pkginfo__.py2
-rw-r--r--adbh.py4
-rw-r--r--clcommands.py4
-rw-r--r--configuration.py84
-rw-r--r--debian.hardy/control34
-rwxr-xr-xdebian.hardy/rules78
-rw-r--r--debian.intrepid/control39
-rwxr-xr-xdebian.intrepid/rules82
-rw-r--r--debian.jaunty/control34
-rwxr-xr-xdebian.jaunty/rules81
-rwxr-xr-xdebian.lenny/rules73
-rw-r--r--debian.stable/control39
-rwxr-xr-xdebian.stable/rules82
-rw-r--r--debian/changelog6
-rwxr-xr-xdebian/rules7
-rw-r--r--decorators.py1
-rw-r--r--doc/makefile2
-rw-r--r--graph.py9
-rw-r--r--html.py125
-rw-r--r--logging_ext.py2
-rw-r--r--optik_ext.py9
-rw-r--r--setup.py8
-rw-r--r--table.py7
-rw-r--r--tasksqueue.py6
-rw-r--r--test/unittest_configuration.py3
-rw-r--r--test/unittest_html.py59
-rw-r--r--test/unittest_testlib.py16
-rw-r--r--textutils.py21
29 files changed, 319 insertions, 603 deletions
diff --git a/ChangeLog b/ChangeLog
index 2a65210..2514e89 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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'
diff --git a/adbh.py b/adbh.py
index 0636bbe..b0047b7 100644
--- a/adbh.py
+++ b/adbh.py
@@ -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
diff --git a/graph.py b/graph.py
index df5d838..c81887e 100644
--- a/graph.py
+++ b/graph.py
@@ -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
diff --git a/html.py b/html.py
new file mode 100644
index 0000000..40c5bd1
--- /dev/null
+++ b/html.py
@@ -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">&nbsp;</td>'
+ s += '<td class="tree_cell_%d_1">&nbsp;</td>' % link_cell
+ s += '<td class="tree_cell_%d_2">&nbsp;</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">&nbsp;</td>'
+
+ s += '</tr>\n'
+ if link_line:
+ s += '<tr>'
+ for j, link_cell in enumerate(link_line):
+ s += '<td class="tree_cell_%d_3">&nbsp;</td>' % link_cell
+ s += '<td class="tree_cell_%d_4">&nbsp;</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
diff --git a/setup.py b/setup.py
index b159394..e86391d 100644
--- a/setup.py
+++ b/setup.py
@@ -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 : '.'}
diff --git a/table.py b/table.py
index df1e23f..865087f 100644
--- a/table.py
+++ b/table.py
@@ -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">&nbsp;</td><td class="tree_cell_1_2">&nbsp;</td><td class="tree_cell" rowspan="2"><div class="tree_cell">child_1_1</div></td><td class="tree_cell_1_1">&nbsp;</td><td class="tree_cell_1_2">&nbsp;</td><td class="tree_cell" rowspan="2"><div class="tree_cell">child_2_1</div></td><td class="tree_cell_0_1">&nbsp;</td><td class="tree_cell_0_2">&nbsp;</td><td rowspan="2">&nbsp;</td></tr>
+<tr><td class="tree_cell_1_3">&nbsp;</td><td class="tree_cell_1_4">&nbsp;</td><td class="tree_cell_1_3">&nbsp;</td><td class="tree_cell_1_4">&nbsp;</td><td class="tree_cell_0_3">&nbsp;</td><td class="tree_cell_0_4">&nbsp;</td></tr>
+<tr><td rowspan="2">&nbsp;</td><td class="tree_cell_2_1">&nbsp;</td><td class="tree_cell_2_2">&nbsp;</td><td rowspan="2">&nbsp;</td><td class="tree_cell_4_1">&nbsp;</td><td class="tree_cell_4_2">&nbsp;</td><td class="tree_cell" rowspan="2"><div class="selected tree_cell">child_2_2</div></td><td class="tree_cell_1_1">&nbsp;</td><td class="tree_cell_1_2">&nbsp;</td><td class="tree_cell" rowspan="2"><div class="tree_cell">child_3_1</div></td></tr>
+<tr><td class="tree_cell_2_3">&nbsp;</td><td class="tree_cell_2_4">&nbsp;</td><td class="tree_cell_4_3">&nbsp;</td><td class="tree_cell_4_4">&nbsp;</td><td class="tree_cell_1_3">&nbsp;</td><td class="tree_cell_1_4">&nbsp;</td></tr>
+<tr><td rowspan="2">&nbsp;</td><td class="tree_cell_2_1">&nbsp;</td><td class="tree_cell_2_2">&nbsp;</td><td rowspan="2">&nbsp;</td><td class="tree_cell_0_1">&nbsp;</td><td class="tree_cell_0_2">&nbsp;</td><td rowspan="2">&nbsp;</td><td class="tree_cell_3_1">&nbsp;</td><td class="tree_cell_3_2">&nbsp;</td><td class="tree_cell" rowspan="2"><div class="tree_cell">child_3_2</div></td></tr>
+<tr><td class="tree_cell_2_3">&nbsp;</td><td class="tree_cell_2_4">&nbsp;</td><td class="tree_cell_0_3">&nbsp;</td><td class="tree_cell_0_4">&nbsp;</td><td class="tree_cell_3_3">&nbsp;</td><td class="tree_cell_3_4">&nbsp;</td></tr>
+<tr><td rowspan="2">&nbsp;</td><td class="tree_cell_2_1">&nbsp;</td><td class="tree_cell_2_2">&nbsp;</td><td rowspan="2">&nbsp;</td><td class="tree_cell_0_1">&nbsp;</td><td class="tree_cell_0_2">&nbsp;</td><td rowspan="2">&nbsp;</td><td class="tree_cell_4_1">&nbsp;</td><td class="tree_cell_4_2">&nbsp;</td><td class="tree_cell" rowspan="2"><div class="tree_cell">child_3_3</div></td></tr>
+<tr><td class="tree_cell_2_3">&nbsp;</td><td class="tree_cell_2_4">&nbsp;</td><td class="tree_cell_0_3">&nbsp;</td><td class="tree_cell_0_4">&nbsp;</td><td class="tree_cell_4_3">&nbsp;</td><td class="tree_cell_4_4">&nbsp;</td></tr>
+<tr><td rowspan="2">&nbsp;</td><td class="tree_cell_4_1">&nbsp;</td><td class="tree_cell_4_2">&nbsp;</td><td class="tree_cell" rowspan="2"><div class="tree_cell">child_1_2</div></td><td class="tree_cell_5_1">&nbsp;</td><td class="tree_cell_5_2">&nbsp;</td><td class="tree_cell" rowspan="2"><div class="tree_cell">child_2_3</div></td><td class="tree_cell_0_1">&nbsp;</td><td class="tree_cell_0_2">&nbsp;</td><td rowspan="2">&nbsp;</td></tr>
+<tr><td class="tree_cell_4_3">&nbsp;</td><td class="tree_cell_4_4">&nbsp;</td><td class="tree_cell_5_3">&nbsp;</td><td class="tree_cell_5_4">&nbsp;</td><td class="tree_cell_0_3">&nbsp;</td><td class="tree_cell_0_4">&nbsp;</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)
+