summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexandre Fayolle <alexandre.fayolle@logilab.fr>2008-05-07 13:46:01 +0200
committerAlexandre Fayolle <alexandre.fayolle@logilab.fr>2008-05-07 13:46:01 +0200
commit0b84587cfd9a8e042ada3f32e2eac903601d467a (patch)
tree5079d027d524916630737206a409dea92fe6394b
parente26e5f5b7f10de0408bbf1fecf0d96701811f742 (diff)
parent904d11f3f7e8cc158c97eae1a6b6399821dd69be (diff)
downloadlogilab-common-0b84587cfd9a8e042ada3f32e2eac903601d467a.tar.gz
merge
-rw-r--r--README78
-rw-r--r--astutils.py2
-rw-r--r--cache.py4
-rw-r--r--clcommands.py1
-rw-r--r--compat.py4
-rw-r--r--logger.py2
-rw-r--r--optparser.py2
-rw-r--r--pdf_ext.py3
-rw-r--r--test/unittest_cache.py59
-rw-r--r--testlib.py42
10 files changed, 140 insertions, 57 deletions
diff --git a/README b/README
index 180a23b..0706c1e 100644
--- a/README
+++ b/README
@@ -30,26 +30,35 @@ Provided modules
Here is a brief description of the available modules :
+* adbh.py:
+ helper functions for using database advanced. Supported RDBMS
+include PostgreSQL, MySQL and sqlite. See also db.py.
+
* astutils:
- Some utilities function to manipulate Python's AST.
+ Deprecated module. Use logilab.astng.
* bind.py :
+ Deprecated module.
Provides a way to optimize globals in certain functions by binding
their names to values provided in a dictionnary.
* cache.py :
A cache implementation with a least recently used algorithm.
+* clcommands.py:
+ helper functions for command line programs handling different
+ subcommands
+
* cli.py :
- Command line interface helper classes.
+ Command line interface helper classes (for interactive programs
+ using the command line)
* compat.py:
Transparent compatibility layer between different python version
- (actually 2.2 vs 2.3 for now)
* configuration.py :
Two mix-in classes to handle configuration from both command line
- (using optik) and configuration file.
+ (using optik/optparse) and configuration file.
* corbautils.py:
Usefull functions for use with the OmniORB CORBA library.
@@ -57,50 +66,95 @@ Here is a brief description of the available modules :
* daemon.py :
A daemon mix-in class.
+* date.py:
+ date manipulation helper functions
+
* db.py :
- A generic method to get a database connection.
+ A generic method to get a database connection. See also adbh.py.
-* html.py :
- Return an html formatted traceback from python exception infos.
+* debugger.py:
+ pdb customization
+
+* decorators.py:
+ useful decorators (cached, timed...)
+
+* deprecation.py:
+ mark functions / classes as deprecated or moved
* fileutils.py :
Some file / file path manipulation utilities.
+* graph.py:
+ graph manipulations, dot file generation
+
+* html.py :
+ Deprecated module
+ Return an html formatted traceback from python exception infos.
+
* interface.py
Bases class for interfaces.
* logger.py :
+ Deprecated module : use logging from stdlib.
Define a logger interface and two concrete loggers : one which prints
everything on stdout, the other using syslog.
+* logging_ext.py:
+ extensions to stdlib's logging module
+
+* logservice.py:
+ Deprecated module. Use logging from stdlib.
+
* modutils.py :
Module manipulation utilities.
+* monclient.py:
+ Deprecated module
+
+* monserver.py:
+ Deprecated module
+
* optik_ext :
Add an abstraction level to transparently import optik classes from
optparse (python >= 2.3) or the optik package. It also defines two
- new option types : regexp and csv.
+ new option types (regexp, csv, color, date...)
+
+* optparser.py:
+ extend optparse's OptionParser to support commands
* patricia.py :
A Python implementation of PATRICIA trie (Practical Algorithm to
Retrieve Information Coded in Alphanumeric).
-* shellutils:
+* pdf_ext.py:
+ pdf and fdf file manipulations, with pdftk.
+
+* pytest.py:
+ unittest runner. See testlib
+
+* shellutils.py:
Some utilities to replace shell scripts with python scripts.
* sqlgen.py :
Helper class to generate SQL strings to use with python's DB-API.
+* table.py:
+ manage tabular data (supports column and row names, sorting, grouping...
+
* testlib.py :
Generic tests execution methods.
* textutils.py:
- Some text manipulation utilities.
+ Some text manipulation utilities (ansi colorization, line wrapping,
+ rest support...)
* tree.py :
Base class to represent tree structure, and some others to make it
works with the visitor implementation (see below).
+* umessage.py:
+ unicode email support
+
* ureports:
Provides a way to create simple reports using python objects
without care of the final formatting. Some formatters text and html
@@ -118,12 +172,14 @@ Here is a brief description of the available modules :
distutils syntax. Note that you can use this to install files that
are not twisted plugins in any package directory of your application.
+* xmlrpcutils.py:
+ Auth support for XML RPC
Comments, support, bug reports
------------------------------
Use the python-projects@logilab.org mailing list. Since we do not have
publicly available bug tracker yet, bug reports should be emailed
-there too.
+there too.
You can subscribe to this mailing list at
http://www.logilab.org/mailinglists/python_projects/mailinglist_register_form
diff --git a/astutils.py b/astutils.py
index 1a14106..a6e1c32 100644
--- a/astutils.py
+++ b/astutils.py
@@ -18,7 +18,7 @@
from warnings import warn
warn('this module has been moved into logilab.astng and will disappear from \
-logilab.common in a near release',
+logilab.common in a future release',
DeprecationWarning, stacklevel=1)
__author__ = u"Sylvain Thenault"
diff --git a/cache.py b/cache.py
index e41a684..50647ec 100644
--- a/cache.py
+++ b/cache.py
@@ -46,7 +46,7 @@ class Cache:
if not self._usage:
self._usage.append(key)
- if self._usage[-1] != key:
+ elif self._usage[-1] != key:
try:
self._usage.remove(key)
except ValueError:
@@ -57,6 +57,8 @@ class Cache:
del self.data[self._usage[0]]
del self._usage[0]
self._usage.append(key)
+ else:
+ pass # key is already the most recently used key
def __getitem__(self, key):
diff --git a/clcommands.py b/clcommands.py
index d3ebbe6..8b1adec 100644
--- a/clcommands.py
+++ b/clcommands.py
@@ -20,6 +20,7 @@ command'specific
:contact: http://www.logilab.fr/ -- mailto:python-projects@logilab.org
"""
+# XXX : merge with optparser ?
import sys
from os.path import basename
diff --git a/compat.py b/compat.py
index d9a11f5..7e98330 100644
--- a/compat.py
+++ b/compat.py
@@ -15,8 +15,8 @@
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-"""some wrapper around tools introduced into python 2.3, making them available
-in python 2.2
+"""some wrapper around some builtins introduced in python 2.3, 2.4 and
+2.5, making them available in for earlier versions of python.
"""
from __future__ import generators
diff --git a/logger.py b/logger.py
index 69ec06f..3b190fa 100644
--- a/logger.py
+++ b/logger.py
@@ -18,7 +18,7 @@ everything on stdout, the other using syslog.
"""
from warnings import warn
-warn('logger module is deprecated and will disappear in a near release. \
+warn('logger module is deprecated and will disappear in a future release. \
use logging module instead.',
DeprecationWarning, stacklevel=1)
diff --git a/optparser.py b/optparser.py
index 6ae42a9..3461ce5 100644
--- a/optparser.py
+++ b/optparser.py
@@ -28,7 +28,7 @@ Example:
With mymod.build that defines two functions run and add_options
"""
-# XXX merge with optik_ext ?
+# XXX merge with optik_ext ? merge with clcommands ?
import sys
import optparse
diff --git a/pdf_ext.py b/pdf_ext.py
index c8ef64e..ed89808 100644
--- a/pdf_ext.py
+++ b/pdf_ext.py
@@ -23,6 +23,9 @@ to merge fdf and pdf:
without flatten, one could further edit the resulting form.
with flatten, everything is turned into text.
"""
+# XXX seems very unix specific
+# TODO: check availability of pdftk at import
+
import os
diff --git a/test/unittest_cache.py b/test/unittest_cache.py
index c2a5b29..dfc9315 100644
--- a/test/unittest_cache.py
+++ b/test/unittest_cache.py
@@ -11,28 +11,36 @@ class CacheTestCase(TestCase):
def test_setitem1(self):
"""Checks that the setitem method works"""
self.cache[1] = 'foo'
- self.assert_(self.cache.data[1] == 'foo',"1 : 'foo' is not in cache.data")
- self.assert_(len(self.cache._usage) == 1, "lenght of usage list is not 1")
- self.assert_(self.cache._usage[-1] == 1, '1 is not the most recently used key')
- self.assert_(self.cache._usage.sort() == self.cache.data.keys().sort(), "usage list and data keys are different")
+ self.assertEqual(self.cache.data[1], 'foo', "1:foo is not in cache")
+ self.assertEqual(len(self.cache._usage), 1)
+ self.assertEqual(self.cache._usage[-1], 1,
+ '1 is not the most recently used key')
+ self.assertSetEqual(self.cache._usage,
+ self.cache.data.keys(),
+ "usage list and data keys are different")
def test_setitem2(self):
"""Checks that the setitem method works for multiple items"""
self.cache[1] = 'foo'
self.cache[2] = 'bar'
- self.assert_(self.cache.data[2] == 'bar',"2 : 'bar' is not in cache.data")
- self.assert_(len(self.cache._usage) == 2, "lenght of usage list is not 2")
- self.assert_(self.cache._usage[-1] == 2, '1 is not the most recently used key')
- self.assert_(self.cache._usage.sort() == self.cache.data.keys().sort(), "usage list and data keys are different")
+ self.assertEqual(self.cache.data[2], 'bar',
+ "2 : 'bar' is not in cache.data")
+ self.assertEqual(len(self.cache._usage), 2,
+ "lenght of usage list is not 2")
+ self.assertEqual(self.cache._usage[-1], 2,
+ '1 is not the most recently used key')
+ self.assertSetEqual(self.cache._usage,
+ self.cache.data.keys())# usage list and data keys are different
def test_setitem3(self):
"""Checks that the setitem method works when replacing an element in the cache"""
self.cache[1] = 'foo'
self.cache[1] = 'bar'
- self.assert_(self.cache.data[1] == 'bar',"1 : 'bar' is not in cache.data")
- self.assert_(len(self.cache._usage) == 1, "lenght of usage list is not 1")
- self.assert_(self.cache._usage[-1] == 1, '1 is not the most recently used key')
- self.assert_(self.cache._usage.sort() == self.cache.data.keys().sort(), "usage list and data keys are different")
+ self.assertEqual(self.cache.data[1], 'bar', "1 : 'bar' is not in cache.data")
+ self.assertEqual(len(self.cache._usage), 1, "lenght of usage list is not 1")
+ self.assertEqual(self.cache._usage[-1], 1, '1 is not the most recently used key')
+ self.assertSetEqual(self.cache._usage,
+ self.cache.data.keys())# usage list and data keys are different
def test_recycling1(self):
"""Checks the removal of old elements"""
@@ -42,11 +50,14 @@ class CacheTestCase(TestCase):
self.cache[4] = 'foz'
self.cache[5] = 'fuz'
self.cache[6] = 'spam'
- self.assert_(not self.cache.data.has_key(1), 'key 1 has not been suppressed from the cache dictionnary')
- self.assert_(1 not in self.cache._usage, 'key 1 has not been suppressed from the cache LRU list')
- self.assert_(len(self.cache._usage) == 5, "lenght of usage list is not 5")
- self.assert_(self.cache._usage[-1] == 6, '6 is not the most recently used key')
- self.assert_(self.cache._usage.sort() == self.cache.data.keys().sort(), "usage list and data keys are different")
+ self.assert_(not self.cache.data.has_key(1),
+ 'key 1 has not been suppressed from the cache dictionnary')
+ self.assert_(1 not in self.cache._usage,
+ 'key 1 has not been suppressed from the cache LRU list')
+ self.assertEqual(len(self.cache._usage), 5, "lenght of usage list is not 5")
+ self.assertEqual(self.cache._usage[-1], 6, '6 is not the most recently used key')
+ self.assertSetEqual(self.cache._usage,
+ self.cache.data.keys())# usage list and data keys are different
def test_recycling2(self):
"""Checks that accessed elements get in the front of the list"""
@@ -55,9 +66,10 @@ class CacheTestCase(TestCase):
self.cache[3] = 'baz'
self.cache[4] = 'foz'
a = self.cache[1]
- self.assert_(a == 'foo')
- self.assert_(self.cache._usage[-1] == 1, '1 is not the most recently used key')
- self.assert_(self.cache._usage.sort() == self.cache.data.keys().sort(), "usage list and data keys are different")
+ self.assertEqual(a, 'foo')
+ self.assertEqual(self.cache._usage[-1], 1, '1 is not the most recently used key')
+ self.assertSetEqual(self.cache._usage,
+ self.cache.data.keys())# usage list and data keys are different
def test_delitem(self):
"""Checks that elements are removed from both element dict and element
@@ -67,7 +79,8 @@ class CacheTestCase(TestCase):
del self.cache['foo']
self.assert_('foo' not in self.cache.data.keys(),"Element 'foo' was not removed cache dictionnary")
self.assert_('foo' not in self.cache._usage,"Element 'foo' was not removed usage list")
- self.assert_(self.cache._usage.sort() == self.cache.data.keys().sort(), "usage list and data keys are different")
+ self.assertSetEqual(self.cache._usage,
+ self.cache.data.keys())# usage list and data keys are different
def test_nullsize(self):
@@ -75,9 +88,9 @@ class CacheTestCase(TestCase):
"""
null_cache = Cache(0)
null_cache['foo'] = 'bar'
- self.assert_(null_cache.size == 0, 'Cache size should be O, not %d' % \
+ self.assertEqual(null_cache.size, 0, 'Cache size should be O, not %d' % \
null_cache.size)
- self.assert_(len(null_cache) == 0, 'Cache should be empty !')
+ self.assertEqual(len(null_cache), 0, 'Cache should be empty !')
# Assert null_cache['foo'] raises a KeyError
self.assertRaises(KeyError, null_cache.__getitem__, 'foo')
# Deleting element should not raise error
diff --git a/testlib.py b/testlib.py
index c4d53f1..467d7fe 100644
--- a/testlib.py
+++ b/testlib.py
@@ -427,7 +427,6 @@ class SkipAwareTextTestRunner(unittest.TextTestRunner):
self.cvg = cvg
self.test_pattern = test_pattern
self.skipped_patterns = skipped_patterns
- self.options = options
def _this_is_skipped(self, testedname):
return any([(pat in testedname) for pat in self.skipped_patterns])
@@ -884,7 +883,6 @@ class TestCase(unittest.TestCase):
self._out = []
self._err = []
self._current_test_descr = None
- self._options_ = None
def datadir(cls):
"""helper attribute holding the standard test's data directory
@@ -969,7 +967,6 @@ class TestCase(unittest.TestCase):
def _get_test_method(self):
return getattr(self, self.__testMethodName)
-
def optval(self, option, default=None):
return getattr(self._options_, option, default)
@@ -1100,19 +1097,25 @@ class TestCase(unittest.TestCase):
self.fail('\n'.join(msgs))
assertDictEqual = assertDictEquals
- def assertSetEquals(self, got, expected):
+ def assertSetEquals(self, got, expected, msg=None):
"""compares two iterables and shows difference between both"""
got, expected = list(got), list(expected)
- self.assertEquals(len(got), len(expected), '%s != %s' % (got, expected))
+ if msg is None:
+ msg1 = '%s != %s' % (got, expected)
+ else:
+ msg1 = msg
+ self.assertEquals(len(got), len(expected), msg1)
got, expected = set(got), set(expected)
if got != expected:
missing = expected - got
unexpected = got - expected
- self.fail('\tunexepected: %s\n\tmissing: %s' % (unexpected,
- missing))
+ if msg is None:
+ msg = '\tunexepected: %s\n\tmissing: %s' % (unexpected,
+ missing)
+ self.fail(msg)
assertSetEqual = assertSetEquals
- def assertListEquals(self, l1, l2):
+ def assertListEquals(self, l1, l2, msg=None):
"""compares two lists
If the two list differ, the first difference is shown in the error
@@ -1128,32 +1131,37 @@ class TestCase(unittest.TestCase):
self.fail('%r != %r for index %d' % (_l1[0], value, i))
del _l1[0]
except IndexError:
- msg = 'l1 has only %d elements, not %s (at least %r missing)'
- self.fail(msg % (i, len(l2), value))
+ if msg is None:
+ msg = 'l1 has only %d elements, not %s (at least %r missing)'% (i, len(l2), value)
+ self.fail(msg)
if _l1:
- self.fail('l2 is lacking %r' % _l1)
+ if msg is None:
+ msg = 'l2 is lacking %r' % _l1
+ self.fail(msg)
assertListEqual = assertListEquals
- def assertLinesEquals(self, l1, l2):
+ def assertLinesEquals(self, l1, l2, msg=None):
"""assert list of lines are equal"""
- self.assertListEquals(l1.splitlines(), l2.splitlines())
+ self.assertListEquals(l1.splitlines(), l2.splitlines(), msg)
assertLineEqual = assertLinesEquals
- def assertXMLWellFormed(self, stream):
+ def assertXMLWellFormed(self, stream, msg=None):
"""asserts the XML stream is well-formed (no DTD conformance check)"""
from xml.sax import make_parser, SAXParseException
parser = make_parser()
try:
parser.parse(stream)
except SAXParseException:
- self.fail('XML stream not well formed')
+ if msg is None:
+ msg = 'XML stream not well formed'
+ self.fail(msg)
assertXMLValid = deprecated_function(assertXMLWellFormed,
'assertXMLValid renamed to more precise assertXMLWellFormed')
- def assertXMLStringWellFormed(self, xml_string):
+ def assertXMLStringWellFormed(self, xml_string, msg=None):
"""asserts the XML string is well-formed (no DTD conformance check)"""
stream = StringIO(xml_string)
- self.assertXMLWellFormed(stream)
+ self.assertXMLWellFormed(stream, msg)
assertXMLStringValid = deprecated_function(
assertXMLStringWellFormed, 'assertXMLStringValid renamed to more precise assertXMLStringWellFormed')