Check valid arguments

So, let's define some functions:

def x(a, b, c): pass def y(a, b=1, c=2): pass def z(a=1, b=2, c=3): pass 

Now, what's the best way, by pointing to a pointer to x , y or z ( p ), a tuple args ( a ) and a dictionary kwargs ( k ), check if there is

 p(*a, **kw) 

will throw any exception due to the lack of sufficient arguments or incorrect arguments, etc. - without actually calling p(*a, **kw) , and then to detect exceptions.

Example

 def valid(ptr, args, kwargs): ... #implementation here valid(x, ("hello", "goodbye", "what?"), {}) # => True valid(x, ("hello", "goodbye"), {}) # => False valid(y, ("hello", "goodbye", "what?"), {}) # => True valid(y, (), {"a": "hello", "b": "goodbye", "c": "what"}) #=> True valid(y, ("hello", "goodbye"), {"c": "what?"}) #=> True 
+6
python
source share
3 answers

The best way I can come up with for this is to create a new function with the same signatures and default values ​​and try to call this and catch the exception. The body of the new function code should be pass so that there are no side effects. There is nothing stupid here, the code is simply tiresome. It is worth noting that this connects you to internal CPython. It works on 2.6. You will have to port it to other versions, but it should not be too complicated.

 import types ARGS_FLAG = 4 #If memory serves, this can be found in code.h in the Python source. KWARGS_FLAG = 8 def valid(f, args, kwargs): def dummy(): pass dummy_code = dummy.func_code real_code = f.func_code args_flag = real_code.co_flags & ARGS_FLAG kwargs_flag = real_code.co_flags & KWARGS_FLAG # help(types.CodeType) for details test_code = types.CodeType(real_code.co_argcount, real_code.co_nlocals, dummy_code.co_stacksize, args_flag | kwargs_flag, dummy_code.co_code, dummy_code.co_consts, dummy_code.co_names, real_code.co_varnames, "<test>", "", 0, "", ()) # help(types.FunctionType) for details test_func = types.FunctionType(test_code, {}, "test", f.func_defaults) try: test_func(*args, **kwargs) except TypeError: return False else: return True def x(a, b, c): pass def y(a, b=1, c=2): pass def z(a=1, b=2, c=3): pass print valid(x, ("hello", "goodbye", "what?"), {}) # => True print valid(x, ("hello", "goodbye"), {}) # => False print valid(y, ("hello", "goodbye", "what?"), {}) # => True print valid(y, (), {"a": "hello", "b": "goodbye", "c": "what"}) #=> True print valid(y, ("hello", "goodbye"), {"c": "what?"}) #=> True 

Running this code gives:

 $ python argspec.py True False True True True 
+1
source share

You can use the inspect module getargspec function to determine the argument functions and their default values, if any, and also if the function accepts varargs or keywordargs. This should be enough information to determine if a given tuple will satisfy the requirements of the argument.

Here is the beginning (you need to start, but I decided that the start of the publication was better than nothing).

 def valid(f, *args, **kwargs): specs = getargspec(f) required = specs.args[:-len(specs.defaults)] if (specs.defaults != None) else specs.args #Now just check that your required arguments list is fulfilled by args and kwargs #And check that no args are left over, unless the argspec has varargs or kwargs defined. 
+4
source share

See this signature type verification decoder for this method:

Python 3: http://code.activestate.com/recipes/572161/
Python 2: same site /.../recipes/426123/

Python 3 is easier to use because it uses annotations for type specifications that are very intuitive:

 @typecheck def foo(a1: int, a2: str, a3: dict, *, kw1: bool) -> list: ... @typecheck def bar(el: list_of(str), stream: with_attr("write", "flush"), num: by_regex("^[0-9]+$"), f: optional(callable) = None): ... @typecheck def biz(p: lambda x: isinstance(x, int) and x % 3 == 0): ... 

and etc.

The Python 2 version is not so pretty, but still usable:

 @takes(int, str, dict, kw1 = bool) @returns(list) def foo(a1, a2, a3, **kwargs): ... 
0
source share