pytest core components: pluggy plug-in system (2)

This article is part of the series “Pytest Source Code Analysis”

Currently being serialized, welcome to follow

Picture

3. Example analysis of plug-in mechanism

A complete software + plug-in example named “eggsample” is provided in the pluggy documentation.

Next, I will use this example for analysis to have a more in-depth experience of the use of the pluggy plug-in system.

01

Writing software

Write the software ontology in a project named eggsample,

First, create the first file to define hookimpl, which can be used by internal or external plugins to implement hooks.

<em># eggsample/eggsample/__init__.py</em></code><code>import pluggy</code>
<code>hookimpl = pluggy.HookimplMarker("eggsample")

Next, create a second file to define hookspecs and describe what kind of hooks there are in the software.

<em># eggsample/eggsample/hookspecs.py</em></code><code>import pluggy</code>
<code>hookspec = pluggy.HookspecMarker("eggsample")</code>

<code>@hookspec</code><code>def eggsample_add_ingredients(ingredients: tuple):</code><code> """This hook receives the ingredients parameter and returns the modified ingredients""\ "</code>

<code>@hookspec</code><code>def eggsample_prep_condiments(condiments: dict):</code><code> """This hook receives the condiments parameter and returns a string as a plug-in comment"\ ""

Next, implement an internal plug-in for use by the software ontology

<em># eggsample/eggsample/lib.py</em></code><code> <em># At this time lib.py is equivalent to a plug-in, but it is an internal plug-in and does not need Installation</em></code>
<code>@eggsample.hookimpl # Implemented hook, but did not receive parameters according to hook requirements</code><code>def eggsample_add_ingredients(): </code><code> spices = ["salt", "pepper "]</code><code> you_can_never_have_enough_eggs = ["egg", "egg"]</code><code> ingredients = spices + you_can_never_have_enough_eggs</code><code> return ingredients</code>

<code>@eggsample.hookimpl # Implemented the hook, but did not return the string as required by the hook</code><code>def eggsample_prep_condiments(condiments):</code><code> condiments["mint sauce"] = 1

Finally, write the code of the software ontology

<em># eggsample/eggsample/host.py</em></code>
<code>import itertools</code><code>import random</code>
<code>import pluggy</code>
<code>from eggsample import hookspecs, lib</code>
<code>condiments_tray = {<!-- -->"pickled walnuts": 13, "steak sauce": 4, "mushy peas": 2}</code>

<code>def main():</code><code> pm = get_plugin_manager()</code><code> cook = EggsellentCook(pm.hook)</code><code> cook.add_ingredients()</code><code> cook.prepare_the_food()</code><code> cook.serve_the_food()</code>

<code>def get_plugin_manager():</code><code> pm = pluggy.PluginManager("eggsample")</code><code> pm.add_hookspecs(hookspecs)</code><code> pm.load_setuptools_entrypoints ("eggsample")</code><code> pm.register(lib)</code><code> return pm</code>

<code>class EggsellentCook:</code><code> FAVORITE_INGREDIENTS = ("egg", "egg", "egg")</code>
<code> def __init__(self, hook):</code><code> self.hook = hook</code><code> self.ingredients = None</code>
<code> def add_ingredients(self):</code><code> results = self.hook.eggsample_add_ingredients(</code><code> ingredients=self.FAVORITE_INGREDIENTS</code><code> )</code><code> my_ingredients = list(self.FAVORITE_INGREDIENTS)</code><code> <em># Each hook returns a list - so we chain this list of lists</em></code><code> other_ingredients = list(itertools. chain(*results))</code><code> self.ingredients = my_ingredients + other_ingredients</code>
<code> def prepare_the_food(self):</code><code> random.shuffle(self.ingredients)</code>
<code> def serve_the_food(self):</code><code> condiment_comments = self.hook.eggsample_prep_condiments(</code><code> condiments=condiments_tray</code><code> )</code><code> print (f"Your food. Enjoy some {<!-- -->', '.join(self.ingredients)}")</code><code> print(f"Some condiments? We have {<!-- -->', '.join(condiments_tray.keys())}")</code><code> if any(condiment_comments):</code><code> print(" \\
".join(condiment_comments))</code>

<code>if __name__ == "__main__":</code><code> main()

In the software ontology, several things are done:

  1. Create pm (line 22)

  2. Load hook declaration (line 23)

  3. Load third-party plug-ins through setuptools (line 24)

  4. Load built-in plugins via import (line 25)

  5. Call hook to complete software functions

Judging from this example, the hook calling code and the non-hook calling code are mixed together.

In other words, the software will use plug-ins (hook implementations) as part of itself, regardless of where these plug-ins are defined

It’s not over yet. In order to use the plug-in to install and run like pytest, you need to create an additional setup.py for the software.

<em># eggsample/setup.py</em></code>
<code>setup(</code><code> name="eggsample",</code><code> install_requires="pluggy",</code><code> entry_points={<!-- - ->"console_scripts": ["eggsample=eggsample.host:main"]},</code><code> packages=find_packages(),</code><code>)

Once everything is ready, you can use pip to install it.

pip install -e eggsample

OK, a python software has been developed and installed successfully!

Run it to see the effect, execute the command eggsample

Picture

It will combine the internally defined data ("egg", "egg", "egg") plus the data returned by the plug-in (["salt" , "pepper","egg", "egg"]) are merged together and output after shuffling the order.

egg, egg, salt, egg, egg, pepper, egg

At the same time, the internally defined data ({"pickled walnuts": 13, "steak sauce": 4, "mushy peas": 2}) will also be passed to the plug-in, The plug-in modifies the data before outputting it.

pickled walnuts, steak sauce, mushy peas, mint sauce

Currently, the software only uses internal plug-ins to complete some functions.

Next let’s take a look at how to create an external plug-in,

And the execution effect of the software after installing external plug-ins

First published on the public account: Test Development Research Club

It’s not easy to be original. If you like it, please star + like + watch

The knowledge points of the article match the official knowledge files, and you can further learn relevant knowledge. Python entry skill treeHomepageOverview 383,273 people are learning the system