A minimalist production ready plugin system

Overview

pluggy - A minimalist production ready plugin system

pypi conda-forge versions github-actions travis Join the chat at https://gitter.im/pytest-dev/pluggy black Code coverage Status

This is the core framework used by the pytest, tox, and devpi projects.

Please read the docs to learn more!

A definitive example

import pluggy

hookspec = pluggy.HookspecMarker("myproject")
hookimpl = pluggy.HookimplMarker("myproject")


class MySpec:
    """A hook specification namespace.
    """

    @hookspec
    def myhook(self, arg1, arg2):
        """My special little hook that you can customize.
        """


class Plugin_1:
    """A hook implementation namespace.
    """

    @hookimpl
    def myhook(self, arg1, arg2):
        print("inside Plugin_1.myhook()")
        return arg1 + arg2


class Plugin_2:
    """A 2nd hook implementation namespace.
    """

    @hookimpl
    def myhook(self, arg1, arg2):
        print("inside Plugin_2.myhook()")
        return arg1 - arg2


# create a manager and add the spec
pm = pluggy.PluginManager("myproject")
pm.add_hookspecs(MySpec)

# register plugins
pm.register(Plugin_1())
pm.register(Plugin_2())

# call our ``myhook`` hook
results = pm.hook.myhook(arg1=1, arg2=2)
print(results)

Running this directly gets us:

