Source code for mmf.objects.attributes

"""Module to facilitate attribute definition.

Goal: 
  To provide a method for easily specifying the _state_vars of classes
  and the arguments of functions.  This should allow:
  - Easy specification of _state_vars, including
    - default values and
    - documentation.
  - Incoporation of specification in docstrings.
  - Automatic definition of __init__ to provide construction and
    copy-construction based on parameter specification.
  - Automatic construction of archiving, persistence (pickling), and
    repr/str.
  - Easy techniques for combining objects, either through
    - inheritance or
    - delegation.
  For functions, support should be provided so that the arguments can
  be easily specified and constructed.
  - Easy specification of arguments, including
    - documentation and
    - defaults.
  - Automatic generation of argument processing.
  - Easy technique for combining arguments from delegates.
  - Automatic checking of argument.

Strategy:
  - For classes, define a metaclass to generate the documentation etc.
    Desired functionality is provided through inheritance.
  - For functions, use decorator syntax.

Example:

>>> import mmf.objects
>>> class Complex(mmf.objects.StateVars):
...     _state_vars = [('x', 0.0, 'Real part'),
...                    ('y', 0.0, 'Imaginary part')]
...     mmf.objects.process_vars()
>>> z1 = Complex()
>>> z1
Complex()
>>> z2 = Complex(x=1.0, y=2.0)
>>> z2
Complex(x=1.0, y=2.0)
>>> z3 = Complex(z2)
>>> z3
Complex(x=1.0, y=2.0)

>> class Options(mmf.objects.StateVars):
...     _state_vars = [('tol', 1e-5, "Tolerance"),
...                    ('max_iter', 1000, "Maximum number of iterations"),
...                    ('verbose', True)]
...     mmf.objects.process_vars()
>> @argsfrom(Options)
... def sqrt(n, _args):
...     "Compute sqrt(n) using Newton's method"
...     opt = Options(_args)
...     _args.check_usage()         # Check to make sure all args were used.
...     x0 = 1.0
...     for i in xrange(opt.max_iter):
...         x1 = x0 - (x0*x0-n)/(2.0*x0)
...         if abs(x0-x1) < opt.tol:
...             return x1
...         x0 = x1
>> sqrt(2)                     # doctest: +ELLIPSIS
1.41421...


One option which is presently rejected is the following syntax:
class Complex(object):
    x = attribute("Real part", default=0.0)
    y = attribute("Imaginary part", default=0.0)

This reads nicely, but does not preserve the order of attributes.  It
is desirable to keep the order of attributes so that important
attributes can be specified before less important ones.
"""

import new
import inspect
import mmf.objects

[docs]def argsfrom(Class, exclude=[]): def decorator(f): exclude_ = set(exclude) (args_f, varargs, varkw_f, defaults_f) = inspect.getargspec(f) (args_c, varargs_c, varkw_c, defaults_c) = Class.getargspec() required_args = (args_f[:-len(defaults_f)] + args_c[:-len(defaults_c)]) default_args = (args_f[-len(defaults_f):] + args_c[-len(defaults_c):]) defaults = tuple(list(defaults_f) + list(defaults_c)) env = dict(zip(default_args, defaults)) env['__f'] = f varkw = varkw_f args = (required_args + ["%s=%s"%x for x in default_args]) fargs = ["%s"%a for a in args_f] if varargs is not None: args += ["*%s"%varargs] fargs += ["*%s"%varargs] if varkw is not None: args += ["**%s"%varkw] subs = dict(name=f.__name__, args=", ".join(args), doc=f.__doc__, fargs='') """\ def %(name)s(%(args)s): %(doc)s _args = %(code)s return __f(%(fargs)s, _args=)""" new_code = new_f.func_code argcount = len(args) nlocals = new_code.co_nlocals + len(args) if varkw is not None: nlocals += 1 if vararg is not None: nlocals += 1 stacksize = new_code.co_stacksize flags = new_code.co_flags "f = new.function()" return f return decorator
''' class C(mmf.objects.StateVars): _state_vars = [('a', 1, 'Coefficient of x^2'), ('b', 2), ('c', 3)] mmf.objects.process_vars() def compute(self, x): return self.a*x**2 + self.b*x + self.c def f(x, b=2, c=3): c = C(a=1, b=b, c=c) return c.compute(x) @argsfrom(C, exclude=['a']) def f(x, _args): c = C(a=1, **_args) return c.compute(x) def decorated_f(x, a=1, b=2, c=3): _args = dict(a=a, b=b, c=c) return f(x, _args) def decorate(msg): def _decorate(f): def new_f(*argv, **kwargs): print msg return f(*argv, **kwargs) return new_f return _decorate @decorate("hi there") def sq(x): """Square x""" return x*x '''