Python topic series (1) pathlib topic
Python topic series (2) csv topic
Python topic series (3) logging topic
Python topic series (4) configparser topic
Python topic series (5) setuptools topic
Python series (6) pluggy series
1 pluggy introduction- pluggy function: it provides a simple and convenient plug-in system, which can loosely couple plug-ins with theme functions
- pluggy is the core framework of pytest, tox and devpi
Execute the following commands
pip install pluggy3 initial experience
import pluggy # HookspecMarker and HookimplMarker are essentially decorator classes with parameters, which are used to add additional attribute settings to functions hookspec = pluggy.HookspecMarker("myproject") hookimpl = pluggy.HookimplMarker("myproject") # Define your own Spec, which can be understood here as defining interface classes class MySpec: # hookspec is a decorator for decorating methods in a class. Additional attribute settings are added to this method. Here, myhook can be understood as defining an interface @hookspec def myhook(self, arg1, arg2): pass # A plug-in is defined class Plugin_1: # The plug-in implements the interface defined above. Similarly, the method to implement the interface is decorated with a hookimpl decorator. The function is to return the sum of two parameters @hookimpl def myhook(self, arg1, arg2): print("inside Plugin_1.myhook()") return arg1 + arg2 # Define the second plug-in class Plugin_2: # The plug-in implements the interface defined above. Similarly, the method of implementing the interface is decorated with a hookimpl decorator. The function is to return the difference between the two parameters @hookimpl def myhook(self, arg1, arg2): print("inside Plugin_2.myhook()") return arg1 - arg2 # Instantiate an object managed by a plug-in. Note that the name here should be consistent with the name of the decorator defined at the beginning of the file pm = pluggy.PluginManager("myproject") # Add the custom interface class to the hook definition pm.add_hookspecs(MySpec) # Register the two plug-ins defined pm.register(Plugin_1()) pm.register(Plugin_2()) # Call the method through the hook of the plug-in management object. At this time, this method in both plug-ins will be executed, and follow the principle of "execute first after registration", that is, LIFO. The results of the two plug-ins will be returned in the form of handout list results = pm.hook.myhook(arg1=1, arg2=2) print(results)
The results are as follows:
inside Plugin_2.myhook() inside Plugin_1.myhook() [-1, 3]4 detailed explanation
From the perspective of code, it's a little more popular. In fact, it's quite simple. It can be understood as first defining an interface class, and then defining many plug-in classes. You can define as many plug-in classes as you want,
Interfaces should be defined in the interface class. The above example value defines an interface. In fact, many interfaces can be defined. In each plug-in class, you need to select the interface in the interface class to implement it. Of course, you don't need each
All need to be realized, which can be selectively realized according to their own needs.
for instance:
For example, an interface class is defined, in which 10 interfaces are defined and 3 classes are defined, which are implemented respectively
Three interfaces, six interfaces and ten interfaces in the interface class are, and then an object managed by the plug-in is instantiated. The instantiated object will
Add the interface class to the definition template, and then register these three classes. After registration, you can manage the hook of the object through the plug-in
Each method in the interface class is called, for example, the first three methods are called. Because they are implemented in each plug-in, there will be three results,
When calling the interfaces in the following 4-6, because only two plug-ins have been implemented, only two results will be returned. Call the interfaces in 7-10
Because only one plug-in class is implemented, only one result will be returned, so the plug-in is very flexible to use,
Can really achieve "hot plug"
The following is a code example:
import pluggy # HookspecMarker and HookimplMarker are essentially decorator classes with parameters, which are used to add additional attribute settings to functions hookspec = pluggy.HookspecMarker("myproject") hookimpl = pluggy.HookimplMarker("myproject") # Define your own Spec, which can be understood here as defining interface classes class MySpec: # hookspec is a decorator for decorating methods in a class. Additional attribute settings are added to this method. Here, myhook can be understood as defining three interfaces @hookspec def myhook1(self, arg1, arg2): pass @hookspec def myhook2(self, arg1, arg2): pass @hookspec def myhook3(self, arg1, arg2): pass # A plug-in is defined class Plugin_1: # The plug-in implements the interface defined above. Similarly, the method to implement the interface is decorated with a hookimpl decorator. The function is to return the sum of two parameters @hookimpl def myhook1(self, arg1, arg2): print("inside Plugin_1.myhook1()") return arg1 + arg2 @hookimpl def myhook2(self, arg1, arg2): print("inside Plugin_1.myhook2()") return arg1 + arg2 +1 @hookimpl def myhook4(self, arg1, arg2): print("inside Plugin_1.myhook4()") return arg1 + arg2 + 2 # Define the second plug-in class Plugin_2: # The plug-in implements the interface defined above. Similarly, the method of implementing the interface is decorated with a hookimpl decorator. The function is to return the difference between the two parameters @hookimpl def myhook1(self, arg1, arg2): print("inside Plugin_2.myhook1()") return arg1 - arg2 @hookimpl def myhook2(self, arg1, arg2): print("inside Plugin_2.myhook2()") return arg1 - arg2 -1 @hookimpl def myhook3(self, arg1, arg2): print("inside Plugin_2.myhook3()") return arg1 - arg2 -2 # Instantiate an object managed by a plug-in. Note that the name here should be consistent with the name of the decorator defined at the beginning of the file pm = pluggy.PluginManager("myproject") # Add the custom interface class to the hook definition pm.add_hookspecs(MySpec) # Register the two plug-ins defined pm.register(Plugin_1()) pm.register(Plugin_2()) # Call the method through the hook of the plug-in management object. At this time, this method in both plug-ins will be executed, and follow the principle of "execute first after registration", that is, LIFO. The results of the two plug-ins will be returned in the form of handout list results = pm.hook.myhook1(arg1=1, arg2=2) print(results) results = pm.hook.myhook2(arg1=1, arg2=2) print(results) results = pm.hook.myhook3(arg1=1, arg2=2) print(results) results = pm.hook.myhook4(arg1=1, arg2=2) print(results)
The results are as follows:
inside Plugin_2.myhook1() inside Plugin_1.myhook1() [-1, 3] inside Plugin_2.myhook2() inside Plugin_1.myhook2() [-2, 4] inside Plugin_2.myhook3() [-3] inside Plugin_1.myhook4() [5]
As can be seen from the above code example:
- 1) Because both myhook1 and myhook2 plug-ins are implemented, all return two results in reverse order
- 2) Since only plug-in 2 implements myhook 3, there is only one return result
- 3) myhook4 is not defined in the spec, but there are results here. At present, it is understood that it may be a pluggy bug, which will be explained after looking at the source code
import pluggy # HookspecMarker and HookimplMarker are essentially decorator classes with parameters, which are used to add additional attribute settings to functions hookspec = pluggy.HookspecMarker("myproject") hookimpl = pluggy.HookimplMarker("myproject") # Define your own Spec, which can be understood here as defining interface classes class MySpec: # hookspec is a decorator for decorating methods in a class. Additional attribute settings are added to this method. Here, myhook can be understood as defining an interface @hookspec(firstresult=True) def myhook(self, arg1, arg2): pass # A plug-in is defined class Plugin_1: # The plug-in implements the interface defined above. Similarly, the method to implement the interface is decorated with a hookimpl decorator. The function is to return the sum of two parameters @hookimpl def myhook(self, arg1, arg2): print("inside Plugin_1.myhook()") return arg1 + arg2 # Define the second plug-in class Plugin_2: # The plug-in implements the interface defined above. Similarly, the method of implementing the interface is decorated with a hookimpl decorator. The function is to return the difference between the two parameters @hookimpl def myhook(self, arg1, arg2): print("inside Plugin_2.myhook()") return arg1 - arg2 # Instantiate an object managed by a plug-in. Note that the name here should be consistent with the name of the decorator defined at the beginning of the file pm = pluggy.PluginManager("myproject") # Add the custom interface class to the hook definition pm.add_hookspecs(MySpec) # Register the two plug-ins defined pm.register(Plugin_1()) pm.register(Plugin_2()) # Call the method through the hook of the plug-in management object. At this time, this method in both plug-ins will be executed, and follow the principle of "execute first after registration", that is, LIFO. The results of the two plug-ins will be returned in the form of handout list results = pm.hook.myhook(arg1=1, arg2=2) print(results)
The results are as follows:
inside Plugin_2.myhook() -16. The hookimplmarker decorator also supports the input of some specific parameters, such as tryfirst, trylast and hookwrapper
- When tryfirst=True is passed in, the hook function of this class will be executed first, and others will still be executed in the order of last in first out
import pluggy # HookspecMarker and HookimplMarker are essentially decorator classes with parameters, which are used to add additional attribute settings to functions hookspec = pluggy.HookspecMarker("myproject") hookimpl = pluggy.HookimplMarker("myproject") # Define your own Spec, which can be understood here as defining interface classes class MySpec: # hookspec is a decorator for decorating methods in a class. Additional attribute settings are added to this method. Here, myhook can be understood as defining an interface @hookspec def myhook(self, arg1, arg2): pass # A plug-in is defined class Plugin_1: # The plug-in implements the interface defined above. Similarly, the method to implement the interface is decorated with a hookimpl decorator. The function is to return the sum of two parameters @hookimpl(tryfirst=True) def myhook(self, arg1, arg2): print("inside Plugin_1.myhook()") return arg1 + arg2 # Define the second plug-in class Plugin_2: # The plug-in implements the interface defined above. Similarly, the method of implementing the interface is decorated with a hookimpl decorator. The function is to return the difference between the two parameters @hookimpl def myhook(self, arg1, arg2): print("inside Plugin_2.myhook()") return arg1 - arg2 # Define the third plug-in class Plugin_3: # The plug-in implements the interface defined above. Similarly, the method of implementing the interface is decorated with a hookimpl decorator. The function is to return the difference between the two parameters @hookimpl def myhook(self, arg1, arg2): print("inside Plugin_3.myhook()") return arg1 - arg2+10 # Instantiate an object managed by a plug-in. Note that the name here should be consistent with the name of the decorator defined at the beginning of the file pm = pluggy.PluginManager("myproject") # Add the custom interface class to the hook definition pm.add_hookspecs(MySpec) # Register the two plug-ins defined pm.register(Plugin_1()) pm.register(Plugin_2()) pm.register(Plugin_3()) # Call the method through the hook of the plug-in management object. At this time, this method in both plug-ins will be executed, and follow the principle of "execute first after registration", that is, LIFO. The results of the two plug-ins will be returned in the form of handout list results = pm.hook.myhook(arg1=1, arg2=2) print(results)
The implementation results are as follows:
inside Plugin_1.myhook() inside Plugin_3.myhook() inside Plugin_2.myhook() [3, 9, -1]
- When trylast=True is passed in, it means that the hook function of the current plug-in will be executed as late as possible, and others will still be executed in the order of last in first out
import pluggy # HookspecMarker and HookimplMarker are essentially decorator classes with parameters, which are used to add additional attribute settings to functions hookspec = pluggy.HookspecMarker("myproject") hookimpl = pluggy.HookimplMarker("myproject") # Define your own Spec, which can be understood here as defining interface classes class MySpec: # hookspec is a decorator for decorating methods in a class. Additional attribute settings are added to this method. Here, myhook can be understood as defining an interface @hookspec def myhook(self, arg1, arg2): pass # A plug-in is defined class Plugin_1: # The plug-in implements the interface defined above. Similarly, the method to implement the interface is decorated with a hookimpl decorator. The function is to return the sum of two parameters @hookimpl() def myhook(self, arg1, arg2): print("inside Plugin_1.myhook()") return arg1 + arg2 # Define the second plug-in class Plugin_2: # The plug-in implements the interface defined above. Similarly, the method of implementing the interface is decorated with a hookimpl decorator. The function is to return the difference between the two parameters @hookimpl(trylast=True) def myhook(self, arg1, arg2): print("inside Plugin_2.myhook()") return arg1 - arg2 # Define the third plug-in class Plugin_3: # The plug-in implements the interface defined above. Similarly, the method of implementing the interface is decorated with a hookimpl decorator. The function is to return the difference between the two parameters @hookimpl def myhook(self, arg1, arg2): print("inside Plugin_3.myhook()") return arg1 - arg2+10 # Instantiate an object managed by a plug-in. Note that the name here should be consistent with the name of the decorator defined at the beginning of the file pm = pluggy.PluginManager("myproject") # Add the custom interface class to the hook definition pm.add_hookspecs(MySpec) # Register the two plug-ins defined pm.register(Plugin_1()) pm.register(Plugin_2()) pm.register(Plugin_3()) # Call the method through the hook of the plug-in management object. At this time, this method in both plug-ins will be executed, and follow the principle of "execute first after registration", that is, LIFO. The results of the two plug-ins will be returned in the form of handout list results = pm.hook.myhook(arg1=1, arg2=2) print(results)
The results are as follows:
inside Plugin_3.myhook() inside Plugin_1.myhook() inside Plugin_2.myhook() [9, 3, -1]
- When hookwrapper=True is passed in, a yield needs to be implemented in the plugin, and the plugin executes yield first
The previous code, then execute other pluggin s, and then come back to execute the code after yield. At the same time, through yield, you can
Get the results of other plug-ins
import pluggy # HookspecMarker and HookimplMarker are essentially decorator classes with parameters, which are used to add additional attribute settings to functions hookspec = pluggy.HookspecMarker("myproject") hookimpl = pluggy.HookimplMarker("myproject") # Define your own Spec, which can be understood here as defining interface classes class MySpec: # hookspec is a decorator for decorating methods in a class. Additional attribute settings are added to this method. Here, myhook can be understood as defining an interface @hookspec def myhook(self, arg1, arg2): pass # A plug-in is defined class Plugin_1: # The plug-in implements the interface defined above. Similarly, the method to implement the interface is decorated with a hookimpl decorator. The function is to return the sum of two parameters @hookimpl() def myhook(self, arg1, arg2): print("inside Plugin_1.myhook()") return arg1 + arg2 # Define the second plug-in class Plugin_2: # The plug-in implements the interface defined above. Similarly, the method of implementing the interface is decorated with a hookimpl decorator. The function is to return the difference between the two parameters @hookimpl(hookwrapper=True) def myhook(self, arg1, arg2): print("inside Plugin_2.myhook() before yield...") output=yield result=output.get_result() print(result) print("inside Plugin_2.myhook() after yield...") # Define the third plug-in class Plugin_3: # The plug-in implements the interface defined above. Similarly, the method of implementing the interface is decorated with a hookimpl decorator. The function is to return the difference between the two parameters @hookimpl def myhook(self, arg1, arg2): print("inside Plugin_3.myhook()") return arg1 - arg2+10 # Instantiate an object managed by a plug-in. Note that the name here should be consistent with the name of the decorator defined at the beginning of the file pm = pluggy.PluginManager("myproject") # Add the custom interface class to the hook definition pm.add_hookspecs(MySpec) # Register the two plug-ins defined pm.register(Plugin_1()) pm.register(Plugin_2()) pm.register(Plugin_3()) # Call the method through the hook of the plug-in management object. At this time, this method in both plug-ins will be executed, and follow the principle of "execute first after registration", that is, LIFO. The results of the two plug-ins will be returned in the form of handout list results = pm.hook.myhook(arg1=1, arg2=2) print(results)
The results are as follows:
inside Plugin_2.myhook() before yield... inside Plugin_3.myhook() inside Plugin_1.myhook() [9, 3] inside Plugin_2.myhook() after yield... [9, 3]