Package: call-args
CallArgsCall functions and create objects by easily assigning values of keyword arguments from some object's attributes of or items from some dictionary.ContentUsageVariable keyword argumentsExtra argumentsExtending the package's functionalityUsageWhen calling a functionfwith keyword arguments contained in a dictionaryd, one can do something like this:result=f(a=d["a"],b=d["b"],...)or simply unpackd:result=f(**d)Similarly, using attributes of objectobj, one can doresult=f(a=obj.a,b=obj.b,...)or simply unpack d:result=f(**vars(d))In both of these cases, the first approach can get tedious if there are many arguments and/or when adding new ones, while the second approach is problematic if a dictionary/object has some items/attributes thatfdoes not accept (which is almost always the case with objects' attributes).Instead, the above calls can be done like this:fromcall_argsimportcall_args_dictresult=call_args_dict(f,d)orfromcall_argsimportcall_args_attrresult=call_args_attr(f,obj)This can be useful in cases when a dictionary or an object exists specifically for the use with the given callable. For example, when using with command-line arguments:parser=argparse.ArgumentParser(prog=splitext(basename(sys.argv[0]))[0],description=sys.modules[__name__].__doc__,)...args=parser.parse_args()call_args_attr(ClassThatDoesTheJob,args).run()Thecall_args_*functions filter out the values thatfwould not understand, as well as all the private ones (those with names starting with underscore_).Variable keyword argumentsIf the callable allows variable keyword arguments, then the whole source will be used. This means thatdeff(**kwargs):...result1=call_args_dict(f,d)result2=call_args_attr(f,obj)ismostlyequivalent todeff(**kwargs):...result1=f(**d)result2=f(**vars(obj))However, there is still a difference: even in this case,call_args_*functions will not assign values to private arguments, which means that all the items fromdand attributes fromobjwith names starting with an underscore_will be omitted.Also, in the case of an object - which might have methods - filtering of names is still useful, as those won't be unpacked (unless the callable expects attributes with exactly those names).Extra argumentsThe calls presented above can accept extra arguments. For example,deff(a=17,b=19,c=23):print(a,b,c)data={"b":"29","c":31,"d":37}call_args_dict(f,data,c=41)will print17 29 41because:ais not defined indatanor among keyword arguments ofcall_args_dict, so it retains its default value;bis defined only indata, getting its value 29 from there;cis defined indata, but also in keyword arguments ofcall_args_dict, which are prioritised and thus provide the value 41;dis defined indata, but it is not accepted byf, socall_args_dictsilently drops it.Note that explicitly given keyword arguments are always passed tof. This call:call_args_dict(f,{"b":"29","c":31,"d":37},c=41,e=43)raises aTypeErrorexception with a messageTypeError: f() got an unexpected keyword argument 'e'Extending the package's functionalityThe work here is done by the classCallArgs. Other "classes" (CallArgsAttrspecifically for working with attributes andCallArgsDictfor working with dictionaries) are dynamically generated, and they are not classes at all. They arepartialobjects.The two functions used above are also dynamically generated.So, in order to modify or extend the functionality presented above, one only needs to inheritCallArgs, and then recreate these two partials and two functions.Let us assume that we have a new classNewCallArgs, inherited fromCallArgs. These interfaces are created with one of the following calls:# All four together:CallArgsAttr,CallArgsDict,call_args_attr,call_args_dict=build_interfaces(NewCallArgs,)# Only "classes" (partials):CallArgsAttr,CallArgsDict=build_classes(NewCallArgs))# Only functions:call_args_attr,call_args_dict=build_functions(NewCallArgs)Thebuild_*functions accept extra keyword arguments that are passed on to
the class constructor, in order to easily modify the default behaviour.
Currently, there are two such arguments:call_args_attr2,call_args_dict2=build_functions(CallArgs,kwargs_as_default=True,skip_private=False,)Settingskip_privatetoFalsemeans that the functions will no longer filter out such attributes.Seetingkwargs_as_defaulttoTruechanges the meaning of keyword arguments in the newly created functions, so that they are treated as default values instead of overrides of the data. Let us reuse one of the above examples:deff(a=17,b=19,c=23):print(a,b,c)data={"b":"29","c":31,"d":37}# Default behaviour described above:call_args_dict(f,data,c=41)# 17 29 41# Modified behaviour where `c=41` serves as a default value:call_args_dict2(f,data,c=41)# 17 29 31  -> 31 comes from `data`data.pop("c")call_args_dict2(f,data,c=41)# 17 29 41