|
|
|
@ -1,6 +1,6 @@ |
|
|
|
|
#!/usr/bin/ipython -pylab |
|
|
|
|
# |
|
|
|
|
# $Id: sugar.py,v 1.3 2010-02-19 18:42:15 wirawan Exp $ |
|
|
|
|
# $Id: sugar.py,v 1.4 2010-05-28 18:47:56 wirawan Exp $ |
|
|
|
|
# |
|
|
|
|
# Created: 20100121 |
|
|
|
|
# Wirawan Purwanto |
|
|
|
@ -63,3 +63,145 @@ def dict_join(*dicts): |
|
|
|
|
return rslt |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Parameters(object): |
|
|
|
|
"""A standardized way to define and/or pass parameters (with possible |
|
|
|
|
default values) among routines. |
|
|
|
|
This provides a very flexible lookup scheme for a parameter with a given name. |
|
|
|
|
It scans through the namescopes (dicts) in a deterministic order, returning |
|
|
|
|
the first one found. |
|
|
|
|
This, hopefully, gets rid of kitchen-sink parameter passing, at least from |
|
|
|
|
programmer's point of view. |
|
|
|
|
|
|
|
|
|
WARNING: This object is derived object instead of python dict, so as to avoid |
|
|
|
|
messing with standard dict names. |
|
|
|
|
Names reserved by this class begin and end with an underscore. |
|
|
|
|
Names reserved by python begin and end with two underscores. |
|
|
|
|
So, avoid specifying parameters with both leading and trailing underscores. |
|
|
|
|
|
|
|
|
|
Some uses: |
|
|
|
|
|
|
|
|
|
def stuff(params=None, **kwparams): |
|
|
|
|
# `params' defines the standard way of passing parameters, which is |
|
|
|
|
# via a Parameters object. |
|
|
|
|
# `kwparams' determine a quick way of temporarily overriding a parameter |
|
|
|
|
# value. |
|
|
|
|
prm = Parameters(kwparams, params, global_defaults) |
|
|
|
|
for step in prm.steps: |
|
|
|
|
... |
|
|
|
|
|
|
|
|
|
Reserved members: |
|
|
|
|
* _no_null_ = (True/False, default False) look for non-null values in all |
|
|
|
|
the parameter lists until one is found. |
|
|
|
|
* _list_ = (list) the list of parameter dicts to search from. |
|
|
|
|
* _prm_ = (dict) the most overriding list of parameters. |
|
|
|
|
""" |
|
|
|
|
def __init__(self, *_override_dicts_, **_opts_): |
|
|
|
|
""" |
|
|
|
|
Again, keyword arguments passed here will become the most overriding options. |
|
|
|
|
""" |
|
|
|
|
prm = _opts_ |
|
|
|
|
self.__dict__["_no_null_"] = ifelse(_opts_.get("_no_null_"), True, False) |
|
|
|
|
self.__dict__["_prm_"] = prm |
|
|
|
|
paramlist = (prm,) + _override_dicts_ #+ tuple(deflist)) |
|
|
|
|
self.__dict__["_list_"] = [ p for p in paramlist if p != None ] |
|
|
|
|
def __getattr__(self, key): |
|
|
|
|
"""Allows options to be accessed in attribute-like manner, like: |
|
|
|
|
opt.niter = 3 |
|
|
|
|
instead of |
|
|
|
|
opt['niter'] = 3 |
|
|
|
|
""" |
|
|
|
|
if self._no_null_: |
|
|
|
|
for ov in self._list_: |
|
|
|
|
if key in ov and ov[key] != None: return ov[key] |
|
|
|
|
else: |
|
|
|
|
for ov in self._list_: |
|
|
|
|
if key in ov: return ov[key] |
|
|
|
|
# Otherwise: |
|
|
|
|
return object.__getattribute__(self, key) |
|
|
|
|
def __setattr__(self, key, value): |
|
|
|
|
"""This method always sets the value on the object's dictionary. |
|
|
|
|
Values set will override any values set in the input parameter lists.""" |
|
|
|
|
self._prm_[key] = value |
|
|
|
|
def __contains__(self, key): |
|
|
|
|
if self._no_null_: |
|
|
|
|
for ov in self._list_: |
|
|
|
|
if key in ov and ov[key] != None: return True |
|
|
|
|
else: |
|
|
|
|
for ov in self._list_: |
|
|
|
|
if key in ov: return True |
|
|
|
|
return False |
|
|
|
|
def __getitem__(self, key): |
|
|
|
|
if self._no_null_: |
|
|
|
|
for ov in self._list_: |
|
|
|
|
if key in ov and ov[key] != None: return ov[key] |
|
|
|
|
else: |
|
|
|
|
for ov in self._list_: |
|
|
|
|
if key in ov: return ov[key] |
|
|
|
|
raise KeyError, "Cannot find parameter `%s'" % key |
|
|
|
|
def __setitem__(self, key, value): |
|
|
|
|
self._prm_[key] = value |
|
|
|
|
# TODO in the future for iterative accesses: |
|
|
|
|
# -- not that essential because we know the name of |
|
|
|
|
# the parameters we want to get: |
|
|
|
|
#def __iter__(self): |
|
|
|
|
# return self._prm_.__iter__ |
|
|
|
|
#def _iteritems_(self): |
|
|
|
|
# return self._prm_.iteritems() |
|
|
|
|
def _update_(self, srcdict): |
|
|
|
|
self._prm_.update(srcdict) |
|
|
|
|
def _create_(self, kwparams="_opts_", userparams="opts", *defaults): |
|
|
|
|
"""Creates a new Parameters() object for standardized function-level |
|
|
|
|
parameter lookup. |
|
|
|
|
This routine *must* be called by the function where we want to access these |
|
|
|
|
parameters, and where some parameters are to be overriden via function arguments, |
|
|
|
|
etc. |
|
|
|
|
|
|
|
|
|
The order of lookup is definite: |
|
|
|
|
* |
|
|
|
|
|
|
|
|
|
class Something(object): |
|
|
|
|
def __init__(self, ...): |
|
|
|
|
self.opts = Parameters() |
|
|
|
|
self.opts.cleanup = True # example |
|
|
|
|
|
|
|
|
|
def doit(self, src=None, info=None, |
|
|
|
|
_defaults_=dict(src="source.txt", info="INFO.txt", debug=1), |
|
|
|
|
**_opts_): |
|
|
|
|
# FIXME: use self-introspection to reduce kitchen-sink params here: |
|
|
|
|
p = self.opts._create_() |
|
|
|
|
# ^ This will create an equivalent of: |
|
|
|
|
# Parameters(locals(), _opts_, _opts_.get('opts'), self.opts, _defaults) |
|
|
|
|
""" |
|
|
|
|
# Look up the stack of the calling function in order to retrieve its |
|
|
|
|
# local variables |
|
|
|
|
from inspect import stack |
|
|
|
|
caller = stack()[1][0] # one frame up; element-0 is the stack frame |
|
|
|
|
|
|
|
|
|
# local variables will be the first to look for |
|
|
|
|
localvars = caller.f_locals |
|
|
|
|
contexts = [ localvars ] |
|
|
|
|
# then _opts_ excess-keyword parameters (see example of doit() above) |
|
|
|
|
if kwparams in localvars: |
|
|
|
|
_opts_ = localvars[kwparams] |
|
|
|
|
contexts.append(_opts_) |
|
|
|
|
else: |
|
|
|
|
_opts_ = {} |
|
|
|
|
# then opts, an explicitly-defined argument carrying set of parameters |
|
|
|
|
if userparams in localvars: |
|
|
|
|
opts = localvars[userparams] |
|
|
|
|
contexts.append(opts) |
|
|
|
|
else: |
|
|
|
|
opts = {} |
|
|
|
|
if userparams in _opts_: |
|
|
|
|
contexts.append(_opts_[userparams]) |
|
|
|
|
|
|
|
|
|
# then this own Parameters data will come here: |
|
|
|
|
contexts.append(self) |
|
|
|
|
|
|
|
|
|
# then any defaults |
|
|
|
|
contexts += [ d for d in defaults ] |
|
|
|
|
|
|
|
|
|
# Now construct the Parameters() class for this calling function: |
|
|
|
|
return Parameters(*contexts) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|