Robot Framework keywords and Inheritance - python

I have a library of keywords. I have a few classes and subclasses, but I'm having an issue with inheritance and keywords being double-defined. For example:
MyLib.py
class Class1:
def __init__(self):
pass
def do_something_generic(self):
#do stuff that is generic to all subclasses
pass
class Subclass1(Class1):
def __init__(self):
pass
def do_something_specific_to_subclass1(self):
#something specific
pass
class Subclass2(Class1):
def __init__(self):
pass
def do_something_specific_to_subclass2(self):
#something specific
pass
The specific keywords work fine, but when I try to call Do Something Generic I get Multiple keywords with name 'Do Something Generic' found. I can fully qualify the library name with MyLib.Class1.Do Something Generic, but is there any way to define Do Something Generic to always refer to the superclass, since the method is only defined there and is simply inherited by the subclasses?

From Robot Framework User Guide
When the static library API is used, Robot Framework uses reflection to find out what public methods the library class or module implements.
It will exclude all methods starting with an underscore,
and with Java libraries also methods that are implemented only in java.lang.Object are ignored.
All the methods that are not ignored are considered keywords.
Have you considered adding helper base class with a _do_something_generic function? You can exclude it from __all__ list. Then use the inheritance to expose keywords from the base class in Class1.
MyLibrary.py:
__all__ = ['Class1', 'Subclass1', 'Subclass2']
class BaseClass:
def _do_something_generic(self):
pass
class Class1(BaseClass):
def __init__(self):
pass
def do_something_generic(self):
return self._do_something_generic()
class Subclass1(BaseClass):
def __init__(self):
pass
def do_something_specific_to_subclass1(self):
a = self._do_something_generic()
return (a, 3)
class Subclass2(BaseClass):
def __init__(self):
pass
def do_something_specific_to_subclass2(self):
#something specific
pass

I think the best solution is to simply move do_something_generic to a separate class, so that your base class only has helper functions and no public keywords:
class Class1:
def __init__(self):
pass
class Subclass0(Class1):
def do_something_generic(self):
#do stuff that is generic to all subclasses
pass
While it might be possible to solve this with something exotic like using __slots__ or __getattr__, or modifying self.__dict__, it's probably not worth the trouble.

Related

Extending a class in Python inside a decorator

I am using a decorator to extend certain classes and add some functionality to them, something like the following:
def useful_stuff(cls):
class LocalClass(cls):
def better_foo(self):
print('better foo')
return LocalClass
#useful_stuff
class MyClass:
def foo(self):
print('foo')
Unfortunaltely, MyClass is no longer pickleable due to the non global LocalClass
AttributeError: Can't pickle local object 'useful_stuff.<locals>.LocalClass'
I need to pickle my classes. Can you recommend a better design?
Considering that there can be multiple decorators on a class, would switching to multiple inheritance by having MyClass inherit all the functionality be a better option?
You need to set the metadata so the subclass looks like the original:
def deco(cls):
class SubClass(cls):
...
SubClass.__name__ = cls.__name__
SubClass.__qualname__ = cls.__qualname__
SubClass.__module__ = cls.__module__
return SubClass
Classes are pickled by using their module and qualname to record where to find the class. Your class needs to be found in the same location the original class would have been if it hadn't been decorated, so pickle needs to see the same module and qualname. This is similar to what funcutils.wraps does for decorated functions.
However, it would probably be simpler and less bug-prone to instead add the new methods directly to the original class instead of creating a subclass:
def better_foo(self):
print('better_foo')
def useful_stuff(cls):
cls.better_foo = better_foo
return cls

How to pass arguments to overridden functions with different interface?

The situation is: An abstract base class defines a set of common actions, while leaving a bunch of specialized actions to subclasses.
from abc import ABC,abstractmethod
class Common(ABC):
def __init__(self,ord_obj):
self.ord_obj = ord_obj
self.abs_obj = self.generate_abs_obj(?)
#abstractmethod
def generate_abs_obj(self):
# Will be implemented in subclasses
pass
def common_operation(self):
return self.ord_obj + self.abs_obj
class specialize_1(Common):
def __init__(self):
super().__init__()
def generate_abs_obj(self,param_1):
# do something
return 25
class specialize_2(Common):
def __init__(self):
super().__init__()
def generate_abs_obj(self,param_1,param_2):
# do something
return param_1+param_2
As shown above, abs_obj is an object generated by an abstract method and initialized in base class __init__. It will be used in the base class by an ordinary method. However, the generate_abs_obj in subclasses have different interfaces. How do I call super().__init__() with different parameters?
The overall approach that you are using seems to be entirely not-pythonic. I highly recommend watching Raymond Hettinger explain Super and Pythonic inheritance in this talk

Multilevel abstraction with interface and inheritance in Python

