#! /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
"""
<module>ll.misc</module> 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 <class>NotImplementedError</class> 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):
'''
<par><class>propclass</class> provides an alternate way to define properties.</par>
<par>Subclassing <class>propclass</class> and defining methods
<method>__get__</method>, <method>__set__</method> and <method>__delete__</method>
will automatically generate the appropriate property:</par>
<prog>
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
</prog>
'''
__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 <z>inheritable module</z> (see the method
<pyref method="makemod"><method>makemod</method></pyref>). Namespaces are
supposed to be subclassed, but not instantiated.
"""
__metaclass__ = _Namespace_Meta
@classmethod
def update(cls, *args, **kwargs):
"""
Copy attributes from all mappings in <arg>args</arg> and from
<arg>kwargs</arg>.
"""
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 <cls/> with objects from <arg>vars</arg> (like
<pyref method="update"><method>update</method></pyref> does) and turn the
namespace into a module (replacing the module that contains the namespace
in <lit>sys.modules</lit>).
"""
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):
"""
<par>Return the <arg>index</arg>th item from the iterator <arg>iterator</arg>.
<arg>index</arg> must be an integer (negative integers are relative to the
end (i.e. the last item produced by the iterator)).</par>
<par>If <arg>default</arg> is given, this will be the default value when
the iterator doesn't contain an item at this position. Otherwise an
<class>IndexError</class> will be raised.</par>
<par>Note that using this function will partially or totally exhaust the
iterator.</par>
"""
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):
"""
<par>Return the first object produced by the iterator <arg>iterator</arg> or
<arg>default</arg> if the iterator didn't produce any items.</par>
<par>Calling this function will consume one item from the iterator.</par>
"""
return item(iterator, 0, default)
def last(iterator, default=_defaultitem):
"""
<par>Return the last object from the iterator <arg>iterator</arg> or
<arg>default</arg> if the iterator didn't produce any items.</par>
<par>Calling this function will exhaust the iterator.</par>
"""
return item(iterator, -1, default)
def count(iterator):
"""
<par>Return the number of items produced by the iterator <arg>iterator</arg>.</par>
<par>Calling this function will exhaust the iterator.</par>
"""
count = 0
for node in iterator:
count += 1
return count
def iterone(item):
"""
Return an iterator that will produce one item: <arg>item</arg>.
"""
yield item
###
### Add __getitem__ support to an iterator
###
class Iterator(object):
"""
<class>Iterator</class> adds <method>__getitem__</method> support to an
iterator. This is done by calling <pyref function="item"><function>item</function></pyref>
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 <arg>index</arg>th item from the iterator
(or <arg>default</arg> if there's no such item).
"""
return item(self, index, default)
class Queue(object):
"""
<class>Queue</class> provides FIFO queues: The method <method>write</method>
writes to the queue and the method <method>read</method> 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 <arg>chars</arg> to the buffer.
"""
self._buffer += chars
def read(self, size=-1):
"""
Read up to <arg>size</arg> character from the buffer (or all if <arg>size</arg> 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)
syntax highlighted by Code2HTML, v. 0.9.1