There are some things related to the auto-association code that torment me (mostly stylistic, but another serious problem):
autoassign does not assign an args attribute:
class Foo(object): @autoassign def __init__(self,a,b,c=False,*args): pass a=Foo('IBM','/tmp',True, 100, 101) print(a.args)
autoassign acts as a decorator. But autoassign(*argnames) calls a function that returns a decorator. To achieve this magic, autoassign needs to check the type of the first argument. If given a choice, I prefer the function not to test the type of its arguments.
It seems that there is a significant amount of code designed to configure sieve , lambda inside lambda, ifilters and a lot of conditions.
if kwargs: exclude, f = set(kwargs['exclude']), None sieve = lambda l:itertools.ifilter(lambda nv: nv[0] not in exclude, l) elif len(names) == 1 and inspect.isfunction(names[0]): f = names[0] sieve = lambda l:l else: names, f = set(names), None sieve = lambda l: itertools.ifilter(lambda nv: nv[0] in names, l)
I think there might be an easier way. (See below).
for _ in itertools.starmap(assigned.setdefault, defaults): pass . I donβt think map or starmap intended to call functions whose sole purpose is their side effects. It could have been written more clearly with the things of the world:
for key,value in defaults.iteritems(): assigned.setdefault(key,value)
Here is an alternative simpler implementation that has the same functionality as the autoassign function (for example, can enable and disable), and which is addressed to the above points:
import inspect import functools def autoargs(*include, **kwargs): def _autoargs(func): attrs, varargs, varkw, defaults = inspect.getargspec(func) def sieve(attr): if kwargs and attr in kwargs['exclude']: return False if not include or attr in include: return True else: return False @functools.wraps(func) def wrapper(self, *args, **kwargs):
And here I used the unit test to test its behavior:
import sys import unittest import utils_method as um class Test(unittest.TestCase): def test_autoargs(self): class A(object): @um.autoargs() def __init__(self,foo,path,debug=False): pass a=A('rhubarb','pie',debug=True) self.assertTrue(a.foo=='rhubarb') self.assertTrue(a.path=='pie') self.assertTrue(a.debug==True) class B(object): @um.autoargs() def __init__(self,foo,path,debug=False,*args): pass a=B('rhubarb','pie',True, 100, 101) self.assertTrue(a.foo=='rhubarb') self.assertTrue(a.path=='pie') self.assertTrue(a.debug==True) self.assertTrue(a.args==(100,101)) class C(object): @um.autoargs() def __init__(self,foo,path,debug=False,*args,**kw): pass a=C('rhubarb','pie',True, 100, 101,verbose=True) self.assertTrue(a.foo=='rhubarb') self.assertTrue(a.path=='pie') self.assertTrue(a.debug==True) self.assertTrue(a.verbose==True) self.assertTrue(a.args==(100,101)) def test_autoargs_names(self): class C(object): @um.autoargs('bar','baz','verbose') def __init__(self,foo,bar,baz,verbose=False): pass a=C('rhubarb','pie',1) self.assertTrue(a.bar=='pie') self.assertTrue(a.baz==1) self.assertTrue(a.verbose==False) self.assertRaises(AttributeError,getattr,a,'foo') def test_autoargs_exclude(self): class C(object): @um.autoargs(exclude=('bar','baz','verbose')) def __init__(self,foo,bar,baz,verbose=False): pass a=C('rhubarb','pie',1) self.assertTrue(a.foo=='rhubarb') self.assertRaises(AttributeError,getattr,a,'bar') def test_defaults_none(self): class A(object): @um.autoargs() def __init__(self,foo,path,debug): pass a=A('rhubarb','pie',debug=True) self.assertTrue(a.foo=='rhubarb') self.assertTrue(a.path=='pie') self.assertTrue(a.debug==True) if __name__ == '__main__': unittest.main(argv = sys.argv + ['--verbose'])
PS. Using autoassign or autoargs compatible with IPython code completion.