I have a base contract class which can be inherited to provide plugin functionality. I'm adding the new plugins using setuptools entrypoints something like
entry_points="""
[plugins]
plugin1=plugins.plugin1:Plugin1
"""
And classes look like...
class Plugin:
__metaclass__ = abc.ABCMeta
#abstractmethod
def must_override_method():
pass
#abstractmethod
def must_override_method2():
pass
#./plugins/plugin1.py
#Actually the plugins could be anywhere
class Plugin1(Plugin):
def must_override_method():
print("Hello")
Although the #abstracmethod doesn't let me instantiate the class at runtime if must_override_methods are not defined but how should I go about adding unit tests for the plugins that are not yet written.
Is there a simple way to write generic test that catches "plugins" that don't implement abstract methods while testing?
I think, the best way is to use mocking for that abstract class. Mocking is a mechanism where it won't really creates an object or try to create. Rather, It will create a mock object which will have same properties. please use mock module for the same
Related
Say I have the following simple example without any typehints:
def wrapper(cls):
class Subclass(cls):
def subclass_method(self):
pass
return Subclass
#wrapper
class Parent:
def parent_method(self):
pass
p = Parent()
p.parent_method()
p.subclass_method()
How can I restructure this code using typehints, such that when I run mypy against an instance of Parent, it will recognize both subclass_method and parent_method?
Possible solutions:
Using a mixin Parent(Mixin): Works, but avoids the decorator. Is it possible to achieve without?
Patching the method onto the existing class: Still has the same issue of resolving subclass_method in mypy
Custom Mypy plugin: Wouldn't be sure where to start with this one, or if it would be possible without one.
This would be much simpler without the wrapper at all.
class SomeMixin:
def subclass_method(self):
pass
class Parent(SomeMixin):
def parent_method(self):
pass
p = Parent()
p.parent_method()
p.subclass_method()
Here, you define SomeMixin once, not once per call to a wrapper, and the class SomeMixin is known statically. All the various classes with the name Subclass are created dynamically, and mypy can't know statically which class the name Parent is actually bound to.
I need to use unittest in python to write some tests. I am testing the behavior of 2 classes, A and B, that have a lot of overlap in behavior because they are both subclasses of C, which is abstract. I would really like to be able to write 3 testing classes: ATestCase, BTestCase, and AbstractTestCase, where AbstractTestCase defines the common setup logic for ATestCase and BTestCase, but does not itself run any tests. ATestCase and BTestCase would be subclasses of AbstractTestCase and would define behavior/input data specific to A and B.
Is there a way to create an abstract class via python unittest that can take care of setup functionality by inheriting from TestCase, but not actually run any tests?
Sure, construct like that will surely work:
class BaseTestCase(unittest.TestCase):
def setUp(self):
pass # common teardown
def tearDown(self):
pass # common teardown
class ATestCase(BaseTestCase):
def test1(self):
pass
class BTestCase(BaseTestCase):
def test1(self):
pass
If knowledge from ATestCase or BTestCase is required in BaseTestCase simply override some method in subclasses but use it in superclass.
class BaseTestCase(unittest.TestCase):
def setUp(self):
self.instance = self._create_instance()
def _create_instance(self):
raise NotImplementedError()
class ATestCase(BaseTestCase):
def _create_instance(self):
return A()
class BestCase(BaseTestCase):
def _create_instance(self):
return B()
Note that if any test_(self) methods will be implemented in BaseTestCase, they'll run (and fail due to failing setUp) when discovered by automated runners.
As a workaround you may use skipTest in your setUp clause in abstract test and override it in subclasses.
class BaseTestCase(unittest.TestCase):
def setUp(self):
self.instance = self._create_instance()
def _create_instance(self):
self.skipTest("Abstract")
def test_fromBase(self):
self.assertTrue(True)
Note that skipping test_fromBase (e.g. via decorator) won't be good, since 'test should be skipped' logic will be inherited by all subclasses.
I tried Łukasz’s answer and it works, but I don’t like OK (SKIP=<number>) messages. For my own desires and aims for having a test suite I don’t want me or someone to start trusting any particular number of skipped tests, or not trusting and digging into the test suite and asking why something was skipped, and always?, and on purpose? For me that’s a non-starter.
I happen to use nosetests exclusively, and by convention test classes starting with _ are not run, so naming my base class _TestBaseClass is sufficient.
I tried this in Pycharm with Unittests and py.test and both of those tried to run my base class and its tests resulting in errors because there’s no instance data in the abstract base class. Maybe someone with specific knowledge of either of those runners could make a suite, or something, that bypasses the base class.
I try to use abstract in python, I created base class and concrete class. After I write register method, it shows Undefined variable from import: register. I suppose register is only a method, which library I shall import?
concreteTest.py:
import baseTest
class concreteTest(baseTest):
def __abcMethodTest__(self):
print("I am in concrete class")
baseTest.register(concreteTest)
if __name__ == '__main__':
print 'Subclass:', issubclass(concreteTest, baseTest)
print 'Instance:
baseTest.py:
from abc import ABCMeta
from abc import abstractmethod
class baseTest:
__metaclass__ = ABCMeta
#abstractmethod
def __abcMethodTest__(self):
while False:
yield None
You have a class called baseTest within a module called baseTest, so after importing the module baseTest, you would need to use baseTest.baseTest to access the class:
baseTest.baseTest.register(concreteTest)
Note that you don't even need to register concreteTest as a virtual subclass, since it's already an actual subclass. The register() method is meant to register classes that don't actually derive from an abstract class you defined, but do fulfill the interface.
That said, it is unlikely that you routinely want to use abstract classes in Python. They are useful in some special circumstances, but aren't used as the standard way of declaring interfaces. See also the blog post Python is not Java.
I'm using Pytest to test hardware via Python models.
My current conftest setup allows me to instantiate the Python model via funcargs, which I like.
def test_part1(target):
...
def test_part2(target):
...
etc.
My models have a deep, but simple single-inheritance structure:
class A():
def __init__:
self.attributes_added_by_class_A
...
class B(A):
def __init__:
super().__init__()
self.attributes_added_by_class_B
...
etc.
My pytests currently look like this:
def test_part1(target):
call_test_for_attributes_set_by_class_A(target)
call_test_for_attributes_set_by_class_B(target)
call_tests_specific_to_part1(target)
def test_part2(target):
call_test_for_attributes_set_by_class_A(target)
call_test_for_attributes_set_by_class_B(target)
call_tests_specific_to_part2(target)
...
I would like to avoid the need for repeating call_test_for_attributes_set_by_class_A(target), etc.
How can I organize/create my pytests so that I'm not constantly re-writing code to test attributes common to each target via their inheritance? In other words, if Part1 and Part2 both inherit from Class A, they both have attributes assigned by Class A.
Is there some way I could use Python's class inheritance structure to allow my pytests to reflect the same inheritance as my objects? In other words, is something like the following possible?
class PytestA():
def test_attributes_set_by_class_A()
class PytestB(PytestA):
def test_attributes_set_by_class_B()
test_all = PytestB(target)
I'm stuck trying to figure out how the above would receive the target argument as __init__() is now allowed (?) in pytest classes.
I'm using Python3
I think you want a fixture that depends on another fixture. See Modularity: using fixtures from a fixture function. Perhaps you want a generic target fixture, then a targetA fixture, and a targetB fixture. I don't entirely understand how your tests are working so I'm hesitant to give an example based on what you've written.
I'm currently writing an application which allows the user to extend it via a 'plugin' type architecture. They can write additional python classes based on a BaseClass object I provide, and these are loaded against various application signals. The exact number and names of the classes loaded as plugins is unknown before the application is started, but are only loaded once at startup.
During my research into the best way to tackle this I've come up with two common solutions.
Option 1 - Roll your own using imp, pkgutil, etc.
See for instance, this answer or this one.
Option 2 - Use a plugin manager library
Randomly picking a couple
straight.plugin
yapsy
this approach
My question is - on the proviso that the application must be restarted in order to load new plugins - is there any benefit of the above methods over something inspired from this SO answer and this one such as:
import inspect
import sys
import my_plugins
def predicate(c):
# filter to classes
return inspect.isclass(c)
def load_plugins():
for name, obj in inspect.getmembers(sys.modules['my_plugins'], predicate):
obj.register_signals()
Are there any disadvantages to this approach compared to the ones above? (other than all the plugins must be in the same file) Thanks!
EDIT
Comments request further information... the only additional thing I can think to add is that the plugins use the blinker library to provide signals that they subscribe to. Each plugin may subscribe to different signals of different types and hence must have its own specific "register" method.
Since Python 3.6 a new class method __init_subclass__ is added, that is called on a base class, whenever a new subclass is created.
This method can further simplify the solution offered by will-hart above, by removing the metaclass.
The __init_subclass__ method was introduced with PEP 487: Simpler customization of class creation. The PEP comes with a minimal example for a plugin architecture:
It is now possible to customize subclass creation without using a
metaclass. The new __init_subclass__ classmethod will be called on
the base class whenever a new subclass is created:
class PluginBase:
subclasses = []
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
cls.subclasses.append(cls)
class Plugin1(PluginBase):
pass
class Plugin2(PluginBase):
pass
The PEP example above stores references to the classes in the Plugin.plugins field.
If you want to store instances of the plugin classes, you can use a structure like this:
class Plugin:
"""Base class for all plugins. Singleton instances of subclasses are created automatically and stored in Plugin.plugins class field."""
plugins = []
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
cls.plugins.append(cls())
class MyPlugin1(Plugin):
def __init__(self):
print("MyPlugin1 instance created")
def do_work(self):
print("Do something")
class MyPlugin2(Plugin):
def __init__(self):
print("MyPlugin2 instance created")
def do_work(self):
print("Do something else")
for plugin in Plugin.plugins:
plugin.do_work()
which outputs:
MyPlugin1 instance created
MyPlugin2 instance created
Do something
Do something else
The metaclass approach is useful for this issue in Python < 3.6 (see #quasoft's answer for Python 3.6+). It is very simple and acts automatically on any imported module. In addition, complex logic can be applied to plugin registration with very little effort. It requires:
The metaclass approach works like the following:
1) A custom PluginMount metaclass is defined which maintains a list of all plugins
2) A Plugin class is defined which sets PluginMount as its metaclass
3) When an object deriving from Plugin - for instance MyPlugin is imported, it triggers the __init__ method on the metaclass. This registers the plugin and performs any application specific logic and event subscription.
Alternatively if you put the PluginMount.__init__ logic in PluginMount.__new__ it is called whenver a new instance of a Plugin derived class is created.
class PluginMount(type):
"""
A plugin mount point derived from:
http://martyalchin.com/2008/jan/10/simple-plugin-framework/
Acts as a metaclass which creates anything inheriting from Plugin
"""
def __init__(cls, name, bases, attrs):
"""Called when a Plugin derived class is imported"""
if not hasattr(cls, 'plugins'):
# Called when the metaclass is first instantiated
cls.plugins = []
else:
# Called when a plugin class is imported
cls.register_plugin(cls)
def register_plugin(cls, plugin):
"""Add the plugin to the plugin list and perform any registration logic"""
# create a plugin instance and store it
# optionally you could just store the plugin class and lazily instantiate
instance = plugin()
# save the plugin reference
cls.plugins.append(instance)
# apply plugin logic - in this case connect the plugin to blinker signals
# this must be defined in the derived class
instance.register_signals()
Then a base plugin class which looks like:
class Plugin(object):
"""A plugin which must provide a register_signals() method"""
__metaclass__ = PluginMount
Finally, an actual plugin class would look like the following:
class MyPlugin(Plugin):
def register_signals(self):
print "Class created and registering signals"
def other_plugin_stuff(self):
print "I can do other plugin stuff"
Plugins can be accessed from any python module that has imported Plugin:
for plugin in Plugin.plugins:
plugin.other_plugin_stuff()
See the full working example
The approach from will-hart was the most useful one to me!
For i needed more control I wrapped the Plugin Base class in a function like:
def get_plugin_base(name='Plugin',
cls=object,
metaclass=PluginMount):
def iter_func(self):
for mod in self._models:
yield mod
bases = not isinstance(cls, tuple) and (cls,) or cls
class_dict = dict(
_models=None,
session=None
)
class_dict['__iter__'] = iter_func
return metaclass(name, bases, class_dict)
and then:
from plugin import get_plugin_base
Plugin = get_plugin_base()
This allows to add additional baseclasses or switching to another metaclass.