Skip to content

Instantly share code, notes, and snippets.

@aule
Created November 16, 2016 00:47
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aule/aaf9aed8e405de72da1f3867d0b1f770 to your computer and use it in GitHub Desktop.
Save aule/aaf9aed8e405de72da1f3867d0b1f770 to your computer and use it in GitHub Desktop.
Object Oriented Robot Wrapper
"""Tools for allowing Robot Framework to interact with Python objects."""
import inspect
class RobotWrapper(object):
"""Exposes all methods of a wrapped Robot library as functions.
Usually, instance methods are bound to a class instance, so that the first
parameter (`self`) is automatically passed in. This wrapper is a hack that
"unbinds" instance methods, making them normal functions:
>>> class Spam(object):
... def __init__(self, ham, eggs):
... pass
... @classmethod
... def create_spam(cls, ham, eggs): # normal keyword
... return cls(ham, eggs)
... def foo(self, bar): # method which will be unbound
... print(bar)
...
>>> instance = Spam('ham', 'eggs')
>>> instance.foo('baz')
baz
>>> foo = instance.foo.__func__ # manually unbind
>>> foo(instance, 'baz')
baz
Why is this interesting? Usually, if a Robot library is a class, then it is
initialised when it is imported into a Robot test suite, i.e. at the start.
This way, when imported into Robot, these unbound functions become keywords
that take an instance as a parameter. Now objects can easily and
dynamically be used inside a Robot test suite, provided there is e.g. a
static/class factory method or the object is returned by another keyword:
*** Keywords ***
Do Foo
${instance} = Create Spam ${ham} ${eggs}
Foo ${instance} ${bar}
In the above example the instance came from a class method which was
imported as an ordinary keyword.
---
To wrap a library, create a wrapper which inherits from this class and set
the `wrapped_class` attribute to the library, then import that wrapper in
the test suite instead of the library. Additional keywords (e.g. factory
methods) can be added to the wrapping class as methods.
"""
wrapped_class = NotImplemented
def __init__(self):
"""Unbind and add all functions from wrapped class to the instance."""
# Only callable members need converting into Robot keywords. All static
# members can be accessed with ${instance.someproperty} syntax
members = inspect.getmembers(self.wrapped_class, predicate=callable)
for name, member in members:
# skip private methods
if name.startswith('_'):
continue
# do we have an instance method?
# if it's a class method, the bound instance is already set
# if it's an instance method, the bound instance is None
if inspect.ismethod(member) and member.__self__ is None:
# hack around robot stripping `self` off instance methods by
# using the "raw" function instead of one bound to the instance
member = member.__func__
# Set the imported functions as attributes so Robot can grab them
setattr(self, name, member)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment