diff --git a/flax/TLUtility.py b/flax/TLUtility.py new file mode 100644 --- /dev/null +++ b/flax/TLUtility.py @@ -0,0 +1,209 @@ +"""Collected utility functions, many are taken from Drew's utils.py in +Cuisine CVS and Hiss's Utility.py.""" + +from __future__ import generators +import sys + +__author__ = "David McClosky , " + \ + "Drew Perttula " +__cvsid__ = "$Id: TLUtility.py,v 1.1 2003/05/25 08:25:35 dmcc Exp $" +__version__ = "$Revision: 1.1 $"[11:-2] + +def make_attributes_from_args(*argnames): + """ + This function simulates the effect of running + self.foo=foo + for each of the given argument names ('foo' in the example just + now). Now you can write: + def __init__(self,foo,bar,baz): + copy_to_attributes('foo','bar','baz') + ... + instead of: + def __init__(self,foo,bar,baz): + self.foo=foo + self.bar=bar + self.baz=baz + ... + """ + + callerlocals=sys._getframe(1).f_locals + callerself=callerlocals['self'] + for a in argnames: + try: + setattr(callerself,a,callerlocals[a]) + except KeyError: + raise KeyError, "Function has no argument '%s'" % a + +def enumerate(*collections): + """Generates an indexed series: (0,coll[0]), (1,coll[1]) ... + + this is a multi-list version of the code from the PEP: + enumerate(a,b) gives (0,a[0],b[0]), (1,a[1],b[1]) ... + """ + i = 0 + iters = [iter(collection) for collection in collections] + while 1: + yield [i,] + [iterator.next() for iterator in iters] + i += 1 + +def dumpobj(o): + """Prints all the object's non-callable attributes""" + print repr(o) + for a in [x for x in dir(o) if not callable(getattr(o, x))]: + try: + print " %20s: %s " % (a, getattr(o, a)) + except: + pass + print "" + +def dict_filter_update(d, **newitems): + """Adds a set of new keys and values to dictionary 'd' if the values are + true: + + >>> some_dict = {} + >>> dict_filter_update(some_dict, a=None, b=0, c=1, e={}, s='hello') + >>> some_dict + {'c': 1, 's': 'hello'} + """ + for k, v in newitems.items(): + if v: d[k] = v + +def try_get_logger(channel): + """Tries to get a logger with the channel 'channel'. Will return a + silent DummyClass if logging is not available.""" + try: + import logging + log = logging.getLogger(channel) + except ImportError: + log = DummyClass() + return log + +class DummyClass: + """A class that can be instantiated but never used. It is intended to + be replaced when information is available. + + Usage: + >>> d = DummyClass(1, 2, x="xyzzy") + >>> d.someattr + Traceback (most recent call last): + File "", line 1, in ? + File "Utility.py", line 33, in __getattr__ + raise AttributeError, "Attempted usage of a DummyClass: %s" % key + AttributeError: Attempted usage of a DummyClass: someattr + >>> d.somefunction() + Traceback (most recent call last): + File "", line 1, in ? + File "Utility.py", line 33, in __getattr__ + raise AttributeError, "Attempted usage of a DummyClass: %s" % key + AttributeError: Attempted usage of a DummyClass: somefunction""" + def __init__(self, use_warnings=1, raise_exceptions=0, **kw): + """Constructs a DummyClass""" + make_attributes_from_args('use_warnings', 'raise_exceptions') + def __getattr__(self, key): + """Raises an exception to warn the user that a Dummy is not being + replaced in time.""" + if key == "__del__": + return + msg = "Attempted usage of '%s' on a DummyClass" % key + if self.use_warnings: + import warnings + warnings.warn(msg) + if self.raise_exceptions: + raise AttributeError, msg + return lambda *args, **kw: self.bogus_function() + def bogus_function(self): + pass + +class ClassyDict(dict): + """A dict that accepts attribute-style access as well (for keys + that are legal names, obviously). I used to call this Struct, but + chose the more colorful name to avoid confusion with the struct + module.""" + def __getattr__(self, a): + return self[a] + def __setattr__(self, a, v): + self[a] = v + def __delattr__(self, a): + del self[a] + +def trace(func): + """Good old fashioned Lisp-style tracing. Example usage: + + >>> def f(a, b, c=3): + >>> print a, b, c + >>> return a + b + >>> + >>> + >>> f = trace(f) + >>> f(1, 2) + |>> f called args: [1, 2] + 1 2 3 + <<| f returned 3 + 3 + + TODO: print out default keywords (maybe) + indent for recursive call like the lisp version (possible use of + generators?)""" + name = func.func_name + def tracer(*args, **kw): + s = '|>> %s called' % name + if args: + s += ' args: %r' % list(args) + if kw: + s += ' kw: %r' % kw + print s + ret = func(*args, **kw) + print '<<| %s returned %s' % (name, ret) + return ret + return tracer + +# these functions taken from old light8 code +def dict_max(*dicts): + """ + ({'a' : 5, 'b' : 9}, {'a' : 10, 'b' : 4}) + returns ==> {'a' : 10, 'b' : 9} + """ + newdict = {} + for d in dicts: + for k,v in d.items(): + newdict[k] = max(v, newdict.get(k, 0)) + return newdict + +def dict_scale(d,scl): + """scales all values in dict and returns a new dict""" + return dict([(k,v*scl) for k,v in d.items()]) + +def dict_subset(d, dkeys, default=0): + """Subset of dictionary d: only the keys in dkeys. If you plan on omitting + keys, make sure you like the default.""" + newd = {} # dirty variables! + for k in dkeys: + newd[k] = d.get(k, default) + return newd + +# functions specific to Timeline +# TBD +def last_less_than(array, x): + """array must be sorted""" + best = None + for elt in array: + if elt <= x: + best = elt + elif best is not None: + return best + return best + +# TBD +def first_greater_than(array, x): + """array must be sorted""" + array_rev = array[:] + array_rev.reverse() + best = None + for elt in array_rev: + if elt >= x: + best = elt + elif best is not None: + return best + return best + +