I'm not exactly sure how to phrase this question, hence the strange title. I also have not been able to find any information on this after searching, so hopefully this isn't a duplicate and I'm just searching for the wrong words. Anyhow, here is the situation, I have an abstract base class with some methods in it, which is inherited by a class. I don't want to set one of the methods in this base class, as this class is meant to be inherited by other classes to provide the common functionality they all share. Something like:
class A(metaclass=abc.ABCMeta):
#abc.abstractmethod
def fun1(self):
pass
#abc.abstractmethod
def fun2(self):
pass
class B(A):
def fun1(self):
#do work here
#abc.abstractmethod
def fun2(self): # Intent to have the final classes define this
pass
class C(B):
def fun2(self):
# do work here
class D(B):
def fun2(self):
# do work here
I would like to keep the function as an ABC.meta to force implementation on the final children, but because there can be multiple types of class B in this case all inheriting from the interface, I want to keep the initial virtulization of the method at this root class, but have a way for class B to enforce that it's sub-classes must implement this. The code works just find if I don't add the abstract method to class B, but that is awkward since subclassess must implement the method and shouldn't have to look all the way up to the interface to figure out everything they need to implement. As written, it will error out because class B cannot declare the method as an abc.abstract. If I don't declare it as an abstract there is no way to enforce the child class has to implement the method.
I hope my convoluted way of writing this makes sense to someone out there...
Thanks!
You probably should not redefine fun2 as an abstract method in the concrete class B. You are creating a set of rules for your interface, but immediately violating them when you do that.
Instead, either define a mix-in class or an additional ABC that C and D can inherit.
class A(metaclass=abc.ABCMeta):
#abc.abstractmethod
def fun1(self):
pass
class A2(metaclass=abc.ABCMeta):
#abc.abstractmethod
def fun2(self):
pass
class B(A):
def fun1(self):
print('hello')
class B2(A2):
def fun2(self):
print('world')
class C(B, B2):
pass
class D(B, B2):
pass

Extending a class hierarchy in Python

I have a class hierarchy in a module that I want to extend.
The module to be extended looks something like this.
Module foo:
class bar(object): pass
class spam(bar): pass
class eggs(bar): pass
Now I want to extend these classes:
class my_bar(foo.bar):
def new_func(): pass
class my_spam(foo.spam): pass
class my_eggs(foo.eggs): pass
Doing so, a new function new_func() in my_bar would not be available in a my_spam instance using
my_spam_instance.new_func()
What is the best ("most pythonic") way to achieve this? I thought of multiple inheritance, like this:
class my_bar(foo.bar): pass
class my_spam(foo.bar, my_bar): pass
class my_eggs(foo.eggs, my_bar): pass
Though I never really used it before and I am not sure this is the best way.
You don't even need to inherit my_bar from bar
pythonic will be adding Mixin, which is actually a base class, but not inherited
class NewFuncMixin():
def new_func(): pass
And add it to new classes
class my_bar(foo.bar, NewFuncMixin): pass
class my_spam(foo.spam, NewFuncMixin): pass
class my_eggs(foo.eggs, NewFuncMixin): pass
What about a mixin class? The pattern is
class My_mixin( object):
def new_func(self): pass
class My_spam( My_mixin, foo.spam): pass
class My_eggs( My_mixin, foo.eggs): pass
mixins should inherit from object, and go on the left of the inheritance list so that the mixin class methods get name priority. Within the mixin you can then wrap any method of the superclass:
class My_mixin( object):
def bar_method( self):
# stuff
bar_result = super( My_mixin, self).bar_method()
# more stuff
return bar_result # or my_result based on bar_result
You can of course completely override the method instead of wrapping it.

Python constructors in ABC inheritance chain for interfaces

I've attempted to create a Python interface class hierachy that looks something like:
class Axis(object, metaclass=ABCMeta):
def __init__(self):
# Do stuff...
class LinearAxis(Axis, metaclass=ABCMeta):
#abstractmethod
def move_linear(self, move_um):
pass
def __init__(self):
# Do stuff...
Axis.__init__(self)
class RotationalAxis(Axis, metaclass=ABCMeta):
#abstractmethod
def move_rotate(self, move_degree):
pass
def __init__(self):
# Do stuff...
Axis.__init__(self)
class XAxis(LinearAxis, metaclass=ABCMeta):
def __init__(self):
# Do stuff...
LinearAxis.__init__(self)
So basically an interface sort of like that with a bunch more functions everywhere and stuff in the constructors etc...
Then I go to derive off my interface:
class AnAxis(Axis):
def __init__(self):
# Do stuff...
Axis.__init__(self)
class AnLinearAxis(AnAxis, LinearAxis):
def move_linear(self, move_um):
pass
def __init__(self):
# Do stuff...
AnAxis.__init__(self)
LinearAxis.__init__(self)
class AnRotationalAxis(AnAxis, RotationalAxis):
def move_rotate(self, move_degree):
pass
def __init__(self):
# Do stuff...
AnAxis.__init__(self)
RotationalAxis.__init__(self)
class AnXAxis(AnLinearAxis, XAxis):
def __init__(self):
# Do stuff...
AnLinearAxis.__init__(self)
XAxis.__init__(self)
I'm trying to work out how to call the constructors properly. The way I have it, I'm pretty sure I call the interface constructors many times... So it's wrong... Is there a preferred way to do it? (Perhaps I don't call constructors in the interface classes, or I only call the interface constructor at the end up my implementation class.)
Also, I've never coded in this style and am open to better ways to code this.
You're probably looking for the super() function.
Calling super().something() calls the method something() of the parent class. It makes sure (using __mro__) to call the parent classes' method only once.
i.e. your code will look like this:
class AnLinearAxis(AnAxis, LinearAxis):
def move_linear(self, move_um):
pass
def __init__(self):
# Do stuff...
super().__init__()
Keep in mind you do not need to pass self or the metaclass. The metaclass passes by the inheritance. Also, you do not need to call super more than once. Super will call all of the parent classes' methods automatically.
Regarding the interface, it looks good but there's no need to pass metaclass=ABCMeta if the class you're inheriting from already has it. The metaclass is passed on by inheritance.

Categories

Resources