$ python docs/examples/toy-example.py
inside Plugin_2.myhook()
inside Plugin_1.myhook()
[-1, 3]
Comments
  • support defaults to enable deprecation

    support defaults to enable deprecation

    this is to help pytest-dev/pytest#1372

    the idea is, that if a hook is invoked with a missing, but specced argument, then the default is used instead

    so if a hook caller would call hook.pytest_deselected(items=...)' and the hook was defined withdef pytest_deselected(items, reason='No Reason Given)` then the receivers would receive the defined default reason

    in order to support deprecation of such usage we could later devise a mark for deprecated defaults

    enhancement 
    opened by RonnyPfannschmidt 48
  • Release 1.0?

    Release 1.0?

    Hi,

    As has been discussed before, is it time to start discussing about the 1.0 release? It has been in use in production in pytest, tox and devpi for some time after all.

    • Do we have any major changes that are backward incompatible?
    • What to do about our scheduled deprecations?

    Milestone 1.0

    https://github.com/pytest-dev/pluggy/milestone/1

    opened by nicoddemus 33
  • Improve docs: introduce roles and add another example

    Improve docs: introduce roles and add another example

    My first go at explaining pluggy from the perspective of three different roles introducing all important concepts and practices in a reading time well under 5 minutes. Please note the pending fixme regarding dependency handling.

    opened by obestwalter 26
  • importlib_metadata breaks TravisCI builds

    importlib_metadata breaks TravisCI builds

    Hi all,

    I've noticed my builds started to fail because importlib_metadata couldn't not be found by pluggy:

    Here is an example https://travis-ci.org/chaostoolkit-incubator/chaostoolkit-aws/jobs/529280278

    running build_ext
    Traceback (most recent call last):
      File "setup.py", line 97, in <module>
        main()
      File "setup.py", line 93, in main
        setuptools.setup(**setup_params)
      File "/home/travis/virtualenv/python3.6.3/lib/python3.6/site-packages/setuptools/__init__.py", line 129, in setup
        return distutils.core.setup(**attrs)
      File "/opt/python/3.6.3/lib/python3.6/distutils/core.py", line 148, in setup
        dist.run_commands()
      File "/opt/python/3.6.3/lib/python3.6/distutils/dist.py", line 955, in run_commands
        self.run_command(cmd)
      File "/opt/python/3.6.3/lib/python3.6/distutils/dist.py", line 974, in run_command
        cmd_obj.run()
      File "/home/travis/build/chaostoolkit-incubator/chaostoolkit-aws/.eggs/pytest_runner-4.4-py3.6.egg/ptr.py", line 190, in run
        return self.run_tests()
      File "/home/travis/build/chaostoolkit-incubator/chaostoolkit-aws/.eggs/pytest_runner-4.4-py3.6.egg/ptr.py", line 201, in run_tests
        result_code = __import__('pytest').main()
      File "/home/travis/build/chaostoolkit-incubator/chaostoolkit-aws/.eggs/pytest-4.4.1-py3.6.egg/pytest.py", line 7, in <module>
        from _pytest.assertion import register_assert_rewrite
      File "/home/travis/build/chaostoolkit-incubator/chaostoolkit-aws/.eggs/pytest-4.4.1-py3.6.egg/_pytest/assertion/__init__.py", line 12, in <module>
        from _pytest.assertion import rewrite
      File "/home/travis/build/chaostoolkit-incubator/chaostoolkit-aws/.eggs/pytest-4.4.1-py3.6.egg/_pytest/assertion/rewrite.py", line 23, in <module>
        from _pytest.assertion import util
      File "/home/travis/build/chaostoolkit-incubator/chaostoolkit-aws/.eggs/pytest-4.4.1-py3.6.egg/_pytest/assertion/util.py", line 10, in <module>
        import _pytest._code
      File "/home/travis/build/chaostoolkit-incubator/chaostoolkit-aws/.eggs/pytest-4.4.1-py3.6.egg/_pytest/_code/__init__.py", line 6, in <module>
        from .code import Code  # noqa
      File "/home/travis/build/chaostoolkit-incubator/chaostoolkit-aws/.eggs/pytest-4.4.1-py3.6.egg/_pytest/_code/code.py", line 15, in <module>
        import pluggy
      File "/home/travis/build/chaostoolkit-incubator/chaostoolkit-aws/.eggs/pluggy-0.10.0-py3.6.egg/pluggy/__init__.py", line 16, in <module>
        from .manager import PluginManager, PluginValidationError
      File "/home/travis/build/chaostoolkit-incubator/chaostoolkit-aws/.eggs/pluggy-0.10.0-py3.6.egg/pluggy/manager.py", line 6, in <module>
        import importlib_metadata
      File "/home/travis/build/chaostoolkit-incubator/chaostoolkit-aws/.eggs/importlib_metadata-0.9-py3.6.egg/importlib_metadata/__init__.py", line 20, in <module>
        __version__ = version(__name__)
      File "/home/travis/build/chaostoolkit-incubator/chaostoolkit-aws/.eggs/importlib_metadata-0.9-py3.6.egg/importlib_metadata/api.py", line 348, in version
        return distribution(package).version
      File "/home/travis/build/chaostoolkit-incubator/chaostoolkit-aws/.eggs/importlib_metadata-0.9-py3.6.egg/importlib_metadata/api.py", line 313, in distribution
        return Distribution.from_name(package)
      File "/home/travis/build/chaostoolkit-incubator/chaostoolkit-aws/.eggs/importlib_metadata-0.9-py3.6.egg/importlib_metadata/api.py", line 160, in from_name
        raise PackageNotFoundError(name)
    importlib_metadata.api.PackageNotFoundError: importlib_metadata
    

    It happens on Python 3.6 and 3.7, but not on 3.5 because, on that one, pytest (which imports pluggy) remains on an older version.

    But then again, I'm confused because, importlib_metadata is indeed installed first:

    Searching for pluggy>=0.9
    Reading https://pypi.python.org/simple/pluggy/
    Downloading https://files.pythonhosted.org/packages/57/ba/54c93ac55084bde8f9195ed5cd04223b3c0d30d300801bf556565cb7675a/pluggy-0.10.0-py2.py3-none-any.whl#sha256=1c0b297d4d41bc9bdfbdc17991b35f9e1d2cfe8eaa4d7c118e86d705870d34c8
    Best match: pluggy 0.10.0
    Processing pluggy-0.10.0-py2.py3-none-any.whl
    Installing pluggy-0.10.0-py2.py3-none-any.whl to /home/travis/build/chaostoolkit-incubator/chaostoolkit-aws/.eggs
    writing requirements to /home/travis/build/chaostoolkit-incubator/chaostoolkit-aws/.eggs/pluggy-0.10.0-py3.6.egg/EGG-INFO/requires.txt
    Installed /home/travis/build/chaostoolkit-incubator/chaostoolkit-aws/.eggs/pluggy-0.10.0-py3.6.egg
    

    Bizarrely, I fail to reproduce locally.

    I'm wondering if this is something you folks have run into by any chance? Likely on my side but I fail to figure out what's different.

    Thanks for any tips!

    opened by Lawouach 25
  • Switch to importlib-metadata

    Switch to importlib-metadata

    This attempts to switch the backing metadata looking from pkg_resources (slow) to importlib-metadata (fast)

    I had to expose one derived property on top of the importlib_metadata Distribution class to make it look like the pkg_resources one -- but otherwise this should be drop-in.

    At least for pytest this isn't enough to get the full performance boost since pytest imports pkg_resources itself -- but baby steps! I'll be following up with the pytest branch in https://github.com/pytest-dev/pytest/pull/5063

    opened by asottile 22
  • Release 0.7.1

    Release 0.7.1

    OK now we are in business.

    Had to call git commit directly in the commit script because apparently gitpython bypasses the local configuration (don't have time right now to look for another solution).

    Other than that, I changed #131 and #66 from feature to trivial, as those are more internal changes than user-facing features.

    opened by nicoddemus 22
  • Document the `pluggy._Result` API

    Document the `pluggy._Result` API

    Currently pluggy._CallOutcome is implied to be part of the internal api by the leading _.

    In practice it is used by hookwrappers.

    An example:

    @pytest.hookimpl(tryfirst=True, hookwrapper=True)
    def pytest_runtest_makereport(item, call):
        outcome = yield
        rep = outcome.get_result() 
        ...
    

    The outcome is accessed directly by the user. Therefore I propose we expose it correct in plugg.__all__ and expose it in the api reference.

    opened by goodboy 20
  • high level overview

    high level overview

    The description "as used by py.test" is not very helpful. To be able to compare with other known plugin systems like Trac, SCons , Roundup or Spyder I need to answer the questions:

    1. how plugin discovery is made
    2. how plugin loading happens
    3. when plugin initialization occurs
    opened by techtonik 20
  • Deprecate __multicall__

    Deprecate __multicall__

    This starts the deprecation of __multicall__ by following the suggestions from @RonnyPfannschmidt and @hpk42 in #23:

    • if __multicall__ is detected in a hookimpl fall back to the legacy version, otherwise use the new faster implementation.

    The benchmark tests show that the new _MultiCall implementation (without the recursion previously required for wrappers) outperforms the _LegacyMultiCall after more then one hook is registered (likely the short circuting of the while loop condition is faster then the multiple try/for loop fallthroughs in the new version).

    For greater numbers of wrappers the new version significanly outperforms; it is also consistently faster with equivalent numbers of hook impls.

    Watch the CI run for the comparisons. Let me know what you guys think!

    enhancement 
    opened by goodboy 19
  • Test builds at travis failing since release of pluggy v0.12

    Test builds at travis failing since release of pluggy v0.12

    Hi all, Our test builds at StarKit began to unexpectedly fail since last 3 days. I checked this by doing some random changes in readme (https://travis-ci.org/jaladh-singhal/starkit/jobs/538727910) and they were still failing with same error:

    Traceback (most recent call last):
      File "<string>", line 1, in <module>
      File "/tmp/starkit-test-k6s1wpex/lib.linux-x86_64-3.6/starkit/_astropy_init.py", line 111, in test
        coverage=coverage, open_files=open_files, **kwargs)
      File "/home/travis/miniconda2/envs/starkit/lib/python3.6/site-packages/astropy/tests/runner.py", line 518, in run_tests
        return super(TestRunner, self).run_tests(**kwargs)
      File "/home/travis/miniconda2/envs/starkit/lib/python3.6/site-packages/astropy/tests/runner.py", line 206, in run_tests
        return pytest.main(args=args, plugins=self.keywords['plugins'])
      File "_pytest.config", line 38, in main
      File "_pytest.config", line 117, in _prepareconfig
      File "_pytest.vendored_packages.pluggy", line 724, in __call__
      File "_pytest.vendored_packages.pluggy", line 338, in _hookexec
      File "_pytest.vendored_packages.pluggy", line 333, in <lambda>
      File "_pytest.vendored_packages.pluggy", line 595, in execute
      File "_pytest.vendored_packages.pluggy", line 249, in _wrapped_call
      File "_pytest.helpconfig", line 28, in pytest_cmdline_parse
      File "_pytest.vendored_packages.pluggy", line 278, in get_result
      File "_pytest.vendored_packages.pluggy", line 264, in __init__
      File "_pytest.vendored_packages.pluggy", line 596, in execute
      File "_pytest.config", line 852, in pytest_cmdline_parse
      File "_pytest.config", line 957, in parse
      File "_pytest.config", line 918, in _preparse
      File "_pytest.vendored_packages.pluggy", line 501, in load_setuptools_entrypoints
      File "/home/travis/miniconda2/envs/starkit/lib/python3.6/site-packages/pkg_resources/__init__.py", line 2433, in load
        self.require(*args, **kwargs)
      File "/home/travis/miniconda2/envs/starkit/lib/python3.6/site-packages/pkg_resources/__init__.py", line 2456, in require
        items = working_set.resolve(reqs, env, installer, extras=self.extras)
      File "/home/travis/miniconda2/envs/starkit/lib/python3.6/site-packages/pkg_resources/__init__.py", line 791, in resolve
        raise VersionConflict(dist, req).with_context(dependent_req)
    pkg_resources.ContextualVersionConflict: (importlib-metadata 0.0.0 (/home/travis/miniconda2/envs/starkit/lib/python3.6/site-packages), Requirement.parse('importlib-metadata>=0.12'), {'pluggy'})
    The command "python setup.py $SETUP_CMD" exited with 1.
    

    I further investigated and found that pluggy has been recently updated to v0.12.0 (on May 28) and since then only our travis builds are failing that are using SETUP_CMD='test --coverage'. FYI, it is an Astropy testing framework that runs pytest by passing option coverage to generate a coverage report. As much as I could understand, I noticed that pluggy is never used by us directly, it is somehow used by pytest under the hood (I may be wrong)?! Thus I resorted here to inform you about the issue to help us solve the problem. Is it somehow related to issue https://github.com/pytest-dev/pluggy/issues/205?

    Thanks in advance. Please let's try to fix this soon :)

    opened by jaladh-singhal 18
  • Hook call mismatch warnings

    Hook call mismatch warnings

    This add's proper warnings for hook calls which do not match the corresponding hookspec's signature. The idea is to encourage callers of hooks to always keep up to date with the most recent spec.

    This comes from discussion in #15.

    @hpk42 @nicoddemus @RonnyPfannschmidt this is the first part of #34 which I have separated out to simplify the review and narrow focus.

    opened by goodboy 17
  • Split CI workflows into 'test' and 'deploy'

    Split CI workflows into 'test' and 'deploy'

    This makes them easier to maintain and follows how other pytest repositories have evolved.

    Also made some changes to the deploy workflow:

    • Using Python 3.10
    • Using build package instead of calling python setup.py
    opened by nicoddemus 5
  • hook call controller in hook specs

    hook call controller in hook specs

    currently hook specs just provide the parameters

    if it was allowed to have them provide more details, this could be a win

    a very initial draft is something like

    python
    
    @hookspec
    def pytest_something_with_paths(call_config: HookCallConfig, path: Path | None = None, oldpath: py.path.local |None = None ) -> ...
       if path is None and old_path is None:
        rasie TypeError(...)
    
       return call_config.set_parameters(....)  
    
    

    i'd love to bikeshed apis a bit

    its also a possible replacement to #151

    cc @goodboy @nicoddemus @bluetech

    enhancement discussion 
    opened by RonnyPfannschmidt 2
  • deprecate passing classes as hookspecs, it shold be instances

    deprecate passing classes as hookspecs, it shold be instances

    we currently have self magic in place to handle unbound methods and passed in classes

    ass signature (see #359) handles the bound methods so much better, we should ensure hook-specs are instances not types, and then generally handle them that way

    followup to https://github.com/pytest-dev/pluggy/pull/359#discussion_r936410218_

    deprecation 
    opened by RonnyPfannschmidt 3
  • incorrect hookimpl signature inspection for decorated functions

    incorrect hookimpl signature inspection for decorated functions

    Please see details at: https://github.com/jaraco/keyring/pull/582#issuecomment-1200450421

    Suggestion for pluggy._hooks:varnames: use inspect.signature(func) instead of getfullargspec since it has the ability to follow wrapper chains (and that's actually the default behavior): https://docs.python.org/3/library/inspect.html#inspect.signature

    As can be seen in the mentioned comment above, the current code (pluggy 1.0.0) actually breaks/limits hook-implementations.

    bug help wanted easy 
    opened by eachimei 9
  • Class based plugins and entrypoints

    Class based plugins and entrypoints

    This is not a bug report but more of a general question about pluggy.

    I know there was talks a few years ago of changing the way entry_points are loaded but it hasn't been implemented.

    As the developer of the host application, I can easily create a class that contains hook implementations (what the example from the documentation calls "A hook implementation namespace") and register it with the PluginManager but unless I'm missing something obvious, plugin authors can only define their hook implementations as functions (at least this is what all the examples I've seen seem to suggest).

    So my question is, is there any plans of allowing/facilitating "Class based plugins" to be loaded via entry_points?

    I "hacked" pluggy (just added a couple lines of code) to be able to do it for my program but maybe a solution from the developers could also be useful to others.

    opened by chevignon93 1
Minimalist BERT implementation assignment for CS11-747

minbert Assignment by Zhengbao Jiang, Shuyan Zhou, and Ritam Dutt This is an exercise in developing a minimalist version of BERT, part of Carnegie Mel

Graham Neubig 51 Jan 3, 2023
A minimalist starknet amm adapted from StarkWare's amm.

viscus • A minimalist starknet amm adapted from StarkWare's amm. Directory Structure contracts

Alucard 4 Dec 27, 2021
Meaningful and minimalist release notes for developers

Managing manual release notes is hard. Therefore, everyone tends to generate release notes from commit messages. But, you won't get a meaningful release note at the end.

codezri 31 Dec 30, 2022
A class to draw curves expressed as L-System production rules

A class to draw curves expressed as L-System production rules

Juna Salviati 6 Sep 9, 2022
A simple but flexible plugin system for Python.

PluginBase PluginBase is a module for Python that enables the development of flexible plugin systems in Python. Step 1: from pluginbase import PluginB

Armin Ronacher 1k Dec 16, 2022
A simple but flexible plugin system for Python.

PluginBase PluginBase is a module for Python that enables the development of flexible plugin systems in Python. Step 1: from pluginbase import PluginB

Armin Ronacher 935 Feb 20, 2021
Domoticz-hyundai-kia - Domoticz Hyundai-Kia plugin for Domoticz home automation system

Domoticz Hyundai-Kia plugin Author: Creasol https://www.creasol.it/domotics For

Creasol 7 Aug 3, 2022
WhyNotWin11 - Detection Script to help identify why your PC isn't Windows 11 Release Ready

WhyNotWin11 - Detection Script to help identify why your PC isn't Windows 11 Release Ready

Robert C. Maehl 5.9k Dec 31, 2022
This is a Docker-based pipeline for preparing sextractor-ready multiwavelength images

Pipeline for creating NB422-detected (ODI) catalog The repository contains a Docker-based pipeline for preprocessing observational data. The pipeline

null 1 Sep 1, 2022
Transform a Google Drive server into a VFX pipeline ready server

Google Drive VFX Server VFX Pipeline About The Project Quick tutorial to setup a Google Drive Server for multiple machines access, and VFX Pipeline on

Valentin Beaumont 17 Jun 27, 2022
Woltcheck - Python script to check if a wolt restaurant is ready to deliver to your location

woltcheck Python script to check if a wolt restaurant is ready to deliver to you

null 30 Sep 13, 2022
The earliest beta version of pytgcalls on Linux x86_64 and ARM64! Use in production at your own risk!

Public beta test. Use in production at your own risk! tgcalls - a python binding for tgcalls (c++ lib by Telegram); pytgcalls - library connecting pyt

Il'ya 21 Jan 13, 2022
Connect Playground - easy way to fill in your account with production-like objects

Just set of scripts to initialise accpunt with production-like data: A - Basic Distributor Account Initialization INPUT Distributor Account Token ACTI

CloudBlue 5 Jun 25, 2021
Estimating the potential photovoltaic production of buildings (in Berlin)

The following people contributed equally to this repository (in alphabetical order): Daniel Bumke JJX Corstiaen Versteegh This repository is forked on

Daniel Bumke 6 Feb 18, 2022
Identify unused production dependencies and avoid a bloated virtual environment.

creosote Identify unused production dependencies and avoid a bloated virtual environment. Quickstart # Install creosote in separate virtual environmen

Fredrik Averpil 7 Dec 29, 2022
A free and powerful system for awareness and research of the American judicial system.

CourtListener Started in 2009, CourtListener.com is the main initiative of Free Law Project. The goal of CourtListener.com is to provide high quality

Free Law Project 332 Dec 25, 2022
Waydroid is a container-based approach to boot a full Android system on a regular GNU/Linux system like Ubuntu.

Waydroid is a container-based approach to boot a full Android system on a regular GNU/Linux system like Ubuntu.

WayDroid 4.7k Jan 8, 2023
System Design Assignments as part of Arpit's System Design Masterclass

System Design Assignments The repository contains a set of problem statements around Software Architecture and System Design as conducted by Arpit's S

Relog 1.1k Jan 9, 2023
A Linux webcam plugin for BGMv2 as used in our demos.

The goal of this repository is to supplement the main Real-Time High Resolution Background Matting repo with a working demo of a videoconferencing plu

Andrey Ryabtsev 144 Dec 27, 2022