#! /usr/bin/env python # -*- coding: iso-8859-1 -*- ## Copyright 2004-2007 by LivingLogic AG, Bayreuth/Germany. ## Copyright 2004-2007 by Walter Dörwald ## ## All Rights Reserved ## ## See __init__.py for the license """ ll.misc contains various utility functions and classes used by the other LivingLogic modules and packages. """ __version__ = tuple(map(int, "$Revision: 1.6 $"[11:-2].split("."))) # $Source: /data/cvsroot/LivingLogic/Python/core/src/ll/misc.py,v $ import sys, types, collections ### ### Decorators ### def notimplemented(function): """ A decorator that raises NotImplementedError when the method is called. This saves you the trouble of formatting the error message yourself for each implementation. """ def wrapper(self, *args, **kwargs): raise NotImplementedError("method %s() not implemented in %r" % (function.__name__, self.__class__)) wrapper.__dict__.update(function.__dict__) wrapper.__doc__ = function.__doc__ wrapper.__name__ = function.__name__ wrapper.__wrapped__ = function return wrapper def withdoc(doc): """ A decorator that adds a docstring to the function it decorates. This can be useful if the docstring is not static, and adding it afterwards is not possible. """ def wrapper(function): function.__doc__ = doc return function return wrapper ### ### Properties ### class _propclass_Meta(type): def __new__(cls, name, bases, dict): if bases == (property,): # create propclass itself normally return super(_propclass_Meta, cls).__new__(cls, name, bases, dict) newdict = dict.copy() newdict.pop("__get__", None) newdict.pop("__set__", None) newdict.pop("__delete__", None) newdict.pop("__metaclass__", None) self = type.__new__(cls, name, bases, newdict) inst = self( dict.get("__get__", None), dict.get("__set__", None), dict.get("__delete__", None), dict.get("__doc__", None) ) inst.__name__ = name return inst class propclass(property): ''' propclass provides an alternate way to define properties. Subclassing propclass and defining methods __get__, __set__ and __delete__ will automatically generate the appropriate property: class name(misc.propclass): """ The name property """ def __get__(self): return self._name def __set__(self, name): self._name = name.lower() def __delete__(self): self._name = None ''' __metaclass__ = _propclass_Meta class _Namespace_Meta(type): def __delattr__(self, key): value = getattr(self, key) if hasattr(value, "__ns__"): value.__ns__ = None return super(_Namespace_Meta, self).__delattr__(key) def __setattr__(self, key, value): if isinstance(value, types.FunctionType): value = staticmethod(value) if hasattr(value, "__ns__"): value.__ns__ = self return super(_Namespace_Meta, self).__setattr__(key, value) def __getitem__(self, key): try: return getattr(self, key) except AttributeError: raise KeyError(key) class Namespace(object): """ A namespace can be used as an inheritable module (see the method makemod). Namespaces are supposed to be subclassed, but not instantiated. """ __metaclass__ = _Namespace_Meta @classmethod def update(cls, *args, **kwargs): """ Copy attributes from all mappings in args and from kwargs. """ for mapping in args + (kwargs,): for (key, value) in mapping.iteritems(): if value is not cls and key not in ("__name__", "__dict__"): setattr(cls, key, value) @classmethod def makemod(cls, vars=None): """ Update with objects from vars (like update does) and turn the namespace into a module (replacing the module that contains the namespace in sys.modules). """ if vars is not None: cls.update(vars) name = vars["__name__"] if name in sys.modules: # If the name can't be found, the import is probably done by execfile(), in this case we can't communicate back that the module has been replaced cls.__originalmodule__ = sys.modules[name] # we have to keep the original module alive, otherwise Python would set all module attribute to None sys.modules[name] = cls # set the class name to the original module name, otherwise inspect.getmodule() will get problems cls.__name__ = name ### ### General iterator utilities ### _defaultitem = object() def item(iterator, index, default=_defaultitem): """ Return the indexth item from the iterator iterator. index must be an integer (negative integers are relative to the end (i.e. the last item produced by the iterator)). If default is given, this will be the default value when the iterator doesn't contain an item at this position. Otherwise an IndexError will be raised. Note that using this function will partially or totally exhaust the iterator. """ i = index if i>=0: for item in iterator: if not i: return item i -= 1 else: i = -index cache = collections.deque() for item in iterator: cache.append(item) if len(cache)>i: cache.popleft() if len(cache)==i: return cache.popleft() if default is _defaultitem: raise IndexError(index) else: return default def first(iterator, default=_defaultitem): """ Return the first object produced by the iterator iterator or default if the iterator didn't produce any items. Calling this function will consume one item from the iterator. """ return item(iterator, 0, default) def last(iterator, default=_defaultitem): """ Return the last object from the iterator iterator or default if the iterator didn't produce any items. Calling this function will exhaust the iterator. """ return item(iterator, -1, default) def count(iterator): """ Return the number of items produced by the iterator iterator. Calling this function will exhaust the iterator. """ count = 0 for node in iterator: count += 1 return count def iterone(item): """ Return an iterator that will produce one item: item. """ yield item ### ### Add __getitem__ support to an iterator ### class Iterator(object): """ Iterator adds __getitem__ support to an iterator. This is done by calling item internally. """ __slots__ = ("iterator", ) def __init__(self, iterator): self.iterator = iterator def __getitem__(self, index): if isinstance(index, slice): return list(self.iterator)[index] return item(self, index) def __iter__(self): return self def next(self): return self.iterator.next() # We can't implement __len__, because if such an object is passed to list(), __len__() would be called, exhausting the iterator def __nonzero__(self): for node in self: return True return False def get(self, index, default=None): """ Return the indexth item from the iterator (or default if there's no such item). """ return item(self, index, default) class Queue(object): """ Queue provides FIFO queues: The method write writes to the queue and the method read read from the other end of the queue and remove the characters read. """ def __init__(self): self._buffer = "" def write(self, chars): """ Write the string chars to the buffer. """ self._buffer += chars def read(self, size=-1): """ Read up to size character from the buffer (or all if size is negative). Those characters will be removed from the buffer. """ if size<0: s = self._buffer self._buffer = "" return s else: s = self._buffer[:size] self._buffer = self._buffer[size:] return s ### ### Constants ### class Const(object): """ This class can be used for singleton constants. """ __slots__ = ("_name") def __init__(self, name): self._name = name def __repr__(self): return "%s.%s" % (self.__module__, self._name)