Is there a way to have embedded arguments, and pass kwargs to the function?
For example:
Robot file
Testcase1
do something "value" extra_args=bla
Python library
#keyword('do something "${value}"')
def do_something(self, value, **kwargs):
print(value)
print(kwargs)
Though the above way does not work. I've also tried with
Testcase1
do something "value" extra_args=bla
Also
*** Variables ***
&{DICT} extra_args=bla
Testcase1
do something "value" &{DICT}
And many other combinations. I've found pull request in Robotframework where this limitation was added, but I am sure other Library writers must want this feature.
Thanks
Short answer - no, that is not allowed; doc link, the last paragraph in Basic Syntax.
Thinking about it, I can see where does this limitation come from - if kwargs were supported in keywords with embedded arguments, the parser would have very hard time, mainly in two regards:
Where to break up the arguments as separate variables?
In this sample code:
My keyword #{kwarg} varies
Pass Execution
, when called like this:
My keyword was called varies
, what should the arguments be - one with value was called, or two - was and called?
Matching the target keyword
Another sample keywords definition:
My keyword #{kwarg}
Pass Execution
My keyword ${normal} argument
Pass Execution
, when called like this:
My keyword one argument
, which of the two keywords should the parser match?
As you can see kwargs in the embedded syntax would cause problems, and this is probably only the tip of the iceberg.
Side note - though I personally find keywords with embedded arguments mega cool - you can have calls that look like plain English! almost no other language gives you this possibility!, with practice I have found them limiting, and now try to avoid them.
Two reasons - you cannot change the signature of such keyword in the future, e.g. add optional arguments (or kwargs ;)). The second is the arguments are always passed on as string objects - so you'll have to cast them in the keyword if it deals with another type, and forget about ever passing complex object types.
In the Robot Framework documentation this is described in section Free keyword arguments (**kwargs).
Robot Framework 2.8 added the support for free keyword arguments using
Python's **kwargs syntax. How to use the syntax in the test data is
discussed in Free keyword arguments section under Creating test cases.
In this section we take a look at how to actually use it in custom
test libraries.
This is the Python example:
def various_args(arg, *varargs, **kwargs):
print 'arg:', arg
for value in varargs:
print 'vararg:', value
for name, value in sorted(kwargs.items()):
print 'kwarg:', name, value
and the corresponding Robot File:
*** Test Cases ***
Positional
Various Args hello world # Logs 'arg: hello' and 'vararg: world'.
Named
Various Args arg=value # Logs 'arg: value'.
Kwargs
Various Args a=1 b=2 c=3 # Logs 'kwarg: a 1', 'kwarg: b 2' and 'kwarg: c 3'.
Various Args c=3 a=1 b=2 # Same as above. Order does not matter.
Positional and kwargs
Various Args 1 2 kw=3 # Logs 'arg: 1', 'vararg: 2' and 'kwarg: kw 3'.
Named and kwargs
Various Args arg=value hello=world # Logs 'arg: value' and 'kwarg: hello world'.
Various Args hello=world arg=value # Same as above. Order does not matter.
Related
I have a function that performs a specific task, and this function takes many options, both named and unnamed options. For example:
def eat_fruit(x,a_number=None , a_fruit=None):
return(f'{x} ate {str(a_number)} {a_fruit}')
#call the function
eat_fruit("John",a_number=5,a_fruit='apples') #outputs 'John ate 5 apples'
Now, I have another function that takes many options, for example:
def do_lots_of_stuff(a,b,activity_1=None,activity_2=None):
return(f'''{a} and {b} {activity_1} and {activity_2}''')
do_lots_of_stuff("Bob","Mary",activity_1='run',activity_2='jump') #returns "Bob and Mary run and jump"
I want to have the function do_lots_of_stuff call the function eat_fruit, sometimes with options. However, it is not clear to me how to pass options from one to the other in a straightforward manner.
Ideally, I am looking for something like:
#This code does not work; it demos the type of functionality I am looking for.
do_lots_of_stuff("Bob","Mary",activity_1='run',activity_2='jump', eat_fruit_options={*put_options_here*}):
eat_fruit(eat_fruit_options)
return(f'''{a} and {b} {activity_1} and {activity_2}''')
Note that this can't be accomplished via do_lots_of_stuff(*do_lots_of_stuff_options*, *eat_fruit_options*) since options for eat_fruit are not valid do_lots_of_stuff options. Furthermore, keyword arguments must come after positional arguments. In addition this solution does not seem to be sufficient here, because I only want to pass some arguments, not all of them.
Other relevant links (although I don't believe they successfully address my question):
can I pass all positional arguments from one function to another in python?
Passing variables between functions in Python
Passing value from one function to another in Python
do_lots_of_stuff("Bob","Mary",activity_1='run',activity_2='jump', eat_fruit_args=["John"], eat_fruit_kwargs={"a_number": 5, "a_fruit": "apples"}):
eat_fruit(*eat_fruit_args, **eat_fruit_kwargs)
return(f'''{a} and {b} {activity_1} and {activity_2}''')
You can pass and forward arguments and keyword arguments. Arguments are in the form of a list. Keyword arguments (kwargs) are in the form of a dictionary, with the key as a string, and the value as the correlating keyword value.
I am trying to create a dictionary of class names residing in module to their constructor args.
Constructor args should also be a dictionary where I will store the default values of the arguments wherever defined.
Any leads will be really helpful. Thanks in advance.
To provide more details about the use case, What I am trying to do here is for all the classes mentioned in the image image
I want to get the constructor parameters for e.g. please refer below image
image
If I understand you correctly, you just want the name of the parameters in the signature of your __init__.
That is actually quite simple using the inspect module:
Modern python answer:
import inspect
signature = inspect.signature(your_class.__init__).parameters
for name, parameter in signature.items():
print(name, parameter.default, parameter.annotation, parameter.kind)
Outdated answer
import inspect
signature = inspect.getargspec(your_class.__init__)
signature.args # All arguments explicitly named in the __init__ function
signature.defaults # Tuple containing all default arguments
signature.varargs # Name of the parameter that can take *args (or None)
signature.keywords # Name of the parameter that can take **kwargs (or None)
You can map the default arguments to the corresponding argument names like this:
argument_defaults = zip(signature.args[::-1], signature.defaults[::-1])
Most recently, the following works:
import inspect
signature = inspect.signature(yourclass.__init__)
for param in signature.parameters.values():
print(param)
The difference being (compared to the accepted answer), that the parameters instance variable needs to be accessed.
I want to make a Builder with one or more optional sources.
I tried this:
env.Append(BUILDERS = {'my_builder': Builder(action = Action(do_something))})
def do_something(target, source, env):
if source[1]:
do_optional_stuff(source[1])
do_other_stuff(target, source[0])
...
env.my_builder(target.txt, [source1, None]) # Fails
env.my_builder(target.txt, [source2, source3]) # Okay
The trouble is, I get 'NoneType' object has no attribute 'get_ninfo' when I pass in None, because scons is expecting Node arguments, and None isn't acceptable.
Is there anything I can do?
Edit:
As noted in the answer below, it's possible to solve this for the simple case of one optional argument, by varying the length of source list. This doesn't work for making arbitrary arguments optional, so I'd still be interested in a way to do that.
Instead of adding a bogus element, check the length of the source list (or better yet, iterate over the list starting after the first element):
def do_something(target, source, env):
if len(source) > 1:
do_optional_stuff(source[1])
# or:
# for opt_src in source[1:]:
# do_optional_stuff(opt_src)
do_main_stuff(target, source[0])
env.Append(BUILDERS = {'my_builder': Builder(action = Action(do_something))})
env.my_builder('target-2.txt', ['foo.txt'])
env.my_builder('target-1.txt', ['foo.txt', 'bar.txt'])
One issue with this approach is that you need to ensure that your sources are listed in the right order. Depending on the details of what you're doing, you might be able to filter the source list by matching file names or extensions. After all, this is Python code, you have the full power of the language at your disposal.
In my command line program, I want to add an argument --list-addons that just prints the available addons in the program and exits (similar to what --version or --help in many programs usually do).
python main.py --list-addons
addon1
addon2
addon3
How can I do this while at the same time having a lot of other arguments defined? The --list-addons option cannot be used in conjunction with any other arguments.
Look in the docs about writing a custom Action class. Here's a simplification of the _VersionAction class, which displays a message and exits.
class ListAction(Action):
# omit the custom __init__
def __call__(self, parser, namespace, values, option_string=None):
message = <format your list>
parser.exit(message=message)
In this question
argparse: require either of two arguments
I find a reference to the solution I want, but it isn't right.
I need at least 1 of 2 options to be present, option1, option2 or both...
The add_argument_group function doesn't have a required argument.
The add_mutually_exclusive function has it, but it forces me to choose between the 2 options, which is not what I want.
rds,
argument_group just controls the help display. It does not affect the parsing or check for errors. mutually_exclusive_group affects usage display and tests for occurrence, but as you note, its logic is not what you want.
There is a Python bug issue requesting some form of nested 'inclusive' group. But a general form that allows nesting and all versions of and/or/xor logic is not a trivial addition.
I think your simplest solution is to test the args after parsing. If there is a wrong mix of defaults, then raise an error.
Assuming the default for both arguments is None:
if args.option1 is None and args.option2 is None:
parser.error('at least one of option1 and option2 is required')
What would be meaningful usage line? required mutually exclusive' uses(opt1 | opt2).(opt1 & opt2)might indicate that both are required. Your case is anon-exclusive or`
usage: PROG [-h] (--opt1 OPT1 ? --opt2 OPT2)