Open Tset-Noitamotua opened 9 years ago
Since you are using positional parameters (the position in the list defines its meaning), you can simply check the length of args: if len(args) < 4: timeout = 5 else: timeout = args[3]
Another possibility is to use a combination of fixed positional, variable positional and variable keywordargs for robustness (existence of fixed positionals are checked by the interpreter). a nice post: https://freepythontips.wordpress.com/2013/08/04/args-and-kwargs-in-python-explained/
Generally: when using these techniques, you should always first check the given parameters and assign the anonymous parameters to variables with speaking names
something like this to make it flexible and robust allows, to change only the parameter handling later, without touching the rest
def do(*args):
# check for valid parameters
argsLen = len(args)
if argsLen == 0 or argsLen < 3 or not args[2]:
raise "do: wrong parameters"
# store given parameters
actionName = args[0]
clickTarget = args[1]
postCondition = arg[2]
if argsLen < 4: timeout = 5
else: timeout = args[3]
# log the given parameters
print "do: %s(%s)" + ("" if not postCondition else "and check with exists(%s, %f)" %
(actionName, clickTarget, postCondition, timeout)
# eval action click (default), double-click or rightclick
if actionName.startsWith('d'):
action = doubleClick
actionName = "doubleClick"
elif actionName .startsWith('r'):
action = rightClick
actionName = "doubleClick"
else:
action = click # default
actionName = "click"
# check clickTarget (Image or Pattern), can not be None (checked before)
if not isinstance(clickTarget, string) and not is instance(clickTarget, Pattern):
raise "do: clickTarget must be String or Pattern"
# check postCondition (Image or Pattern), might be None
if postCondition and not isinstance(postCondition, string) and not is instance(postCondition, Pattern):
raise "do: postCondition must be String or Pattern"
# perform action e.g.
# click clickTarget
action(clickTarget)
if not postCondition:
return
# check postCondition until timeout
if not exists(postCondition, timeout):
print "FAIL! Post condition not met!"
Thank you very much, Raiman!
After nearly two week of diving in _args & *_kwargs I have the following which is working so far:
I have a class with a __init__()
funciton which holds all parameters I need for my actions:
def __init__(self, **kwargs):
self.timeout = kwargs.setdefault('timeout', 5.0)
self.action = kwargs.setdefault('action', 'click')
self.click_target = kwargs.setdefault('click_target', None)
self.post_condition = kwargs.setdefault('post_condition', None)
self.similarity = kwargs.setdefault('similarity', 0.7)
Settings.MinSimilarity = self.similarity
self.reg = Region(Screen())
Further I have some functions for the different types of actions the user can do, like click, double click etc. The function names also act as keyword names for the Robot Framework. For the moment they don´t do more than taking the given parameters which are passed as arguments from Robot Framework keywords. The given arguments are then handled by my do()
function. (But this is a design question for know - maybe I will get rid of the do()
function in the future.)
The different action functions look like this:
def click(self, *args, **kwargs):
"""
Left mouse click on a click target (e.g. gui element)
"""
self.action = 'click'
# pass given arguments to -> do() function
self.do(*args, **kwargs)
A custom Robot Framework keyword then can look like this:
*** Keywords ***
My Custom Keyword
click target.png
click target.png post-condition.png
click target.png post-condition.png timeout=10.0
click target.png post-conditon.png similarity=0.5
click target.png post-condition.png timeout=11.0 similarity=0.55
Another Custom Keyword
type textstring
type textstring post-condition.png # optional timeout=11 similarity=0.5
...
The do()
function handles the given arguments:
def do(self, *args, **kwargs):
"""
Interacts with gui elements and checks condition after action.
"""
# Check for valid arguments - - - - - - - - - - - - - - - - - - - - - - - - - - - -
if len(args) == 0 or not args[0]:
raise ValueError('At least one argument (click target) is required')
# If only one argument given
elif len(args) < 2:
# Store it with meaningful name/key
self.click_target = args[0]
# Check click_target, can not be None.
if not isinstance(self.click_target, basestring) and \
not isinstance(self.click_target, Pattern):
raise ValueError('First argument (click target) must be \
String or Pattern!')
# If more than one argument given
else:
# Store given arguments with meaningful names/keys
self.click_target = args[0]
self.post_condition = args[1]
# Check click_target argument, can not be None.
if not isinstance(self.click_target, basestring) and \
not isinstance(self.click_target, Pattern):
raise ValueError('First argument (click target) must be \
String or Pattern!')
# check post_condition argument, might be None
if not isinstance(self.post_condition, basestring) and \
not isinstance(self.post_condition, Pattern):
raise ValueError('Second argument (post_condition) must be \
String or Patter!')
# first eval action then perform it - - - - - - - - - - - - - - - - - - - - - - - - -
if self.action == 'double_click':
pass # TODO
elif self.action == 'context_click':
action = self.reg.rightClick
# perform context_click action
action(self.click_target)
# check post_condition until timeout
self.check_post_condition(*args, **kwargs)
# restore default region
self.reg = Screen()
elif self.action == 'click':
action = self.reg.click
# perform click action
action(self.click_target)
# check post condition until timeout
self.check_post_condition(*args, **kwargs)
# restore default region
self.reg = Screen()
elif self.action == 'type':
action = self.reg.type
# perform action type
action(self.click_target)
# check post condition until timeout
self.check_post_condition(*args, **kwargs)
# restore default region
self.reg = Screen()
else:
# befor this can happen RF will throw a 'unknown keyword' error
raise ValueError('Unknown action "%s"!' % self.action)
Post condition after each action is checked in a seperate function:
def check_post_condition(self, *args, **kwargs):
self.timeout = kwargs.get('timeout', 5.0)
# change MinSimilarity if necessary
self.similarity = kwargs.get('similarity', 0.7)
Settings.MinSimilarity = float(self.similarity)
# log given arguments
self.log_arguments('CHECK_POST_CONDITION', *args, **kwargs)
if not self.post_condition:
self.log.passed('Action successfully performed!')
else:
# check post condition until timeout
if not exists(self.post_condition, float(self.timeout)):
self.log.failed('Post condition (%s) NOT MET!' % self.post_condition)
else:
self.log.passed('Expected post condition (%s) met.' % self.post_condition)
# restore default post_condition
self.post_condition = kwargs.get('post_condition', None)
# restore default similarity
Settings.MinSimilarity = 0.7
self.similarity = Settings.MinSimilarity
For logging the given arguments I use a seperate function:
def log_arguments(self, in_func, *args, **kwargs):
""" - - - - - - - - - - - - - - - - - - - - - -
Logs all arguments that are passed from RF
- - - - - - - - - - - - - - - - - - - - - - - -
"""
# log given *args
print "*args in '%s()':" % in_func
if args is not None:
i = 0
for arg in args:
print "- args[%i] = %s" % (i, arg)
i += 1
print '\n'
# log given **kwargs
if kwargs is not None:
print "**kwargs in '%s()': " % in_func
for key, value in kwargs.iteritems():
print "- kwargs: %s = %s" % (key, value)
print '\n'
# log __init__ vars
print "__init__(vars) in '%s()':" % in_func
print '- self.timeout:\t\t\t', self.timeout
print '- self.action:\t\t\t', self.action
print '- self.click_target:\t\t', self.click_target
print '- self.post_condition:\t\t', self.post_condition
print '- self.similarity:\t\t', self.similarity
print '- self.press_key:\t\t', self.press_key
print '\n'
Combined with Mikes´s tutorial this can become a realy cool Robot Framework Sikuli Library ;-)))
Interesting - looks good.
I just made a similar approach (varargs) to support running scripts and snippets written in JavaScript (the engine is contained in Java, so no external interpreter is needed).
This will become an additional generic API in version 2, to access the SikuliX features from anywhere.
keep up with your approach. welcome for any help you need.
This is not a SikuliX specific question - just a programming question of a beginner :)
I have a function / method like this:
And I call it with that:
Now my question: How can I set timeout argument
agrs[3]
to a default value (of e.g. 5 seconds) so that I don´t have to write it each time I call mydo
function?