The protocol scheme is similar with the subclass one,
except for one point: instead of using and inheriting the base class,
we define the Protocol.
Python typing protocols
are the class signatures. Instead of defining the base class and inherit
from it, we define the functions and their signatures.
The major advantage of the protocol scheme is the fact we need no to import
something from the core in our plugins.
fromtypingimportProtocol,runtime_checkable@runtime_checkableclassPluginProtocol(Protocol):"""Protocol for our plugin."""def__init__(self)->None:...defexecute(self,x:int,y:int)->int:"""Plugin performs operation on two integers and returns integer."""...
More complicated, then subclass, but still simple.
fromtypingimportProtocol,runtime_checkable@runtime_checkableclassPluginProtocol(Protocol):"""Protocol for our plugin."""def__init__(self)->None:...defexecute(self,x:int,y:int)->int:"""Plugin performs operation on two integers and returns integer."""...
We need Protocol and runtime_checkable from the Python
typing module.
fromtypingimportProtocol,runtime_checkable@runtime_checkableclassPluginProtocol(Protocol):"""Protocol for our plugin."""def__init__(self)->None:...defexecute(self,x:int,y:int)->int:"""Plugin performs operation on two integers and returns integer."""...
@runtime_checkable decorator is necessary for Loader
to be able to check plugins signatures. Do not forget it.
fromtypingimportProtocol,runtime_checkable@runtime_checkableclassPluginProtocol(Protocol):"""Protocol for our plugin."""def__init__(self)->None:...defexecute(self,x:int,y:int)->int:"""Plugin performs operation on two integers and returns integer."""...
Protocols are derived from Protocol generic class.
fromtypingimportProtocol,runtime_checkable@runtime_checkableclassPluginProtocol(Protocol):"""Protocol for our plugin."""def__init__(self)->None:...defexecute(self,x:int,y:int)->int:"""Plugin performs operation on two integers and returns integer."""...
Though docstrings are advisory it will help to navigate our code,
so describe plugin tasks and features.
fromtypingimportProtocol,runtime_checkable@runtime_checkableclassPluginProtocol(Protocol):"""Protocol for our plugin."""def__init__(self)->None:...defexecute(self,x:int,y:int)->int:"""Plugin performs operation on two integers and returns integer."""...
Our plugins has no initialization parameters, so we declare plain __init__ constructor.
fromtypingimportProtocol,runtime_checkable@runtime_checkableclassPluginProtocol(Protocol):"""Protocol for our plugin."""def__init__(self)->None:...defexecute(self,x:int,y:int)->int:"""Plugin performs operation on two integers and returns integer."""...
All protocol functions are abstract, so we add ... operator to skip the implementation.
fromtypingimportProtocol,runtime_checkable@runtime_checkableclassPluginProtocol(Protocol):"""Protocol for our plugin."""def__init__(self)->None:...defexecute(self,x:int,y:int)->int:"""Plugin performs operation on two integers and returns integer."""...
Our main function of the plugin, do not to forget to place the proper type hints to allow
static type checking.
fromtypingimportProtocol,runtime_checkable@runtime_checkableclassPluginProtocol(Protocol):"""Protocol for our plugin."""def__init__(self)->None:...defexecute(self,x:int,y:int)->int:"""Plugin performs operation on two integers and returns integer."""...
The main function of the plugin already should be documented.
fromtypingimportProtocol,runtime_checkable@runtime_checkableclassPluginProtocol(Protocol):"""Protocol for our plugin."""def__init__(self)->None:...defexecute(self,x:int,y:int)->int:"""Plugin performs operation on two integers and returns integer."""...
Ellipses operator (...) shows the implementation is abstract.
If missed, mypy
will complain the function doesn't return an integer value.
Then let's create a loader instance. You need only one loader instance per each type for your application. So loaders are usually singletons.
Loader is the generic type, so we must pass the exact plugin type. In the protocol scheme
plugins are classes, following from the PluginProtocol protocol. In Python's typing terms,
the protocol type is the PluginProtocol. We'd placed the type into
the brackets just after the Loader.
After defining the plugin's type, we need to initialize the loader itself.
Loader has several initialization parameters, see Reference
for details. Here we consider our plugins will be in plugins folder of our applications.
Loader returns the class. We create the instance to show we can use some plugin initialization
tasks. We can also define the execute method as a @classmethod to skip
the initialization step.
Create the new plugin class. Class must follow PluginProtocol signature
but we need no the point it explicitly. So we may derive our plugin from any class.
Let's start from object.