def resolve_arguments(self, args_and_kwargs, variables=None): """More Pythonic argument handling for interactive :class:`robottools.testrobot.keyword.Keyword` calls. Original ``resolve_arguments`` methods from ``robot.running.handlers`` expect as first argument a single list of Keyword arguments coming from an RFW script:: ['arg0', 'arg1', ..., 'name=value', ...] So there is no chance to pass unstringified named argument values. Only unstringified positional arguments are possible. This wrapper method takes a normal Python `args_and_kwargs` pair instead as first argument:: (arg0, arg1, ...), {name: value, ...} It resolves the named arguments stringified via the original method but returns the original Python values:: [arg0, arg1, ...], [(name, value), ...] Only strings are untouched. So RFW ``'...${variable}...'`` substitution still works. """ posargs, kwargs = args_and_kwargs rfwargs = list(posargs) # prepare 'key=value' strings for original RFW method for name, value in dictitems(kwargs): if not isstring(value): value = repr(value) rfwargs.append(u'%s=%s' % (name, value)) posargs, rfwkwargs = self._resolve_arguments(rfwargs, variables) # and replace values with original non-string objects after resolving kwargslist = [] if isdict(rfwkwargs): # ==> RFW < 3.0 rfwkwargs = dictitems(rfwkwargs) for name, rfwvalue in rfwkwargs: value = kwargs[name] if isstring(value): value = rfwvalue kwargslist.append((name, value)) if hasattr(self, 'run'): # ==> RFW < 3.0 return posargs, dict(kwargslist) # RFW >= 3.0 return posargs, kwargslist
def __new__(mcs, clsname=None, bases=None, clsattrs=None, **kwargs): metaattrs = {'__module__': clsattrs.get('__module__')} for name, obj in list(dictitems(clsattrs)): if ismetamethod(obj): metaattrs[name] = clsattrs.pop(name).func elif ismetaclassmethod(obj): metaattrs[name] = classmethod(clsattrs.pop(name).func) metabases = tuple(type(b) for b in bases if issubclass(b, object)) # is this `mcs` already under the bases' metaclasses? if not any(issubclass(mb, mcs) for mb in metabases): # no? ==> change that metabases = (mcs, ) + metabases # is there a user-defined inner `meta` options class? meta = clsattrs.pop('meta', None) if meta: # then prepend it as additional metaclass base metabases = (meta, ) + metabases # and now create the new metaclass for the new modeled class mcs = type(clsname + '.meta', metabases, metaattrs) # create basic modeled-class-specific exception class mcs.Exception = type('%s.Exception' % clsname, (Exception, ), {}) # delegate actual class creation to modeled.base.metabase cls = base.__new__(mcs, clsname, bases, clsattrs) if meta: # re-add user-defined inner meta options class # for later use in derived __new__ or __init__ methods clsattrs['meta'] = meta return cls
def method(self, *args, **kwargs): try: variables = BuiltIn()._variables except RobotNotRunningError: pass else: for key, value in list(dictitems(kwargs)): if is_scalar_var(key): kwargs[variables[key]] = kwargs.pop(key) return func(self, *args, **kwargs)
def test_dictitems(dict_): """Test the dictitems() function, which returns an iterator of a dictionary's (key, value) pairs and also works with simpledict() class instances. """ items = dictitems(dict_) # dictitems() should not return lists, only iterators assert not isinstance(items, list) # compare with reference sequence from dict method for (key, value), (refkey, refvalue) in zip(items, dict_.items()): assert key is refkey and refvalue is refvalue # and check that iterator is exhausted with pytest.raises(StopIteration): next(items)
def open_session(self, *args, **kwargs): """Open an unnamed {singular}. This keyword automatically closes any other currently active unnamed {singular}. """ previous = cls.session active = func(self, *args, **kwargs) cls.add_session(active) if previous is not None and close_func: # explicitly close previously active session if unnamed for name, session in dictitems(cls.sessions): if session is previous: break else: close_func(self, previous)
def close_session(cls, name=None): """Helper method for closing the currently active session. """ if name: try: session = cls.sessions.pop(name) except KeyError: raise cls.SessionError('Session not found: %s' % repr(name)) if session is cls.session: cls.session = None return session if cls.session is None: raise cls.SessionError('No active session.') for name, session in list(dictitems(cls.sessions)): if session is cls.session: del cls.sessions[name] session = cls.session cls.session = None return session
def switch_session(self, name): previous = cls.session active = cls.switch_session(name) if previous is not None and close_func: # explicitly close previously active session if unnamed for name, session in dictitems(cls.sessions): if session is previous: break else: try: close_func(self, previous) except Exception as exc: raise cls.SessionError( "Couldn't close unnamed session " "on switching to %s (%s: %s)" % (repr(name), qualname(type(exc)), exc)) if switch_func: try: switch_func(self, active) except Exception as exc: raise cls.SessionError( "Couldn't switch session to %s (%s: %s)" % (repr(name), qualname(type(exc)), exc)) cls.session = active
def __init__(cls, clsname, bases, clsattrs): """Generate the actual session management keywords for :class:`Handler` derived classes. - Includes the session management helper methods of :class:`Handler` and user-defined session opener methods (whose names start with 'open'). - Looks for optional custom session switch/close hook methods named 'switch'/'close' - All Keyword names include the handler-specific `meta.identifier_name`. """ try: meta = cls.meta except AttributeError: return open_funcs = [] switch_func = close_func = None for name, func in dictitems(clsattrs): if name.startswith('open'): open_funcs.append(func) elif name == 'switch': switch_func = func elif name == 'close': close_func = func for func in open_funcs: cls.add_opener(func, close_func=close_func) def switch_session(self, name): previous = cls.session active = cls.switch_session(name) if previous is not None and close_func: # explicitly close previously active session if unnamed for name, session in dictitems(cls.sessions): if session is previous: break else: try: close_func(self, previous) except Exception as exc: raise cls.SessionError( "Couldn't close unnamed session " "on switching to %s (%s: %s)" % (repr(name), qualname(type(exc)), exc)) if switch_func: try: switch_func(self, active) except Exception as exc: raise cls.SessionError( "Couldn't switch session to %s (%s: %s)" % (repr(name), qualname(type(exc)), exc)) cls.session = active keywordname = 'switch_' + meta.identifier_name cls.keywords[keywordname] = switch_session def close_session(self, name=None): session = cls.close_session(name) if close_func: close_func(self, session) keywordname = 'close_' + meta.identifier_name cls.keywords[keywordname] = close_session
def __call__(self, *args, **kwargs): """Call the Keyword's actual function with the given arguments. """ func = self.func # the exception to finally reraise (if any) error = None # look for explicit <session>= and <context>= switching options # in kwargs and store the currently active # session aliases and context names # for switching back after the Keyword call: current_sessions = {} for _, hcls in self.libinstance.session_handlers: if not hcls.meta.auto_explicit: continue identifier = hcls.meta.identifier_name plural_identifier = hcls.meta.plural_identifier_name try: sname = kwargs.pop(identifier) except KeyError: continue previous = getattr(self.libinstance, identifier) switch = getattr(self.libinstance, 'switch_' + identifier) try: switch(sname) except hcls.SessionError: error = sys.exc_info() # don't switch any more sessions break # store previous session for switching back later current_sessions[identifier, plural_identifier] = previous # only perform explicit context switching # if explicit session switching didn't raise any error current_contexts = {} if error is None: for hcls in self.context_handlers: if not getattr(hcls, 'auto_explicit', False): continue identifier = hcls.__name__.lower() try: ctxname = kwargs.pop(identifier) except KeyError: continue previous = getattr(self.libinstance, identifier) switch = getattr(self.libinstance, 'switch_' + identifier) try: switch(ctxname) except hcls.ContextError: error = sys.exc_info() # don't switch any more contexts break # store previous context for switching back later current_contexts[identifier] = previous # only call the acutal keyword func # if explicit session and context switching didn't raise any error if error is None: # Look for arg type specs: if func.argtypes: casted = [] for arg, argtype in zip(args, func.argtypes): if not isinstance(arg, argtype): arg = argtype(arg) casted.append(arg) args = tuple(casted) + args[len(func.argtypes):] # Look for context specific implementation of the Keyword function for context, context_func in dictitems(func.contexts): if context in self.libinstance.contexts: func = context_func # Does the keyword support **kwargs? if self.func.argspec.keywords or not kwargs: try: result = func(self.libinstance, *args, **kwargs) except Exception: error = sys.exc_info() else: # resolve **kwargs to positional args... posargs = [] # (argspec.args start index includes self) for name in self.func.argspec.args[1 + len(args):]: if name in kwargs: posargs.append(kwargs.pop(name)) # and turn the rest into *varargs in 'key=value' style varargs = [ '%s=%s' % (key, kwargs.pop(key)) for key in list(kwargs) if key not in self.func.argspec.args ] try: result = func( self.libinstance, *chain(args, posargs, varargs), # if **kwargs left ==> TypeError from Python **kwargs) except: error = sys.exc_info() # finally try to switch back contexts and sessions (in reverse order) # before either returning result or reraising any error catched above for identifier, ctxname in dictitems(current_contexts): switch = getattr(self.libinstance, 'switch_' + identifier) # don't catch anything here. just step out on error switch(ctxname) for (identifier, plural_identifier), session in dictitems(current_sessions): for sname, sinstance in dictitems( getattr(self.libinstance, plural_identifier)): if sinstance is session: switch = getattr(self.libinstance, 'switch_' + identifier) # don't catch anything here. just step out on error switch(sname) # was an error catched on initial session or context switching # or on calling the actual keyword func? if error is not None: reraise(*error) # great! everything went fine :) return result
def __call__(self, *args, **kwargs): """Call the Keyword's actual function with the given arguments. """ func = self.func # the exception to finally reraise (if any) error = None # look for explicit <session>= and <context>= switching options # in kwargs and store the currently active # session aliases and context names # for switching back after the Keyword call: current_sessions = {} for _, hcls in self.libinstance.session_handlers: if not hcls.meta.auto_explicit: continue identifier = hcls.meta.identifier_name plural_identifier = hcls.meta.plural_identifier_name try: sname = kwargs.pop(identifier) except KeyError: continue previous = getattr(self.libinstance, identifier) switch = getattr(self.libinstance, 'switch_' + identifier) try: switch(sname) except hcls.SessionError: error = sys.exc_info() # don't switch any more sessions break # store previous session for switching back later current_sessions[identifier, plural_identifier] = previous # only perform explicit context switching # if explicit session switching didn't raise any error current_contexts = {} if error is None: for hcls in self.context_handlers: if not getattr(hcls, 'auto_explicit', False): continue identifier = hcls.__name__.lower() try: ctxname = kwargs.pop(identifier) except KeyError: continue previous = getattr(self.libinstance, identifier) switch = getattr(self.libinstance, 'switch_' + identifier) try: switch(ctxname) except hcls.ContextError: error = sys.exc_info() # don't switch any more contexts break # store previous context for switching back later current_contexts[identifier] = previous # only call the acutal keyword func # if explicit session and context switching didn't raise any error if error is None: # Look for arg type specs: if func.argtypes: casted = [] for arg, argtype in zip(args, func.argtypes): if not isinstance(arg, argtype): arg = argtype(arg) casted.append(arg) args = tuple(casted) + args[len(func.argtypes):] # Look for context specific implementation of the Keyword function for context, context_func in dictitems(func.contexts): if context in self.libinstance.contexts: func = context_func # Does the keyword support **kwargs? if self.func.argspec.keywords or not kwargs: try: result = func(self.libinstance, *args, **kwargs) except Exception: error = sys.exc_info() else: # resolve **kwargs to positional args... posargs = [] # (argspec.args start index includes self) for name in self.func.argspec.args[1 + len(args):]: if name in kwargs: posargs.append(kwargs.pop(name)) # and turn the rest into *varargs in 'key=value' style varargs = ['%s=%s' % (key, kwargs.pop(key)) for key in list(kwargs) if key not in self.func.argspec.args] try: result = func(self.libinstance, *chain(args, posargs, varargs), # if **kwargs left ==> TypeError from Python **kwargs) except: error = sys.exc_info() # finally try to switch back contexts and sessions (in reverse order) # before either returning result or reraising any error catched above for identifier, ctxname in dictitems(current_contexts): switch = getattr(self.libinstance, 'switch_' + identifier) # don't catch anything here. just step out on error switch(ctxname) for (identifier, plural_identifier), session in dictitems( current_sessions ): for sname, sinstance in dictitems(getattr( self.libinstance, plural_identifier )): if sinstance is session: switch = getattr(self.libinstance, 'switch_' + identifier) # don't catch anything here. just step out on error switch(sname) # was an error catched on initial session or context switching # or on calling the actual keyword func? if error is not None: reraise(*error) # great! everything went fine :) return result