Hello community, here is the log from the commit of package python-logilab-common for openSUSE:Factory checked in at 2012-02-24 06:53:44 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-logilab-common (Old) and /work/SRC/openSUSE:Factory/.python-logilab-common.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-logilab-common", Maintainer is "[email protected]" Changes: -------- --- /work/SRC/openSUSE:Factory/python-logilab-common/python-logilab-common.changes 2011-09-23 12:42:56.000000000 +0200 +++ /work/SRC/openSUSE:Factory/.python-logilab-common.new/python-logilab-common.changes 2012-02-24 06:53:50.000000000 +0100 @@ -1,0 +2,9 @@ +Sat Feb 18 16:32:08 UTC 2012 - [email protected] + +- Update to version 0.57.1 + * daemon: change $HOME after dropping privileges (closes #81297) + * compat: method_type for py3k use instance of the class to + * have a real instance method (closes: #79268) + + +------------------------------------------------------------------- Old: ---- logilab-common-0.56.2.tar.gz New: ---- logilab-common-0.57.1.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-logilab-common.spec ++++++ --- /var/tmp/diff_new_pack.EbZpLM/_old 2012-02-24 06:53:52.000000000 +0100 +++ /var/tmp/diff_new_pack.EbZpLM/_new 2012-02-24 06:53:52.000000000 +0100 @@ -1,7 +1,7 @@ # # spec file for package python-logilab-common # -# Copyright (c) 2011 SUSE LINUX Products GmbH, Nuernberg, Germany. +# Copyright (c) 2012 SUSE LINUX Products GmbH, Nuernberg, Germany. # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -16,9 +16,8 @@ # - Name: python-logilab-common -Version: 0.56.2 +Version: 0.57.1 Release: 0 Url: http://www.logilab.org/projects/common/ Summary: Python lowlevel functionality shared by logilab projects ++++++ logilab-common-0.56.2.tar.gz -> logilab-common-0.57.1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/logilab-common-0.56.2/ChangeLog new/logilab-common-0.57.1/ChangeLog --- old/logilab-common-0.56.2/ChangeLog 2011-09-08 16:43:22.000000000 +0200 +++ new/logilab-common-0.57.1/ChangeLog 2011-10-28 12:00:09.000000000 +0200 @@ -1,6 +1,29 @@ ChangeLog for logilab.common ============================ +2011-10-28 -- 0.57.1 + * daemon: change $HOME after dropping privileges (closes #81297) + + * compat: method_type for py3k use instance of the class to have a + real instance method (closes: #79268) + + + +2011-10-12 -- 0.57.0 + * only install unittest2 when python version < 2.7 (closes: #76068) + + * daemon: make pidfile world-readable (closes #75968) + + * daemon: remove unused(?) DaemonMixin class + + * update compat module for callable() and method_type() + + * decorators: fix monkeypatch py3k compat (closes #75290) + + * decorators: provide a @cachedproperty decorator + + + 2011-09-08 -- 0.56.2 * daemon: call initgroups/setgid before setuid (closes #74173) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/logilab-common-0.56.2/PKG-INFO new/logilab-common-0.57.1/PKG-INFO --- old/logilab-common-0.56.2/PKG-INFO 2011-09-08 18:14:09.000000000 +0200 +++ new/logilab-common-0.57.1/PKG-INFO 2011-10-28 12:00:19.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 1.0 Name: logilab-common -Version: 0.56.2 +Version: 0.57.1 Summary: collection of low-level Python packages and modules used by Logilab projects Home-page: http://www.logilab.org/project/logilab-common Author: Logilab diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/logilab-common-0.56.2/__pkginfo__.py new/logilab-common-0.57.1/__pkginfo__.py --- old/logilab-common-0.56.2/__pkginfo__.py 2011-09-08 16:43:22.000000000 +0200 +++ new/logilab-common-0.57.1/__pkginfo__.py 2011-10-28 12:00:09.000000000 +0200 @@ -17,13 +17,14 @@ # with logilab-common. If not, see <http://www.gnu.org/licenses/>. """logilab.common packaging information""" __docformat__ = "restructuredtext en" +import sys distname = 'logilab-common' modname = 'common' subpackage_of = 'logilab' subpackage_master = True -numversion = (0, 56, 2) +numversion = (0, 57, 1) version = '.'.join([str(num) for num in numversion]) license = 'LGPL' # 2.1 or later @@ -39,4 +40,6 @@ scripts = [join('bin', 'pytest')] include_dirs = [join('test', 'data')] -install_requires = ['unittest2 >= 0.5.1'] +if sys.version_info < (2, 7): + install_requires = ['unittest2 >= 0.5.1'] + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/logilab-common-0.56.2/bin/pytest new/logilab-common-0.57.1/bin/pytest --- old/logilab-common-0.56.2/bin/pytest 2008-03-05 18:08:25.000000000 +0100 +++ new/logilab-common-0.57.1/bin/pytest 2011-09-08 18:14:28.000000000 +0200 @@ -1,4 +1,4 @@ -#!/usr/bin/python -u +#!/usr/bin/python -uWdefault from logilab.common.pytest import run run() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/logilab-common-0.56.2/compat.py new/logilab-common-0.57.1/compat.py --- old/logilab-common-0.56.2/compat.py 2011-04-26 09:44:21.000000000 +0200 +++ new/logilab-common-0.57.1/compat.py 2011-10-28 12:00:10.000000000 +0200 @@ -20,7 +20,7 @@ 2.5, making them available in for earlier versions of python. See another compatibility snippets from other projects: - + :mod:`lib2to3.fixes` :mod:`coverage.backward` :mod:`unittest2.compatibility` @@ -32,6 +32,7 @@ import os import sys +import types from warnings import warn import __builtin__ as builtins # 2to3 will tranform '__builtin__' to 'builtins' @@ -50,14 +51,23 @@ def str_encode(string, encoding): return str(string) -# XXX shouldn't we remove this and just let 2to3 do his job ? +# XXX callable built-in seems back in all python versions try: - callable = callable -except NameError:# callable removed from py3k - import collections + callable = builtins.callable +except AttributeError: + from collections import Callable def callable(something): - return isinstance(something, collections.Callable) - del collections + return isinstance(something, Callable) + del Callable + +# See also http://bugs.python.org/issue11776 +if sys.version_info[0] == 3: + def method_type(callable, instance, klass): + # api change. klass is no more considered + return types.MethodType(callable, instance) +else: + # alias types otherwise + method_type = types.MethodType if sys.version_info < (3, 0): raw_input = raw_input diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/logilab-common-0.56.2/daemon.py new/logilab-common-0.57.1/daemon.py --- old/logilab-common-0.56.2/daemon.py 2011-09-08 16:43:22.000000000 +0200 +++ new/logilab-common-0.57.1/daemon.py 2011-10-28 12:00:10.000000000 +0200 @@ -15,7 +15,7 @@ # # You should have received a copy of the GNU Lesser General Public License along # with logilab-common. If not, see <http://www.gnu.org/licenses/>. -"""A daemonize function (for Unices) and daemon mix-in class""" +"""A daemonize function (for Unices)""" __docformat__ = "restructuredtext en" @@ -46,6 +46,7 @@ raise OSError(err, os.strerror(err), 'initgroups') os.setgid(passwd.pw_gid) os.setuid(passwd.pw_uid) + os.putenv('HOME', passwd.pw_dir) def daemonize(pidfile=None, uid=None, umask=077): @@ -92,109 +93,8 @@ f = file(pidfile, 'w') f.write(str(os.getpid())) f.close() + os.chmod(pidfile, 0644) # change process uid if uid: setugid(uid) return None - - -class DaemonMixIn: - """Mixin to make a daemon from watchers/queriers. - """ - - def __init__(self, configmod) : - self.delay = configmod.DELAY - self.name = str(self.__class__).split('.')[-1] - self._pid_file = os.path.join('/tmp', '%s.pid'%self.name) - if os.path.exists(self._pid_file): - raise Exception('''Another instance of %s must be running. -If it i not the case, remove the file %s''' % (self.name, self._pid_file)) - self._alive = 1 - self._sleeping = 0 - self.config = configmod - - def _daemonize(self): - if not self.config.NODETACH: - if daemonize(self._pid_file) is None: - # put signal handler - signal.signal(signal.SIGTERM, self.signal_handler) - signal.signal(signal.SIGHUP, self.signal_handler) - else: - return -1 - - def run(self): - """ optionally go in daemon mode and - do what concrete class has to do and pauses for delay between runs - If self.delay is negative, do a pause before starting - """ - if self._daemonize() == -1: - return - if self.delay < 0: - self.delay = -self.delay - time.sleep(self.delay) - while True: - try: - self._run() - except Exception, ex: - # display for info, sleep, and hope the problem will be solved - # later. - self.config.exception('Internal error: %s', ex) - if not self._alive: - break - try: - self._sleeping = 1 - time.sleep(self.delay) - self._sleeping = 0 - except SystemExit: - break - self.config.info('%s instance exited', self.name) - # remove pid file - os.remove(self._pid_file) - - def signal_handler(self, sig_num, stack_frame): - if sig_num == signal.SIGTERM: - if self._sleeping: - # we are sleeping so we can exit without fear - self.config.debug('exit on SIGTERM') - sys.exit(0) - else: - self.config.debug('exit on SIGTERM (on next turn)') - self._alive = 0 - elif sig_num == signal.SIGHUP: - self.config.info('reloading configuration on SIGHUP') - reload(self.config) - - def _run(self): - """should be overridden in the mixed class""" - raise NotImplementedError() - - -import logging -from logilab.common.logging_ext import set_log_methods -set_log_methods(DaemonMixIn, logging.getLogger('lgc.daemon')) - -## command line utilities ###################################################### - -L_OPTIONS = ["help", "log=", "delay=", 'no-detach'] -S_OPTIONS = 'hl:d:n' - -def print_help(modconfig): - print """ --help or -h - displays this message - --log <log_level> - log treshold (7 record everything, 0 record only emergency.) - Defaults to %s - --delay <delay> - the number of seconds between two runs. - Defaults to %s""" % (modconfig.LOG_TRESHOLD, modconfig.DELAY) - -def handle_option(modconfig, opt_name, opt_value, help_meth): - if opt_name in ('-h', '--help'): - help_meth() - sys.exit(0) - elif opt_name in ('-l', '--log'): - modconfig.LOG_TRESHOLD = int(opt_value) - elif opt_name in ('-d', '--delay'): - modconfig.DELAY = int(opt_value) - elif opt_name in ('-n', '--no-detach'): - modconfig.NODETACH = 1 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/logilab-common-0.56.2/decorators.py new/logilab-common-0.57.1/decorators.py --- old/logilab-common-0.56.2/decorators.py 2011-09-08 16:43:22.000000000 +0200 +++ new/logilab-common-0.57.1/decorators.py 2011-10-28 12:00:10.000000000 +0200 @@ -18,10 +18,11 @@ """ A few useful function/method decorators. """ __docformat__ = "restructuredtext en" -import types -import sys, re +import sys from time import clock, time +from logilab.common.compat import callable, method_type + # XXX rewrite so we can use the decorator syntax when keyarg has to be specified def _is_generator_function(callableobj): @@ -116,6 +117,45 @@ else: return decorator(callableobj) + +class cachedproperty(object): + """ Provides a cached property equivalent to the stacking of + @cached and @property, but more efficient. + + After first usage, the <property_name> becomes part of the object's + __dict__. Doing: + + del obj.<property_name> empties the cache. + + Idea taken from the pyramid_ framework and the mercurial_ project. + + .. _pyramid: http://pypi.python.org/pypi/pyramid + .. _mercurial: http://pypi.python.org/pypi/Mercurial + """ + __slots__ = ('wrapped',) + + def __init__(self, wrapped): + try: + wrapped.__name__ + except AttributeError: + raise TypeError('%s must have a __name__ attribute' % + wrapped) + self.wrapped = wrapped + + @property + def __doc__(self): + doc = getattr(self.wrapped, '__doc__', None) + return ('<wrapped by the cachedproperty decorator>%s' + % ('\n%s' % doc if doc else '')) + + def __get__(self, inst, objtype=None): + if inst is None: + return self + val = self.wrapped(inst) + setattr(inst, self.wrapped.__name__, val) + return val + + def get_cache_impl(obj, funcname): cls = obj.__class__ member = getattr(cls, funcname) @@ -175,8 +215,8 @@ self.func = func def __get__(self, instance, objtype): if instance is None: - return types.MethodType(self.func, objtype, objtype.__class__) - return types.MethodType(self.func, instance, objtype) + return method_type(self.func, objtype, objtype.__class__) + return method_type(self.func, instance, objtype) def __set__(self, instance, value): raise AttributeError("can't set attribute") @@ -233,8 +273,8 @@ raise AttributeError('%s has no __name__ attribute: ' 'you should provide an explicit `methodname`' % func) - if callable(func): - setattr(klass, name, types.MethodType(func, None, klass)) + if callable(func) and sys.version_info < (3, 0): + setattr(klass, name, method_type(func, None, klass)) else: # likely a property # this is quite borderline but usage already in the wild ... diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/logilab-common-0.56.2/deprecation.py new/logilab-common-0.57.1/deprecation.py --- old/logilab-common-0.56.2/deprecation.py 2011-04-26 09:44:21.000000000 +0200 +++ new/logilab-common-0.57.1/deprecation.py 2011-10-28 11:59:58.000000000 +0200 @@ -71,7 +71,7 @@ old_name, new_class.__module__, new_class.__name__) return class_renamed(old_name, new_class, message) -def deprecated(reason=None, stacklevel=2): +def deprecated(reason=None, stacklevel=2, name=None, doc=None): """Decorator that raises a DeprecationWarning to print a message when the decorated function is called. """ @@ -83,10 +83,10 @@ warn(message, DeprecationWarning, stacklevel=stacklevel) return func(*args, **kwargs) try: - wrapped.__name__ = func.__name__ + wrapped.__name__ = name or func.__name__ except TypeError: # readonly attribute in 2.3 pass - wrapped.__doc__ = func.__doc__ + wrapped.__doc__ = doc or func.__doc__ return wrapped return deprecated_decorator diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/logilab-common-0.56.2/test/unittest_decorators.py new/logilab-common-0.57.1/test/unittest_decorators.py --- old/logilab-common-0.56.2/test/unittest_decorators.py 2011-09-08 16:43:22.000000000 +0200 +++ new/logilab-common-0.57.1/test/unittest_decorators.py 2011-10-28 11:59:58.000000000 +0200 @@ -20,7 +20,8 @@ import types from logilab.common.testlib import TestCase, unittest_main -from logilab.common.decorators import monkeypatch, cached, clear_cache, copy_cache +from logilab.common.decorators import (monkeypatch, cached, clear_cache, + copy_cache, cachedproperty) class DecoratorsTC(TestCase): @@ -161,5 +162,40 @@ copy_cache(foo2, 'foo', foo) self.assertEqual(foo2._foo, {(1,): None}) + + def test_cachedproperty(self): + class Foo(object): + x = 0 + @cachedproperty + def bar(self): + self.__class__.x += 1 + return self.__class__.x + @cachedproperty + def quux(self): + """ some prop """ + return 42 + + foo = Foo() + self.assertEqual(Foo.x, 0) + self.failIf('bar' in foo.__dict__) + self.assertEqual(foo.bar, 1) + self.failUnless('bar' in foo.__dict__) + self.assertEqual(foo.bar, 1) + self.assertEqual(foo.quux, 42) + self.assertEqual(Foo.bar.__doc__, + '<wrapped by the cachedproperty decorator>') + self.assertEqual(Foo.quux.__doc__, + '<wrapped by the cachedproperty decorator>\n some prop ') + + foo2 = Foo() + self.assertEqual(foo2.bar, 2) + # make sure foo.foo is cached + self.assertEqual(foo.bar, 1) + + class Kallable(object): + def __call__(self): + return 42 + self.assertRaises(TypeError, cachedproperty, Kallable()) + if __name__ == '__main__': unittest_main() -- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
