aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/python/pluggy
diff options
context:
space:
mode:
authorshadchin <shadchin@yandex-team.ru>2022-02-10 16:44:30 +0300
committerDaniil Cherednik <dcherednik@yandex-team.ru>2022-02-10 16:44:30 +0300
commit2598ef1d0aee359b4b6d5fdd1758916d5907d04f (patch)
tree012bb94d777798f1f56ac1cec429509766d05181 /contrib/python/pluggy
parent6751af0b0c1b952fede40b19b71da8025b5d8bcf (diff)
downloadydb-2598ef1d0aee359b4b6d5fdd1758916d5907d04f.tar.gz
Restoring authorship annotation for <shadchin@yandex-team.ru>. Commit 1 of 2.
Diffstat (limited to 'contrib/python/pluggy')
-rw-r--r--contrib/python/pluggy/py2/.dist-info/METADATA964
-rw-r--r--contrib/python/pluggy/py2/.dist-info/top_level.txt2
-rw-r--r--contrib/python/pluggy/py2/LICENSE42
-rw-r--r--contrib/python/pluggy/py2/README.rst216
-rw-r--r--contrib/python/pluggy/py2/pluggy/_tracing.py28
-rw-r--r--contrib/python/pluggy/py2/pluggy/_version.py2
-rw-r--r--contrib/python/pluggy/py2/pluggy/hooks.py74
-rw-r--r--contrib/python/pluggy/py2/pluggy/manager.py176
-rw-r--r--contrib/python/pluggy/py2/tests/benchmark.py102
-rw-r--r--contrib/python/pluggy/py2/tests/conftest.py52
-rw-r--r--contrib/python/pluggy/py2/tests/test_deprecations.py108
-rw-r--r--contrib/python/pluggy/py2/tests/test_details.py270
-rw-r--r--contrib/python/pluggy/py2/tests/test_helpers.py180
-rw-r--r--contrib/python/pluggy/py2/tests/test_hookcaller.py430
-rw-r--r--contrib/python/pluggy/py2/tests/test_invocations.py432
-rw-r--r--contrib/python/pluggy/py2/tests/test_multicall.py372
-rw-r--r--contrib/python/pluggy/py2/tests/test_pluginmanager.py1200
-rw-r--r--contrib/python/pluggy/py2/tests/test_tracer.py156
-rw-r--r--contrib/python/pluggy/py2/tests/ya.make46
-rw-r--r--contrib/python/pluggy/py2/ya.make36
-rw-r--r--contrib/python/pluggy/py3/.dist-info/METADATA286
-rw-r--r--contrib/python/pluggy/py3/.dist-info/top_level.txt2
-rw-r--r--contrib/python/pluggy/py3/LICENSE42
-rw-r--r--contrib/python/pluggy/py3/README.rst202
-rw-r--r--contrib/python/pluggy/py3/pluggy/__init__.py36
-rw-r--r--contrib/python/pluggy/py3/pluggy/_callers.py120
-rw-r--r--contrib/python/pluggy/py3/pluggy/_hooks.py650
-rw-r--r--contrib/python/pluggy/py3/pluggy/_manager.py746
-rw-r--r--contrib/python/pluggy/py3/pluggy/_result.py120
-rw-r--r--contrib/python/pluggy/py3/pluggy/_tracing.py124
-rw-r--r--contrib/python/pluggy/py3/pluggy/_version.py10
-rw-r--r--contrib/python/pluggy/py3/tests/benchmark.py204
-rw-r--r--contrib/python/pluggy/py3/tests/conftest.py52
-rw-r--r--contrib/python/pluggy/py3/tests/test_details.py270
-rw-r--r--contrib/python/pluggy/py3/tests/test_helpers.py168
-rw-r--r--contrib/python/pluggy/py3/tests/test_hookcaller.py544
-rw-r--r--contrib/python/pluggy/py3/tests/test_invocations.py430
-rw-r--r--contrib/python/pluggy/py3/tests/test_multicall.py294
-rw-r--r--contrib/python/pluggy/py3/tests/test_pluginmanager.py1088
-rw-r--r--contrib/python/pluggy/py3/tests/test_tracer.py156
-rw-r--r--contrib/python/pluggy/py3/tests/ya.make44
-rw-r--r--contrib/python/pluggy/py3/ya.make64
-rw-r--r--contrib/python/pluggy/ya.make24
43 files changed, 5282 insertions, 5282 deletions
diff --git a/contrib/python/pluggy/py2/.dist-info/METADATA b/contrib/python/pluggy/py2/.dist-info/METADATA
index cc993ee777..eaad7534d8 100644
--- a/contrib/python/pluggy/py2/.dist-info/METADATA
+++ b/contrib/python/pluggy/py2/.dist-info/METADATA
@@ -1,482 +1,482 @@
-Metadata-Version: 2.1
-Name: pluggy
-Version: 0.13.1
-Summary: plugin and hook calling mechanisms for python
-Home-page: https://github.com/pytest-dev/pluggy
-Author: Holger Krekel
-Author-email: holger@merlinux.eu
-License: MIT license
-Platform: unix
-Platform: linux
-Platform: osx
-Platform: win32
-Classifier: Development Status :: 4 - Beta
-Classifier: Intended Audience :: Developers
-Classifier: License :: OSI Approved :: MIT License
-Classifier: Operating System :: POSIX
-Classifier: Operating System :: Microsoft :: Windows
-Classifier: Operating System :: MacOS :: MacOS X
-Classifier: Topic :: Software Development :: Testing
-Classifier: Topic :: Software Development :: Libraries
-Classifier: Topic :: Utilities
-Classifier: Programming Language :: Python :: Implementation :: CPython
-Classifier: Programming Language :: Python :: Implementation :: PyPy
-Classifier: Programming Language :: Python :: 2
-Classifier: Programming Language :: Python :: 2.7
-Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.4
-Classifier: Programming Language :: Python :: 3.5
-Classifier: Programming Language :: Python :: 3.6
-Classifier: Programming Language :: Python :: 3.7
-Classifier: Programming Language :: Python :: 3.8
-Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*
-Requires-Dist: importlib-metadata (>=0.12) ; python_version < "3.8"
-Provides-Extra: dev
-Requires-Dist: pre-commit ; extra == 'dev'
-Requires-Dist: tox ; extra == 'dev'
-
-====================================================
-pluggy - A minimalist production ready plugin system
-====================================================
-
-|pypi| |conda-forge| |versions| |travis| |appveyor| |gitter| |black| |codecov|
-
-This is the core framework used by the `pytest`_, `tox`_, and `devpi`_ projects.
-
-Please `read the docs`_ to learn more!
-
-A definitive example
-====================
-.. code-block:: python
-
- import pluggy
-
- hookspec = pluggy.HookspecMarker("myproject")
- hookimpl = pluggy.HookimplMarker("myproject")
-
-
- class MySpec(object):
- """A hook specification namespace.
- """
-
- @hookspec
- def myhook(self, arg1, arg2):
- """My special little hook that you can customize.
- """
-
-
- class Plugin_1(object):
- """A hook implementation namespace.
- """
-
- @hookimpl
- def myhook(self, arg1, arg2):
- print("inside Plugin_1.myhook()")
- return arg1 + arg2
-
-
- class Plugin_2(object):
- """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]
-
-
-.. badges
-
-.. |pypi| image:: https://img.shields.io/pypi/v/pluggy.svg
- :target: https://pypi.org/pypi/pluggy
-
-.. |versions| image:: https://img.shields.io/pypi/pyversions/pluggy.svg
- :target: https://pypi.org/pypi/pluggy
-
-.. |travis| image:: https://img.shields.io/travis/pytest-dev/pluggy/master.svg
- :target: https://travis-ci.org/pytest-dev/pluggy
-
-.. |appveyor| image:: https://img.shields.io/appveyor/ci/pytestbot/pluggy/master.svg
- :target: https://ci.appveyor.com/project/pytestbot/pluggy
-
-.. |conda-forge| image:: https://img.shields.io/conda/vn/conda-forge/pluggy.svg
- :target: https://anaconda.org/conda-forge/pytest
-
-.. |gitter| image:: https://badges.gitter.im/pytest-dev/pluggy.svg
- :alt: Join the chat at https://gitter.im/pytest-dev/pluggy
- :target: https://gitter.im/pytest-dev/pluggy?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
-
-.. |black| image:: https://img.shields.io/badge/code%20style-black-000000.svg
- :target: https://github.com/ambv/black
-
-.. |codecov| image:: https://codecov.io/gh/pytest-dev/pluggy/branch/master/graph/badge.svg
- :target: https://codecov.io/gh/pytest-dev/pluggy
- :alt: Code coverage Status
-
-.. links
-.. _pytest:
- http://pytest.org
-.. _tox:
- https://tox.readthedocs.org
-.. _devpi:
- http://doc.devpi.net
-.. _read the docs:
- https://pluggy.readthedocs.io/en/latest/
-
-
-=========
-Changelog
-=========
-
-.. towncrier release notes start
-
-pluggy 0.13.1 (2019-11-21)
-==========================
-
-Trivial/Internal Changes
-------------------------
-
-- `#236 <https://github.com/pytest-dev/pluggy/pull/236>`_: Improved documentation, especially with regard to references.
-
-
-pluggy 0.13.0 (2019-09-10)
-==========================
-
-Trivial/Internal Changes
-------------------------
-
-- `#222 <https://github.com/pytest-dev/pluggy/issues/222>`_: Replace ``importlib_metadata`` backport with ``importlib.metadata`` from the
- standard library on Python 3.8+.
-
-
-pluggy 0.12.0 (2019-05-27)
-==========================
-
-Features
---------
-
-- `#215 <https://github.com/pytest-dev/pluggy/issues/215>`_: Switch from ``pkg_resources`` to ``importlib-metadata`` for entrypoint detection for improved performance and import time. This time with ``.egg`` support.
-
-
-pluggy 0.11.0 (2019-05-07)
-==========================
-
-Bug Fixes
----------
-
-- `#205 <https://github.com/pytest-dev/pluggy/issues/205>`_: Revert changes made in 0.10.0 release breaking ``.egg`` installs.
-
-
-pluggy 0.10.0 (2019-05-07)
-==========================
-
-Features
---------
-
-- `#199 <https://github.com/pytest-dev/pluggy/issues/199>`_: Switch from ``pkg_resources`` to ``importlib-metadata`` for entrypoint detection for improved performance and import time.
-
-
-pluggy 0.9.0 (2019-02-21)
-=========================
-
-Features
---------
-
-- `#189 <https://github.com/pytest-dev/pluggy/issues/189>`_: ``PluginManager.load_setuptools_entrypoints`` now accepts a ``name`` parameter that when given will
- load only entry points with that name.
-
- ``PluginManager.load_setuptools_entrypoints`` also now returns the number of plugins loaded by the
- call, as opposed to the number of all plugins loaded by all calls to this method.
-
-
-
-Bug Fixes
----------
-
-- `#187 <https://github.com/pytest-dev/pluggy/issues/187>`_: Fix internal ``varnames`` function for PyPy3.
-
-
-pluggy 0.8.1 (2018-11-09)
-=========================
-
-Trivial/Internal Changes
-------------------------
-
-- `#166 <https://github.com/pytest-dev/pluggy/issues/166>`_: Add ``stacklevel=2`` to implprefix warning so that the reported location of warning is the caller of PluginManager.
-
-
-pluggy 0.8.0 (2018-10-15)
-=========================
-
-Features
---------
-
-- `#177 <https://github.com/pytest-dev/pluggy/issues/177>`_: Add ``get_hookimpls()`` method to hook callers.
-
-
-
-Trivial/Internal Changes
-------------------------
-
-- `#165 <https://github.com/pytest-dev/pluggy/issues/165>`_: Add changelog in long package description and documentation.
-
-
-- `#172 <https://github.com/pytest-dev/pluggy/issues/172>`_: Add a test exemplifying the opt-in nature of spec defined args.
-
-
-- `#57 <https://github.com/pytest-dev/pluggy/issues/57>`_: Encapsulate hook specifications in a type for easier introspection.
-
-
-pluggy 0.7.1 (2018-07-28)
-=========================
-
-Deprecations and Removals
--------------------------
-
-- `#116 <https://github.com/pytest-dev/pluggy/issues/116>`_: Deprecate the ``implprefix`` kwarg to ``PluginManager`` and instead
- expect users to start using explicit ``HookimplMarker`` everywhere.
-
-
-
-Features
---------
-
-- `#122 <https://github.com/pytest-dev/pluggy/issues/122>`_: Add ``.plugin`` member to ``PluginValidationError`` to access failing plugin during post-mortem.
-
-
-- `#138 <https://github.com/pytest-dev/pluggy/issues/138>`_: Add per implementation warnings support for hookspecs allowing for both
- deprecation and future warnings of legacy and (future) experimental hooks
- respectively.
-
-
-
-Bug Fixes
----------
-
-- `#110 <https://github.com/pytest-dev/pluggy/issues/110>`_: Fix a bug where ``_HookCaller.call_historic()`` would call the ``proc``
- arg even when the default is ``None`` resulting in a ``TypeError``.
-
-- `#160 <https://github.com/pytest-dev/pluggy/issues/160>`_: Fix problem when handling ``VersionConflict`` errors when loading setuptools plugins.
-
-
-
-Improved Documentation
-----------------------
-
-- `#123 <https://github.com/pytest-dev/pluggy/issues/123>`_: Document how exceptions are handled and how the hook call loop
- terminates immediately on the first error which is then delivered
- to any surrounding wrappers.
-
-
-- `#136 <https://github.com/pytest-dev/pluggy/issues/136>`_: Docs rework including a much better introduction and comprehensive example
- set for new users. A big thanks goes out to @obestwalter for the great work!
-
-
-
-Trivial/Internal Changes
-------------------------
-
-- `#117 <https://github.com/pytest-dev/pluggy/issues/117>`_: Break up the main monolithic package modules into separate modules by concern
-
-
-- `#131 <https://github.com/pytest-dev/pluggy/issues/131>`_: Automate ``setuptools`` wheels building and PyPi upload using TravisCI.
-
-
-- `#153 <https://github.com/pytest-dev/pluggy/issues/153>`_: Reorganize tests more appropriately by modules relating to each
- internal component/feature. This is in an effort to avoid (future)
- duplication and better separation of concerns in the test set.
-
-
-- `#156 <https://github.com/pytest-dev/pluggy/issues/156>`_: Add ``HookImpl.__repr__()`` for better debugging.
-
-
-- `#66 <https://github.com/pytest-dev/pluggy/issues/66>`_: Start using ``towncrier`` and a custom ``tox`` environment to prepare releases!
-
-
-pluggy 0.7.0 (Unreleased)
-=========================
-
-* `#160 <https://github.com/pytest-dev/pluggy/issues/160>`_: We discovered a deployment issue so this version was never released to PyPI, only the tag exists.
-
-pluggy 0.6.0 (2017-11-24)
-=========================
-
-- Add CI testing for the features, release, and master
- branches of ``pytest`` (PR `#79`_).
-- Document public API for ``_Result`` objects passed to wrappers
- (PR `#85`_).
-- Document and test hook LIFO ordering (PR `#85`_).
-- Turn warnings into errors in test suite (PR `#89`_).
-- Deprecate ``_Result.result`` (PR `#88`_).
-- Convert ``_Multicall`` to a simple function distinguishing it from
- the legacy version (PR `#90`_).
-- Resolve E741 errors (PR `#96`_).
-- Test and bug fix for unmarked hook collection (PRs `#97`_ and
- `#102`_).
-- Drop support for EOL Python 2.6 and 3.3 (PR `#103`_).
-- Fix ``inspect`` based arg introspection on py3.6 (PR `#94`_).
-
-.. _#79: https://github.com/pytest-dev/pluggy/pull/79
-.. _#85: https://github.com/pytest-dev/pluggy/pull/85
-.. _#88: https://github.com/pytest-dev/pluggy/pull/88
-.. _#89: https://github.com/pytest-dev/pluggy/pull/89
-.. _#90: https://github.com/pytest-dev/pluggy/pull/90
-.. _#94: https://github.com/pytest-dev/pluggy/pull/94
-.. _#96: https://github.com/pytest-dev/pluggy/pull/96
-.. _#97: https://github.com/pytest-dev/pluggy/pull/97
-.. _#102: https://github.com/pytest-dev/pluggy/pull/102
-.. _#103: https://github.com/pytest-dev/pluggy/pull/103
-
-
-pluggy 0.5.2 (2017-09-06)
-=========================
-
-- fix bug where ``firstresult`` wrappers were being sent an incorrectly configured
- ``_Result`` (a list was set instead of a single value). Add tests to check for
- this as well as ``_Result.force_result()`` behaviour. Thanks to `@tgoodlet`_
- for the PR `#72`_.
-
-- fix incorrect ``getattr`` of ``DeprecationWarning`` from the ``warnings``
- module. Thanks to `@nicoddemus`_ for the PR `#77`_.
-
-- hide ``pytest`` tracebacks in certain core routines. Thanks to
- `@nicoddemus`_ for the PR `#80`_.
-
-.. _#72: https://github.com/pytest-dev/pluggy/pull/72
-.. _#77: https://github.com/pytest-dev/pluggy/pull/77
-.. _#80: https://github.com/pytest-dev/pluggy/pull/80
-
-
-pluggy 0.5.1 (2017-08-29)
-=========================
-
-- fix a bug and add tests for case where ``firstresult`` hooks return
- ``None`` results. Thanks to `@RonnyPfannschmidt`_ and `@tgoodlet`_
- for the issue (`#68`_) and PR (`#69`_) respectively.
-
-.. _#69: https://github.com/pytest-dev/pluggy/pull/69
-.. _#68: https://github.com/pytest-dev/pluggy/issues/68
-
-
-pluggy 0.5.0 (2017-08-28)
-=========================
-
-- fix bug where callbacks for historic hooks would not be called for
- already registered plugins. Thanks `@vodik`_ for the PR
- and `@hpk42`_ for further fixes.
-
-- fix `#17`_ by considering only actual functions for hooks
- this removes the ability to register arbitrary callable objects
- which at first glance is a reasonable simplification,
- thanks `@RonnyPfannschmidt`_ for report and pr.
-
-- fix `#19`_: allow registering hookspecs from instances. The PR from
- `@tgoodlet`_ also modernized the varnames implementation.
-
-- resolve `#32`_: split up the test set into multiple modules.
- Thanks to `@RonnyPfannschmidt`_ for the PR and `@tgoodlet`_ for
- the initial request.
-
-- resolve `#14`_: add full sphinx docs. Thanks to `@tgoodlet`_ for
- PR `#39`_.
-
-- add hook call mismatch warnings. Thanks to `@tgoodlet`_ for the
- PR `#42`_.
-
-- resolve `#44`_: move to new-style classes. Thanks to `@MichalTHEDUDE`_
- for PR `#46`_.
-
-- add baseline benchmarking/speed tests using ``pytest-benchmark``
- in PR `#54`_. Thanks to `@tgoodlet`_.
-
-- update the README to showcase the API. Thanks to `@tgoodlet`_ for the
- issue and PR `#55`_.
-
-- deprecate ``__multicall__`` and add a faster call loop implementation.
- Thanks to `@tgoodlet`_ for PR `#58`_.
-
-- raise a comprehensible error when a ``hookimpl`` is called with positional
- args. Thanks to `@RonnyPfannschmidt`_ for the issue and `@tgoodlet`_ for
- PR `#60`_.
-
-- fix the ``firstresult`` test making it more complete
- and remove a duplicate of that test. Thanks to `@tgoodlet`_
- for PR `#62`_.
-
-.. _#62: https://github.com/pytest-dev/pluggy/pull/62
-.. _#60: https://github.com/pytest-dev/pluggy/pull/60
-.. _#58: https://github.com/pytest-dev/pluggy/pull/58
-.. _#55: https://github.com/pytest-dev/pluggy/pull/55
-.. _#54: https://github.com/pytest-dev/pluggy/pull/54
-.. _#46: https://github.com/pytest-dev/pluggy/pull/46
-.. _#44: https://github.com/pytest-dev/pluggy/issues/44
-.. _#42: https://github.com/pytest-dev/pluggy/pull/42
-.. _#39: https://github.com/pytest-dev/pluggy/pull/39
-.. _#32: https://github.com/pytest-dev/pluggy/pull/32
-.. _#19: https://github.com/pytest-dev/pluggy/issues/19
-.. _#17: https://github.com/pytest-dev/pluggy/issues/17
-.. _#14: https://github.com/pytest-dev/pluggy/issues/14
-
-
-pluggy 0.4.0 (2016-09-25)
-=========================
-
-- add ``has_plugin(name)`` method to pluginmanager. thanks `@nicoddemus`_.
-
-- fix `#11`_: make plugin parsing more resilient against exceptions
- from ``__getattr__`` functions. Thanks `@nicoddemus`_.
-
-- fix issue `#4`_: specific ``HookCallError`` exception for when a hook call
- provides not enough arguments.
-
-- better error message when loading setuptools entrypoints fails
- due to a ``VersionConflict``. Thanks `@blueyed`_.
-
-.. _#11: https://github.com/pytest-dev/pluggy/issues/11
-.. _#4: https://github.com/pytest-dev/pluggy/issues/4
-
-
-pluggy 0.3.1 (2015-09-17)
-=========================
-
-- avoid using deprecated-in-python3.5 getargspec method. Thanks
- `@mdboom`_.
-
-
-pluggy 0.3.0 (2015-05-07)
-=========================
-
-initial release
-
-.. contributors
-.. _@hpk42: https://github.com/hpk42
-.. _@tgoodlet: https://github.com/goodboy
-.. _@MichalTHEDUDE: https://github.com/MichalTHEDUDE
-.. _@vodik: https://github.com/vodik
-.. _@RonnyPfannschmidt: https://github.com/RonnyPfannschmidt
-.. _@blueyed: https://github.com/blueyed
-.. _@nicoddemus: https://github.com/nicoddemus
-.. _@mdboom: https://github.com/mdboom
-
-
+Metadata-Version: 2.1
+Name: pluggy
+Version: 0.13.1
+Summary: plugin and hook calling mechanisms for python
+Home-page: https://github.com/pytest-dev/pluggy
+Author: Holger Krekel
+Author-email: holger@merlinux.eu
+License: MIT license
+Platform: unix
+Platform: linux
+Platform: osx
+Platform: win32
+Classifier: Development Status :: 4 - Beta
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Operating System :: POSIX
+Classifier: Operating System :: Microsoft :: Windows
+Classifier: Operating System :: MacOS :: MacOS X
+Classifier: Topic :: Software Development :: Testing
+Classifier: Topic :: Software Development :: Libraries
+Classifier: Topic :: Utilities
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.4
+Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: 3.8
+Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*
+Requires-Dist: importlib-metadata (>=0.12) ; python_version < "3.8"
+Provides-Extra: dev
+Requires-Dist: pre-commit ; extra == 'dev'
+Requires-Dist: tox ; extra == 'dev'
+
+====================================================
+pluggy - A minimalist production ready plugin system
+====================================================
+
+|pypi| |conda-forge| |versions| |travis| |appveyor| |gitter| |black| |codecov|
+
+This is the core framework used by the `pytest`_, `tox`_, and `devpi`_ projects.
+
+Please `read the docs`_ to learn more!
+
+A definitive example
+====================
+.. code-block:: python
+
+ import pluggy
+
+ hookspec = pluggy.HookspecMarker("myproject")
+ hookimpl = pluggy.HookimplMarker("myproject")
+
+
+ class MySpec(object):
+ """A hook specification namespace.
+ """
+
+ @hookspec
+ def myhook(self, arg1, arg2):
+ """My special little hook that you can customize.
+ """
+
+
+ class Plugin_1(object):
+ """A hook implementation namespace.
+ """
+
+ @hookimpl
+ def myhook(self, arg1, arg2):
+ print("inside Plugin_1.myhook()")
+ return arg1 + arg2
+
+
+ class Plugin_2(object):
+ """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]
+
+
+.. badges
+
+.. |pypi| image:: https://img.shields.io/pypi/v/pluggy.svg
+ :target: https://pypi.org/pypi/pluggy
+
+.. |versions| image:: https://img.shields.io/pypi/pyversions/pluggy.svg
+ :target: https://pypi.org/pypi/pluggy
+
+.. |travis| image:: https://img.shields.io/travis/pytest-dev/pluggy/master.svg
+ :target: https://travis-ci.org/pytest-dev/pluggy
+
+.. |appveyor| image:: https://img.shields.io/appveyor/ci/pytestbot/pluggy/master.svg
+ :target: https://ci.appveyor.com/project/pytestbot/pluggy
+
+.. |conda-forge| image:: https://img.shields.io/conda/vn/conda-forge/pluggy.svg
+ :target: https://anaconda.org/conda-forge/pytest
+
+.. |gitter| image:: https://badges.gitter.im/pytest-dev/pluggy.svg
+ :alt: Join the chat at https://gitter.im/pytest-dev/pluggy
+ :target: https://gitter.im/pytest-dev/pluggy?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
+
+.. |black| image:: https://img.shields.io/badge/code%20style-black-000000.svg
+ :target: https://github.com/ambv/black
+
+.. |codecov| image:: https://codecov.io/gh/pytest-dev/pluggy/branch/master/graph/badge.svg
+ :target: https://codecov.io/gh/pytest-dev/pluggy
+ :alt: Code coverage Status
+
+.. links
+.. _pytest:
+ http://pytest.org
+.. _tox:
+ https://tox.readthedocs.org
+.. _devpi:
+ http://doc.devpi.net
+.. _read the docs:
+ https://pluggy.readthedocs.io/en/latest/
+
+
+=========
+Changelog
+=========
+
+.. towncrier release notes start
+
+pluggy 0.13.1 (2019-11-21)
+==========================
+
+Trivial/Internal Changes
+------------------------
+
+- `#236 <https://github.com/pytest-dev/pluggy/pull/236>`_: Improved documentation, especially with regard to references.
+
+
+pluggy 0.13.0 (2019-09-10)
+==========================
+
+Trivial/Internal Changes
+------------------------
+
+- `#222 <https://github.com/pytest-dev/pluggy/issues/222>`_: Replace ``importlib_metadata`` backport with ``importlib.metadata`` from the
+ standard library on Python 3.8+.
+
+
+pluggy 0.12.0 (2019-05-27)
+==========================
+
+Features
+--------
+
+- `#215 <https://github.com/pytest-dev/pluggy/issues/215>`_: Switch from ``pkg_resources`` to ``importlib-metadata`` for entrypoint detection for improved performance and import time. This time with ``.egg`` support.
+
+
+pluggy 0.11.0 (2019-05-07)
+==========================
+
+Bug Fixes
+---------
+
+- `#205 <https://github.com/pytest-dev/pluggy/issues/205>`_: Revert changes made in 0.10.0 release breaking ``.egg`` installs.
+
+
+pluggy 0.10.0 (2019-05-07)
+==========================
+
+Features
+--------
+
+- `#199 <https://github.com/pytest-dev/pluggy/issues/199>`_: Switch from ``pkg_resources`` to ``importlib-metadata`` for entrypoint detection for improved performance and import time.
+
+
+pluggy 0.9.0 (2019-02-21)
+=========================
+
+Features
+--------
+
+- `#189 <https://github.com/pytest-dev/pluggy/issues/189>`_: ``PluginManager.load_setuptools_entrypoints`` now accepts a ``name`` parameter that when given will
+ load only entry points with that name.
+
+ ``PluginManager.load_setuptools_entrypoints`` also now returns the number of plugins loaded by the
+ call, as opposed to the number of all plugins loaded by all calls to this method.
+
+
+
+Bug Fixes
+---------
+
+- `#187 <https://github.com/pytest-dev/pluggy/issues/187>`_: Fix internal ``varnames`` function for PyPy3.
+
+
+pluggy 0.8.1 (2018-11-09)
+=========================
+
+Trivial/Internal Changes
+------------------------
+
+- `#166 <https://github.com/pytest-dev/pluggy/issues/166>`_: Add ``stacklevel=2`` to implprefix warning so that the reported location of warning is the caller of PluginManager.
+
+
+pluggy 0.8.0 (2018-10-15)
+=========================
+
+Features
+--------
+
+- `#177 <https://github.com/pytest-dev/pluggy/issues/177>`_: Add ``get_hookimpls()`` method to hook callers.
+
+
+
+Trivial/Internal Changes
+------------------------
+
+- `#165 <https://github.com/pytest-dev/pluggy/issues/165>`_: Add changelog in long package description and documentation.
+
+
+- `#172 <https://github.com/pytest-dev/pluggy/issues/172>`_: Add a test exemplifying the opt-in nature of spec defined args.
+
+
+- `#57 <https://github.com/pytest-dev/pluggy/issues/57>`_: Encapsulate hook specifications in a type for easier introspection.
+
+
+pluggy 0.7.1 (2018-07-28)
+=========================
+
+Deprecations and Removals
+-------------------------
+
+- `#116 <https://github.com/pytest-dev/pluggy/issues/116>`_: Deprecate the ``implprefix`` kwarg to ``PluginManager`` and instead
+ expect users to start using explicit ``HookimplMarker`` everywhere.
+
+
+
+Features
+--------
+
+- `#122 <https://github.com/pytest-dev/pluggy/issues/122>`_: Add ``.plugin`` member to ``PluginValidationError`` to access failing plugin during post-mortem.
+
+
+- `#138 <https://github.com/pytest-dev/pluggy/issues/138>`_: Add per implementation warnings support for hookspecs allowing for both
+ deprecation and future warnings of legacy and (future) experimental hooks
+ respectively.
+
+
+
+Bug Fixes
+---------
+
+- `#110 <https://github.com/pytest-dev/pluggy/issues/110>`_: Fix a bug where ``_HookCaller.call_historic()`` would call the ``proc``
+ arg even when the default is ``None`` resulting in a ``TypeError``.
+
+- `#160 <https://github.com/pytest-dev/pluggy/issues/160>`_: Fix problem when handling ``VersionConflict`` errors when loading setuptools plugins.
+
+
+
+Improved Documentation
+----------------------
+
+- `#123 <https://github.com/pytest-dev/pluggy/issues/123>`_: Document how exceptions are handled and how the hook call loop
+ terminates immediately on the first error which is then delivered
+ to any surrounding wrappers.
+
+
+- `#136 <https://github.com/pytest-dev/pluggy/issues/136>`_: Docs rework including a much better introduction and comprehensive example
+ set for new users. A big thanks goes out to @obestwalter for the great work!
+
+
+
+Trivial/Internal Changes
+------------------------
+
+- `#117 <https://github.com/pytest-dev/pluggy/issues/117>`_: Break up the main monolithic package modules into separate modules by concern
+
+
+- `#131 <https://github.com/pytest-dev/pluggy/issues/131>`_: Automate ``setuptools`` wheels building and PyPi upload using TravisCI.
+
+
+- `#153 <https://github.com/pytest-dev/pluggy/issues/153>`_: Reorganize tests more appropriately by modules relating to each
+ internal component/feature. This is in an effort to avoid (future)
+ duplication and better separation of concerns in the test set.
+
+
+- `#156 <https://github.com/pytest-dev/pluggy/issues/156>`_: Add ``HookImpl.__repr__()`` for better debugging.
+
+
+- `#66 <https://github.com/pytest-dev/pluggy/issues/66>`_: Start using ``towncrier`` and a custom ``tox`` environment to prepare releases!
+
+
+pluggy 0.7.0 (Unreleased)
+=========================
+
+* `#160 <https://github.com/pytest-dev/pluggy/issues/160>`_: We discovered a deployment issue so this version was never released to PyPI, only the tag exists.
+
+pluggy 0.6.0 (2017-11-24)
+=========================
+
+- Add CI testing for the features, release, and master
+ branches of ``pytest`` (PR `#79`_).
+- Document public API for ``_Result`` objects passed to wrappers
+ (PR `#85`_).
+- Document and test hook LIFO ordering (PR `#85`_).
+- Turn warnings into errors in test suite (PR `#89`_).
+- Deprecate ``_Result.result`` (PR `#88`_).
+- Convert ``_Multicall`` to a simple function distinguishing it from
+ the legacy version (PR `#90`_).
+- Resolve E741 errors (PR `#96`_).
+- Test and bug fix for unmarked hook collection (PRs `#97`_ and
+ `#102`_).
+- Drop support for EOL Python 2.6 and 3.3 (PR `#103`_).
+- Fix ``inspect`` based arg introspection on py3.6 (PR `#94`_).
+
+.. _#79: https://github.com/pytest-dev/pluggy/pull/79
+.. _#85: https://github.com/pytest-dev/pluggy/pull/85
+.. _#88: https://github.com/pytest-dev/pluggy/pull/88
+.. _#89: https://github.com/pytest-dev/pluggy/pull/89
+.. _#90: https://github.com/pytest-dev/pluggy/pull/90
+.. _#94: https://github.com/pytest-dev/pluggy/pull/94
+.. _#96: https://github.com/pytest-dev/pluggy/pull/96
+.. _#97: https://github.com/pytest-dev/pluggy/pull/97
+.. _#102: https://github.com/pytest-dev/pluggy/pull/102
+.. _#103: https://github.com/pytest-dev/pluggy/pull/103
+
+
+pluggy 0.5.2 (2017-09-06)
+=========================
+
+- fix bug where ``firstresult`` wrappers were being sent an incorrectly configured
+ ``_Result`` (a list was set instead of a single value). Add tests to check for
+ this as well as ``_Result.force_result()`` behaviour. Thanks to `@tgoodlet`_
+ for the PR `#72`_.
+
+- fix incorrect ``getattr`` of ``DeprecationWarning`` from the ``warnings``
+ module. Thanks to `@nicoddemus`_ for the PR `#77`_.
+
+- hide ``pytest`` tracebacks in certain core routines. Thanks to
+ `@nicoddemus`_ for the PR `#80`_.
+
+.. _#72: https://github.com/pytest-dev/pluggy/pull/72
+.. _#77: https://github.com/pytest-dev/pluggy/pull/77
+.. _#80: https://github.com/pytest-dev/pluggy/pull/80
+
+
+pluggy 0.5.1 (2017-08-29)
+=========================
+
+- fix a bug and add tests for case where ``firstresult`` hooks return
+ ``None`` results. Thanks to `@RonnyPfannschmidt`_ and `@tgoodlet`_
+ for the issue (`#68`_) and PR (`#69`_) respectively.
+
+.. _#69: https://github.com/pytest-dev/pluggy/pull/69
+.. _#68: https://github.com/pytest-dev/pluggy/issues/68
+
+
+pluggy 0.5.0 (2017-08-28)
+=========================
+
+- fix bug where callbacks for historic hooks would not be called for
+ already registered plugins. Thanks `@vodik`_ for the PR
+ and `@hpk42`_ for further fixes.
+
+- fix `#17`_ by considering only actual functions for hooks
+ this removes the ability to register arbitrary callable objects
+ which at first glance is a reasonable simplification,
+ thanks `@RonnyPfannschmidt`_ for report and pr.
+
+- fix `#19`_: allow registering hookspecs from instances. The PR from
+ `@tgoodlet`_ also modernized the varnames implementation.
+
+- resolve `#32`_: split up the test set into multiple modules.
+ Thanks to `@RonnyPfannschmidt`_ for the PR and `@tgoodlet`_ for
+ the initial request.
+
+- resolve `#14`_: add full sphinx docs. Thanks to `@tgoodlet`_ for
+ PR `#39`_.
+
+- add hook call mismatch warnings. Thanks to `@tgoodlet`_ for the
+ PR `#42`_.
+
+- resolve `#44`_: move to new-style classes. Thanks to `@MichalTHEDUDE`_
+ for PR `#46`_.
+
+- add baseline benchmarking/speed tests using ``pytest-benchmark``
+ in PR `#54`_. Thanks to `@tgoodlet`_.
+
+- update the README to showcase the API. Thanks to `@tgoodlet`_ for the
+ issue and PR `#55`_.
+
+- deprecate ``__multicall__`` and add a faster call loop implementation.
+ Thanks to `@tgoodlet`_ for PR `#58`_.
+
+- raise a comprehensible error when a ``hookimpl`` is called with positional
+ args. Thanks to `@RonnyPfannschmidt`_ for the issue and `@tgoodlet`_ for
+ PR `#60`_.
+
+- fix the ``firstresult`` test making it more complete
+ and remove a duplicate of that test. Thanks to `@tgoodlet`_
+ for PR `#62`_.
+
+.. _#62: https://github.com/pytest-dev/pluggy/pull/62
+.. _#60: https://github.com/pytest-dev/pluggy/pull/60
+.. _#58: https://github.com/pytest-dev/pluggy/pull/58
+.. _#55: https://github.com/pytest-dev/pluggy/pull/55
+.. _#54: https://github.com/pytest-dev/pluggy/pull/54
+.. _#46: https://github.com/pytest-dev/pluggy/pull/46
+.. _#44: https://github.com/pytest-dev/pluggy/issues/44
+.. _#42: https://github.com/pytest-dev/pluggy/pull/42
+.. _#39: https://github.com/pytest-dev/pluggy/pull/39
+.. _#32: https://github.com/pytest-dev/pluggy/pull/32
+.. _#19: https://github.com/pytest-dev/pluggy/issues/19
+.. _#17: https://github.com/pytest-dev/pluggy/issues/17
+.. _#14: https://github.com/pytest-dev/pluggy/issues/14
+
+
+pluggy 0.4.0 (2016-09-25)
+=========================
+
+- add ``has_plugin(name)`` method to pluginmanager. thanks `@nicoddemus`_.
+
+- fix `#11`_: make plugin parsing more resilient against exceptions
+ from ``__getattr__`` functions. Thanks `@nicoddemus`_.
+
+- fix issue `#4`_: specific ``HookCallError`` exception for when a hook call
+ provides not enough arguments.
+
+- better error message when loading setuptools entrypoints fails
+ due to a ``VersionConflict``. Thanks `@blueyed`_.
+
+.. _#11: https://github.com/pytest-dev/pluggy/issues/11
+.. _#4: https://github.com/pytest-dev/pluggy/issues/4
+
+
+pluggy 0.3.1 (2015-09-17)
+=========================
+
+- avoid using deprecated-in-python3.5 getargspec method. Thanks
+ `@mdboom`_.
+
+
+pluggy 0.3.0 (2015-05-07)
+=========================
+
+initial release
+
+.. contributors
+.. _@hpk42: https://github.com/hpk42
+.. _@tgoodlet: https://github.com/goodboy
+.. _@MichalTHEDUDE: https://github.com/MichalTHEDUDE
+.. _@vodik: https://github.com/vodik
+.. _@RonnyPfannschmidt: https://github.com/RonnyPfannschmidt
+.. _@blueyed: https://github.com/blueyed
+.. _@nicoddemus: https://github.com/nicoddemus
+.. _@mdboom: https://github.com/mdboom
+
+
diff --git a/contrib/python/pluggy/py2/.dist-info/top_level.txt b/contrib/python/pluggy/py2/.dist-info/top_level.txt
index 11bdb5c1f5..b78af1723b 100644
--- a/contrib/python/pluggy/py2/.dist-info/top_level.txt
+++ b/contrib/python/pluggy/py2/.dist-info/top_level.txt
@@ -1 +1 @@
-pluggy
+pluggy
diff --git a/contrib/python/pluggy/py2/LICENSE b/contrib/python/pluggy/py2/LICENSE
index 85f4dd63d2..58f08de42e 100644
--- a/contrib/python/pluggy/py2/LICENSE
+++ b/contrib/python/pluggy/py2/LICENSE
@@ -1,21 +1,21 @@
-The MIT License (MIT)
-
-Copyright (c) 2015 holger krekel (rather uses bitbucket/hpk42)
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
+The MIT License (MIT)
+
+Copyright (c) 2015 holger krekel (rather uses bitbucket/hpk42)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/contrib/python/pluggy/py2/README.rst b/contrib/python/pluggy/py2/README.rst
index b79ef02460..a226e9f8b1 100644
--- a/contrib/python/pluggy/py2/README.rst
+++ b/contrib/python/pluggy/py2/README.rst
@@ -1,108 +1,108 @@
-====================================================
-pluggy - A minimalist production ready plugin system
-====================================================
-
-|pypi| |conda-forge| |versions| |travis| |appveyor| |gitter| |black| |codecov|
-
-This is the core framework used by the `pytest`_, `tox`_, and `devpi`_ projects.
-
-Please `read the docs`_ to learn more!
-
-A definitive example
-====================
-.. code-block:: python
-
- import pluggy
-
- hookspec = pluggy.HookspecMarker("myproject")
- hookimpl = pluggy.HookimplMarker("myproject")
-
-
- class MySpec(object):
- """A hook specification namespace.
- """
-
- @hookspec
- def myhook(self, arg1, arg2):
- """My special little hook that you can customize.
- """
-
-
- class Plugin_1(object):
- """A hook implementation namespace.
- """
-
- @hookimpl
- def myhook(self, arg1, arg2):
- print("inside Plugin_1.myhook()")
- return arg1 + arg2
-
-
- class Plugin_2(object):
- """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]
-
-
-.. badges
-
-.. |pypi| image:: https://img.shields.io/pypi/v/pluggy.svg
- :target: https://pypi.org/pypi/pluggy
-
-.. |versions| image:: https://img.shields.io/pypi/pyversions/pluggy.svg
- :target: https://pypi.org/pypi/pluggy
-
-.. |travis| image:: https://img.shields.io/travis/pytest-dev/pluggy/master.svg
- :target: https://travis-ci.org/pytest-dev/pluggy
-
-.. |appveyor| image:: https://img.shields.io/appveyor/ci/pytestbot/pluggy/master.svg
- :target: https://ci.appveyor.com/project/pytestbot/pluggy
-
-.. |conda-forge| image:: https://img.shields.io/conda/vn/conda-forge/pluggy.svg
- :target: https://anaconda.org/conda-forge/pytest
-
-.. |gitter| image:: https://badges.gitter.im/pytest-dev/pluggy.svg
- :alt: Join the chat at https://gitter.im/pytest-dev/pluggy
- :target: https://gitter.im/pytest-dev/pluggy?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
-
-.. |black| image:: https://img.shields.io/badge/code%20style-black-000000.svg
- :target: https://github.com/ambv/black
-
-.. |codecov| image:: https://codecov.io/gh/pytest-dev/pluggy/branch/master/graph/badge.svg
- :target: https://codecov.io/gh/pytest-dev/pluggy
- :alt: Code coverage Status
-
-.. links
-.. _pytest:
- http://pytest.org
-.. _tox:
- https://tox.readthedocs.org
-.. _devpi:
- http://doc.devpi.net
-.. _read the docs:
- https://pluggy.readthedocs.io/en/latest/
+====================================================
+pluggy - A minimalist production ready plugin system
+====================================================
+
+|pypi| |conda-forge| |versions| |travis| |appveyor| |gitter| |black| |codecov|
+
+This is the core framework used by the `pytest`_, `tox`_, and `devpi`_ projects.
+
+Please `read the docs`_ to learn more!
+
+A definitive example
+====================
+.. code-block:: python
+
+ import pluggy
+
+ hookspec = pluggy.HookspecMarker("myproject")
+ hookimpl = pluggy.HookimplMarker("myproject")
+
+
+ class MySpec(object):
+ """A hook specification namespace.
+ """
+
+ @hookspec
+ def myhook(self, arg1, arg2):
+ """My special little hook that you can customize.
+ """
+
+
+ class Plugin_1(object):
+ """A hook implementation namespace.
+ """
+
+ @hookimpl
+ def myhook(self, arg1, arg2):
+ print("inside Plugin_1.myhook()")
+ return arg1 + arg2
+
+
+ class Plugin_2(object):
+ """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]
+
+
+.. badges
+
+.. |pypi| image:: https://img.shields.io/pypi/v/pluggy.svg
+ :target: https://pypi.org/pypi/pluggy
+
+.. |versions| image:: https://img.shields.io/pypi/pyversions/pluggy.svg
+ :target: https://pypi.org/pypi/pluggy
+
+.. |travis| image:: https://img.shields.io/travis/pytest-dev/pluggy/master.svg
+ :target: https://travis-ci.org/pytest-dev/pluggy
+
+.. |appveyor| image:: https://img.shields.io/appveyor/ci/pytestbot/pluggy/master.svg
+ :target: https://ci.appveyor.com/project/pytestbot/pluggy
+
+.. |conda-forge| image:: https://img.shields.io/conda/vn/conda-forge/pluggy.svg
+ :target: https://anaconda.org/conda-forge/pytest
+
+.. |gitter| image:: https://badges.gitter.im/pytest-dev/pluggy.svg
+ :alt: Join the chat at https://gitter.im/pytest-dev/pluggy
+ :target: https://gitter.im/pytest-dev/pluggy?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
+
+.. |black| image:: https://img.shields.io/badge/code%20style-black-000000.svg
+ :target: https://github.com/ambv/black
+
+.. |codecov| image:: https://codecov.io/gh/pytest-dev/pluggy/branch/master/graph/badge.svg
+ :target: https://codecov.io/gh/pytest-dev/pluggy
+ :alt: Code coverage Status
+
+.. links
+.. _pytest:
+ http://pytest.org
+.. _tox:
+ https://tox.readthedocs.org
+.. _devpi:
+ http://doc.devpi.net
+.. _read the docs:
+ https://pluggy.readthedocs.io/en/latest/
diff --git a/contrib/python/pluggy/py2/pluggy/_tracing.py b/contrib/python/pluggy/py2/pluggy/_tracing.py
index 8b9715f026..b041b7fa0b 100644
--- a/contrib/python/pluggy/py2/pluggy/_tracing.py
+++ b/contrib/python/pluggy/py2/pluggy/_tracing.py
@@ -5,14 +5,14 @@ Tracing utils
class TagTracer(object):
def __init__(self):
- self._tags2proc = {}
- self._writer = None
+ self._tags2proc = {}
+ self._writer = None
self.indent = 0
def get(self, name):
return TagTracerSub(self, (name,))
- def _format_message(self, tags, args):
+ def _format_message(self, tags, args):
if isinstance(args[-1], dict):
extra = args[-1]
args = args[:-1]
@@ -27,27 +27,27 @@ class TagTracer(object):
for name, value in extra.items():
lines.append("%s %s: %s\n" % (indent, name, value))
- return "".join(lines)
-
- def _processmessage(self, tags, args):
- if self._writer is not None and args:
- self._writer(self._format_message(tags, args))
+ return "".join(lines)
+
+ def _processmessage(self, tags, args):
+ if self._writer is not None and args:
+ self._writer(self._format_message(tags, args))
try:
- processor = self._tags2proc[tags]
+ processor = self._tags2proc[tags]
except KeyError:
pass
- else:
- processor(tags, args)
+ else:
+ processor(tags, args)
def setwriter(self, writer):
- self._writer = writer
+ self._writer = writer
def setprocessor(self, tags, processor):
if isinstance(tags, str):
tags = tuple(tags.split(":"))
else:
assert isinstance(tags, tuple)
- self._tags2proc[tags] = processor
+ self._tags2proc[tags] = processor
class TagTracerSub(object):
@@ -56,7 +56,7 @@ class TagTracerSub(object):
self.tags = tags
def __call__(self, *args):
- self.root._processmessage(self.tags, args)
+ self.root._processmessage(self.tags, args)
def get(self, name):
return self.__class__(self.root, self.tags + (name,))
diff --git a/contrib/python/pluggy/py2/pluggy/_version.py b/contrib/python/pluggy/py2/pluggy/_version.py
index 2ba90cb83e..846afe23bf 100644
--- a/contrib/python/pluggy/py2/pluggy/_version.py
+++ b/contrib/python/pluggy/py2/pluggy/_version.py
@@ -1,4 +1,4 @@
# coding: utf-8
# file generated by setuptools_scm
# don't change, don't track in version control
-version = '0.13.1'
+version = '0.13.1'
diff --git a/contrib/python/pluggy/py2/pluggy/hooks.py b/contrib/python/pluggy/py2/pluggy/hooks.py
index 0a1c287198..fa66eb80df 100644
--- a/contrib/python/pluggy/py2/pluggy/hooks.py
+++ b/contrib/python/pluggy/py2/pluggy/hooks.py
@@ -2,7 +2,7 @@
Internal hook annotation, representation and calling machinery.
"""
import inspect
-import sys
+import sys
import warnings
from .callers import _legacymulticall, _multicall
@@ -11,8 +11,8 @@ class HookspecMarker(object):
""" Decorator helper class for marking functions as hook specifications.
You can instantiate it with a project_name to get a decorator.
- Calling :py:meth:`.PluginManager.add_hookspecs` later will discover all marked functions
- if the :py:class:`.PluginManager` uses the same project_name.
+ Calling :py:meth:`.PluginManager.add_hookspecs` later will discover all marked functions
+ if the :py:class:`.PluginManager` uses the same project_name.
"""
def __init__(self, project_name):
@@ -22,15 +22,15 @@ class HookspecMarker(object):
self, function=None, firstresult=False, historic=False, warn_on_impl=None
):
""" if passed a function, directly sets attributes on the function
- which will make it discoverable to :py:meth:`.PluginManager.add_hookspecs`.
- If passed no function, returns a decorator which can be applied to a function
+ which will make it discoverable to :py:meth:`.PluginManager.add_hookspecs`.
+ If passed no function, returns a decorator which can be applied to a function
later using the attributes supplied.
- If ``firstresult`` is ``True`` the 1:N hook call (N being the number of registered
+ If ``firstresult`` is ``True`` the 1:N hook call (N being the number of registered
hook implementation functions) will stop at I<=N when the I'th function
- returns a non-``None`` result.
+ returns a non-``None`` result.
- If ``historic`` is ``True`` calls to a hook will be memorized and replayed
+ If ``historic`` is ``True`` calls to a hook will be memorized and replayed
on later registered plugins.
"""
@@ -58,9 +58,9 @@ class HookspecMarker(object):
class HookimplMarker(object):
""" Decorator helper class for marking functions as hook implementations.
- You can instantiate with a ``project_name`` to get a decorator.
- Calling :py:meth:`.PluginManager.register` later will discover all marked functions
- if the :py:class:`.PluginManager` uses the same project_name.
+ You can instantiate with a ``project_name`` to get a decorator.
+ Calling :py:meth:`.PluginManager.register` later will discover all marked functions
+ if the :py:class:`.PluginManager` uses the same project_name.
"""
def __init__(self, project_name):
@@ -76,25 +76,25 @@ class HookimplMarker(object):
):
""" if passed a function, directly sets attributes on the function
- which will make it discoverable to :py:meth:`.PluginManager.register`.
- If passed no function, returns a decorator which can be applied to a
- function later using the attributes supplied.
+ which will make it discoverable to :py:meth:`.PluginManager.register`.
+ If passed no function, returns a decorator which can be applied to a
+ function later using the attributes supplied.
- If ``optionalhook`` is ``True`` a missing matching hook specification will not result
+ If ``optionalhook`` is ``True`` a missing matching hook specification will not result
in an error (by default it is an error if no matching spec is found).
- If ``tryfirst`` is ``True`` this hook implementation will run as early as possible
- in the chain of N hook implementations for a specification.
+ If ``tryfirst`` is ``True`` this hook implementation will run as early as possible
+ in the chain of N hook implementations for a specification.
- If ``trylast`` is ``True`` this hook implementation will run as late as possible
+ If ``trylast`` is ``True`` this hook implementation will run as late as possible
in the chain of N hook implementations.
- If ``hookwrapper`` is ``True`` the hook implementations needs to execute exactly
- one ``yield``. The code before the ``yield`` is run early before any non-hookwrapper
- function is run. The code after the ``yield`` is run after all non-hookwrapper
- function have run. The ``yield`` receives a :py:class:`.callers._Result` object
- representing the exception or result outcome of the inner calls (including other
- hookwrapper calls).
+ If ``hookwrapper`` is ``True`` the hook implementations needs to execute exactly
+ one ``yield``. The code before the ``yield`` is run early before any non-hookwrapper
+ function is run. The code after the ``yield`` is run after all non-hookwrapper
+ function have run. The ``yield`` receives a :py:class:`.callers._Result` object
+ representing the exception or result outcome of the inner calls (including other
+ hookwrapper calls).
"""
@@ -136,9 +136,9 @@ else:
return inspect.getargspec(func)
-_PYPY3 = hasattr(sys, "pypy_version_info") and sys.version_info.major == 3
-
-
+_PYPY3 = hasattr(sys, "pypy_version_info") and sys.version_info.major == 3
+
+
def varnames(func):
"""Return tuple of positional and keywrord argument names for a function,
method, class or callable.
@@ -161,7 +161,7 @@ def varnames(func):
try:
func = getattr(func, "__call__", func)
except Exception:
- return (), ()
+ return (), ()
try: # func MUST be a function or method here or we won't parse any args
spec = _getargspec(func)
@@ -171,24 +171,24 @@ def varnames(func):
args, defaults = tuple(spec.args), spec.defaults
if defaults:
index = -len(defaults)
- args, kwargs = args[:index], tuple(args[index:])
+ args, kwargs = args[:index], tuple(args[index:])
else:
- kwargs = ()
+ kwargs = ()
# strip any implicit instance arg
- # pypy3 uses "obj" instead of "self" for default dunder methods
- implicit_names = ("self",) if not _PYPY3 else ("self", "obj")
+ # pypy3 uses "obj" instead of "self" for default dunder methods
+ implicit_names = ("self",) if not _PYPY3 else ("self", "obj")
if args:
if inspect.ismethod(func) or (
- "." in getattr(func, "__qualname__", ()) and args[0] in implicit_names
+ "." in getattr(func, "__qualname__", ()) and args[0] in implicit_names
):
args = args[1:]
try:
- cache["_varnames"] = args, kwargs
+ cache["_varnames"] = args, kwargs
except TypeError:
pass
- return args, kwargs
+ return args, kwargs
class _HookRelay(object):
@@ -290,7 +290,7 @@ class _HookCaller(object):
for all plugins which will be registered afterwards.
If ``result_callback`` is not ``None`` it will be called for for each
- non-``None`` result obtained from a hook implementation.
+ non-``None`` result obtained from a hook implementation.
.. note::
The ``proc`` argument is now deprecated.
@@ -314,7 +314,7 @@ class _HookCaller(object):
def call_extra(self, methods, kwargs):
""" Call the hook with some additional temporarily participating
- methods using the specified ``kwargs`` as call parameters. """
+ methods using the specified ``kwargs`` as call parameters. """
old = list(self._nonwrappers), list(self._wrappers)
for method in methods:
opts = dict(hookwrapper=False, trylast=False, tryfirst=False)
diff --git a/contrib/python/pluggy/py2/pluggy/manager.py b/contrib/python/pluggy/py2/pluggy/manager.py
index 07b42cba2d..28b2ed0df7 100644
--- a/contrib/python/pluggy/py2/pluggy/manager.py
+++ b/contrib/python/pluggy/py2/pluggy/manager.py
@@ -1,16 +1,16 @@
import inspect
-import sys
+import sys
from . import _tracing
-from .callers import _Result
+from .callers import _Result
from .hooks import HookImpl, _HookRelay, _HookCaller, normalize_hookimpl_opts
import warnings
-if sys.version_info >= (3, 8):
- from importlib import metadata as importlib_metadata
-else:
- import importlib_metadata
-
+if sys.version_info >= (3, 8):
+ from importlib import metadata as importlib_metadata
+else:
+ import importlib_metadata
+
def _warn_for_function(warning, function):
warnings.warn_explicit(
warning,
@@ -32,53 +32,53 @@ class PluginValidationError(Exception):
super(Exception, self).__init__(message)
-class DistFacade(object):
- """Emulate a pkg_resources Distribution"""
-
- def __init__(self, dist):
- self._dist = dist
-
- @property
- def project_name(self):
- return self.metadata["name"]
-
- def __getattr__(self, attr, default=None):
- return getattr(self._dist, attr, default)
-
- def __dir__(self):
- return sorted(dir(self._dist) + ["_dist", "project_name"])
-
-
+class DistFacade(object):
+ """Emulate a pkg_resources Distribution"""
+
+ def __init__(self, dist):
+ self._dist = dist
+
+ @property
+ def project_name(self):
+ return self.metadata["name"]
+
+ def __getattr__(self, attr, default=None):
+ return getattr(self._dist, attr, default)
+
+ def __dir__(self):
+ return sorted(dir(self._dist) + ["_dist", "project_name"])
+
+
class PluginManager(object):
- """ Core :py:class:`.PluginManager` class which manages registration
+ """ Core :py:class:`.PluginManager` class which manages registration
of plugin objects and 1:N hook calling.
- You can register new hooks by calling :py:meth:`add_hookspecs(module_or_class)
- <.PluginManager.add_hookspecs>`.
+ You can register new hooks by calling :py:meth:`add_hookspecs(module_or_class)
+ <.PluginManager.add_hookspecs>`.
You can register plugin objects (which contain hooks) by calling
- :py:meth:`register(plugin) <.PluginManager.register>`. The :py:class:`.PluginManager`
- is initialized with a prefix that is searched for in the names of the dict
- of registered plugin objects.
+ :py:meth:`register(plugin) <.PluginManager.register>`. The :py:class:`.PluginManager`
+ is initialized with a prefix that is searched for in the names of the dict
+ of registered plugin objects.
- For debugging purposes you can call :py:meth:`.PluginManager.enable_tracing`
+ For debugging purposes you can call :py:meth:`.PluginManager.enable_tracing`
which will subsequently send debug information to the trace helper.
"""
def __init__(self, project_name, implprefix=None):
"""If ``implprefix`` is given implementation functions
- will be recognized if their name matches the ``implprefix``. """
+ will be recognized if their name matches the ``implprefix``. """
self.project_name = project_name
self._name2plugin = {}
self._plugin2hookcallers = {}
self._plugin_distinfo = []
self.trace = _tracing.TagTracer().get("pluginmanage")
- self.hook = _HookRelay()
+ self.hook = _HookRelay()
if implprefix is not None:
warnings.warn(
"Support for the `implprefix` arg is now deprecated and will "
"be removed in an upcoming release. Please use HookimplMarker.",
DeprecationWarning,
- stacklevel=2,
+ stacklevel=2,
)
self._implprefix = implprefix
self._inner_hookexec = lambda hook, methods, kwargs: hook.multicall(
@@ -93,9 +93,9 @@ class PluginManager(object):
return self._inner_hookexec(hook, methods, kwargs)
def register(self, plugin, name=None):
- """ Register a plugin and return its canonical name or ``None`` if the name
- is blocked from registering. Raise a :py:class:`ValueError` if the plugin
- is already registered. """
+ """ Register a plugin and return its canonical name or ``None`` if the name
+ is blocked from registering. Raise a :py:class:`ValueError` if the plugin
+ is already registered. """
plugin_name = name or self.get_canonical_name(plugin)
if plugin_name in self._name2plugin or plugin in self._plugin2hookcallers:
@@ -177,11 +177,11 @@ class PluginManager(object):
self._name2plugin[name] = None
def is_blocked(self, name):
- """ return ``True`` if the given plugin name is blocked. """
+ """ return ``True`` if the given plugin name is blocked. """
return name in self._name2plugin and self._name2plugin[name] is None
def add_hookspecs(self, module_or_class):
- """ add new hook specifications defined in the given ``module_or_class``.
+ """ add new hook specifications defined in the given ``module_or_class``.
Functions are recognized if they have been decorated accordingly. """
names = []
for name in dir(module_or_class):
@@ -212,27 +212,27 @@ class PluginManager(object):
return set(self._plugin2hookcallers)
def is_registered(self, plugin):
- """ Return ``True`` if the plugin is already registered. """
+ """ Return ``True`` if the plugin is already registered. """
return plugin in self._plugin2hookcallers
def get_canonical_name(self, plugin):
""" Return canonical name for a plugin object. Note that a plugin
may be registered under a different name which was specified
- by the caller of :py:meth:`register(plugin, name) <.PluginManager.register>`.
- To obtain the name of an registered plugin use :py:meth:`get_name(plugin)
- <.PluginManager.get_name>` instead."""
+ by the caller of :py:meth:`register(plugin, name) <.PluginManager.register>`.
+ To obtain the name of an registered plugin use :py:meth:`get_name(plugin)
+ <.PluginManager.get_name>` instead."""
return getattr(plugin, "__name__", None) or str(id(plugin))
def get_plugin(self, name):
- """ Return a plugin or ``None`` for the given name. """
+ """ Return a plugin or ``None`` for the given name. """
return self._name2plugin.get(name)
def has_plugin(self, name):
- """ Return ``True`` if a plugin with the given name is registered. """
+ """ Return ``True`` if a plugin with the given name is registered. """
return self.get_plugin(name) is not None
def get_name(self, plugin):
- """ Return name for registered plugin or ``None`` if not registered. """
+ """ Return name for registered plugin or ``None`` if not registered. """
for name, val in self._name2plugin.items():
if plugin == val:
return name
@@ -264,7 +264,7 @@ class PluginManager(object):
def check_pending(self):
""" Verify that all hooks which have not been verified against
- a hook specification are optional, otherwise raise :py:class:`.PluginValidationError`."""
+ a hook specification are optional, otherwise raise :py:class:`.PluginValidationError`."""
for name in self.hook.__dict__:
if name[0] != "_":
hook = getattr(self.hook, name)
@@ -277,30 +277,30 @@ class PluginManager(object):
% (name, hookimpl.plugin),
)
- def load_setuptools_entrypoints(self, group, name=None):
- """ Load modules from querying the specified setuptools ``group``.
-
- :param str group: entry point group to load plugins
- :param str name: if given, loads only plugins with the given ``name``.
- :rtype: int
- :return: return the number of loaded plugins by this call.
- """
- count = 0
- for dist in importlib_metadata.distributions():
- for ep in dist.entry_points:
- if (
- ep.group != group
- or (name is not None and ep.name != name)
- # already registered
- or self.get_plugin(ep.name)
- or self.is_blocked(ep.name)
- ):
- continue
+ def load_setuptools_entrypoints(self, group, name=None):
+ """ Load modules from querying the specified setuptools ``group``.
+
+ :param str group: entry point group to load plugins
+ :param str name: if given, loads only plugins with the given ``name``.
+ :rtype: int
+ :return: return the number of loaded plugins by this call.
+ """
+ count = 0
+ for dist in importlib_metadata.distributions():
+ for ep in dist.entry_points:
+ if (
+ ep.group != group
+ or (name is not None and ep.name != name)
+ # already registered
+ or self.get_plugin(ep.name)
+ or self.is_blocked(ep.name)
+ ):
+ continue
plugin = ep.load()
- self.register(plugin, name=ep.name)
- self._plugin_distinfo.append((plugin, DistFacade(dist)))
- count += 1
- return count
+ self.register(plugin, name=ep.name)
+ self._plugin_distinfo.append((plugin, DistFacade(dist)))
+ count += 1
+ return count
def list_plugin_distinfo(self):
""" return list of distinfo/plugin tuples for all setuptools registered
@@ -325,27 +325,27 @@ class PluginManager(object):
of HookImpl instances and the keyword arguments for the hook call.
``after(outcome, hook_name, hook_impls, kwargs)`` receives the
- same arguments as ``before`` but also a :py:class:`pluggy.callers._Result` object
+ same arguments as ``before`` but also a :py:class:`pluggy.callers._Result` object
which represents the result of the overall hook call.
"""
- oldcall = self._inner_hookexec
-
- def traced_hookexec(hook, hook_impls, kwargs):
- before(hook.name, hook_impls, kwargs)
- outcome = _Result.from_call(lambda: oldcall(hook, hook_impls, kwargs))
- after(outcome, hook.name, hook_impls, kwargs)
- return outcome.get_result()
-
- self._inner_hookexec = traced_hookexec
-
- def undo():
- self._inner_hookexec = oldcall
-
- return undo
-
+ oldcall = self._inner_hookexec
+
+ def traced_hookexec(hook, hook_impls, kwargs):
+ before(hook.name, hook_impls, kwargs)
+ outcome = _Result.from_call(lambda: oldcall(hook, hook_impls, kwargs))
+ after(outcome, hook.name, hook_impls, kwargs)
+ return outcome.get_result()
+
+ self._inner_hookexec = traced_hookexec
+
+ def undo():
+ self._inner_hookexec = oldcall
+
+ return undo
+
def enable_tracing(self):
""" enable tracing of hook calls and return an undo function. """
- hooktrace = self.trace.root.get("hook")
+ hooktrace = self.trace.root.get("hook")
def before(hook_name, methods, kwargs):
hooktrace.root.indent += 1
@@ -359,7 +359,7 @@ class PluginManager(object):
return self.add_hookcall_monitoring(before, after)
def subset_hook_caller(self, name, remove_plugins):
- """ Return a new :py:class:`.hooks._HookCaller` instance for the named method
+ """ Return a new :py:class:`.hooks._HookCaller` instance for the named method
which manages calls to all registered plugins except the
ones from remove_plugins. """
orig = getattr(self.hook, name)
diff --git a/contrib/python/pluggy/py2/tests/benchmark.py b/contrib/python/pluggy/py2/tests/benchmark.py
index aa8de92911..cb99660adb 100644
--- a/contrib/python/pluggy/py2/tests/benchmark.py
+++ b/contrib/python/pluggy/py2/tests/benchmark.py
@@ -1,51 +1,51 @@
-"""
-Benchmarking and performance tests.
-"""
-import pytest
-from pluggy import HookspecMarker, HookimplMarker
-from pluggy.hooks import HookImpl
-from pluggy.callers import _multicall, _legacymulticall
-
-hookspec = HookspecMarker("example")
-hookimpl = HookimplMarker("example")
-
-
-def MC(methods, kwargs, callertype, firstresult=False):
- hookfuncs = []
- for method in methods:
- f = HookImpl(None, "<temp>", method, method.example_impl)
- hookfuncs.append(f)
- return callertype(hookfuncs, kwargs, firstresult=firstresult)
-
-
-@hookimpl
-def hook(arg1, arg2, arg3):
- return arg1, arg2, arg3
-
-
-@hookimpl(hookwrapper=True)
-def wrapper(arg1, arg2, arg3):
- yield
-
-
-@pytest.fixture(params=[10, 100], ids="hooks={}".format)
-def hooks(request):
- return [hook for i in range(request.param)]
-
-
-@pytest.fixture(params=[10, 100], ids="wrappers={}".format)
-def wrappers(request):
- return [wrapper for i in range(request.param)]
-
-
-@pytest.fixture(params=[_multicall, _legacymulticall], ids=lambda item: item.__name__)
-def callertype(request):
- return request.param
-
-
-def inner_exec(methods, callertype):
- return MC(methods, {"arg1": 1, "arg2": 2, "arg3": 3}, callertype)
-
-
-def test_hook_and_wrappers_speed(benchmark, hooks, wrappers, callertype):
- benchmark(inner_exec, hooks + wrappers, callertype)
+"""
+Benchmarking and performance tests.
+"""
+import pytest
+from pluggy import HookspecMarker, HookimplMarker
+from pluggy.hooks import HookImpl
+from pluggy.callers import _multicall, _legacymulticall
+
+hookspec = HookspecMarker("example")
+hookimpl = HookimplMarker("example")
+
+
+def MC(methods, kwargs, callertype, firstresult=False):
+ hookfuncs = []
+ for method in methods:
+ f = HookImpl(None, "<temp>", method, method.example_impl)
+ hookfuncs.append(f)
+ return callertype(hookfuncs, kwargs, firstresult=firstresult)
+
+
+@hookimpl
+def hook(arg1, arg2, arg3):
+ return arg1, arg2, arg3
+
+
+@hookimpl(hookwrapper=True)
+def wrapper(arg1, arg2, arg3):
+ yield
+
+
+@pytest.fixture(params=[10, 100], ids="hooks={}".format)
+def hooks(request):
+ return [hook for i in range(request.param)]
+
+
+@pytest.fixture(params=[10, 100], ids="wrappers={}".format)
+def wrappers(request):
+ return [wrapper for i in range(request.param)]
+
+
+@pytest.fixture(params=[_multicall, _legacymulticall], ids=lambda item: item.__name__)
+def callertype(request):
+ return request.param
+
+
+def inner_exec(methods, callertype):
+ return MC(methods, {"arg1": 1, "arg2": 2, "arg3": 3}, callertype)
+
+
+def test_hook_and_wrappers_speed(benchmark, hooks, wrappers, callertype):
+ benchmark(inner_exec, hooks + wrappers, callertype)
diff --git a/contrib/python/pluggy/py2/tests/conftest.py b/contrib/python/pluggy/py2/tests/conftest.py
index e44667ef5f..3e40b0f9ca 100644
--- a/contrib/python/pluggy/py2/tests/conftest.py
+++ b/contrib/python/pluggy/py2/tests/conftest.py
@@ -1,26 +1,26 @@
-import pytest
-
-
-@pytest.fixture(
- params=[lambda spec: spec, lambda spec: spec()],
- ids=["spec-is-class", "spec-is-instance"],
-)
-def he_pm(request, pm):
- from pluggy import HookspecMarker
-
- hookspec = HookspecMarker("example")
-
- class Hooks(object):
- @hookspec
- def he_method1(self, arg):
- return arg + 1
-
- pm.add_hookspecs(request.param(Hooks))
- return pm
-
-
-@pytest.fixture
-def pm():
- from pluggy import PluginManager
-
- return PluginManager("example")
+import pytest
+
+
+@pytest.fixture(
+ params=[lambda spec: spec, lambda spec: spec()],
+ ids=["spec-is-class", "spec-is-instance"],
+)
+def he_pm(request, pm):
+ from pluggy import HookspecMarker
+
+ hookspec = HookspecMarker("example")
+
+ class Hooks(object):
+ @hookspec
+ def he_method1(self, arg):
+ return arg + 1
+
+ pm.add_hookspecs(request.param(Hooks))
+ return pm
+
+
+@pytest.fixture
+def pm():
+ from pluggy import PluginManager
+
+ return PluginManager("example")
diff --git a/contrib/python/pluggy/py2/tests/test_deprecations.py b/contrib/python/pluggy/py2/tests/test_deprecations.py
index 7151921b66..72048397d7 100644
--- a/contrib/python/pluggy/py2/tests/test_deprecations.py
+++ b/contrib/python/pluggy/py2/tests/test_deprecations.py
@@ -1,54 +1,54 @@
-"""
-Deprecation warnings testing roundup.
-"""
-import pytest
-from pluggy.callers import _Result
-from pluggy import PluginManager, HookimplMarker, HookspecMarker
-
-hookspec = HookspecMarker("example")
-hookimpl = HookimplMarker("example")
-
-
-def test_result_deprecated():
- r = _Result(10, None)
- with pytest.deprecated_call():
- assert r.result == 10
-
-
-def test_implprefix_deprecated():
- with pytest.deprecated_call():
- pm = PluginManager("blah", implprefix="blah_")
-
- class Plugin:
- def blah_myhook(self, arg1):
- return arg1
-
- with pytest.deprecated_call():
- pm.register(Plugin())
-
-
-def test_callhistoric_proc_deprecated(pm):
- """``proc`` kwarg to `PluginMananger.call_historic()` is now officially
- deprecated.
- """
-
- class P1(object):
- @hookspec(historic=True)
- @hookimpl
- def m(self, x):
- pass
-
- p1 = P1()
- pm.add_hookspecs(p1)
- pm.register(p1)
- with pytest.deprecated_call():
- pm.hook.m.call_historic(kwargs=dict(x=10), proc=lambda res: res)
-
-
-def test_multicall_deprecated(pm):
- class P1(object):
- @hookimpl
- def m(self, __multicall__, x):
- pass
-
- pytest.deprecated_call(pm.register, P1())
+"""
+Deprecation warnings testing roundup.
+"""
+import pytest
+from pluggy.callers import _Result
+from pluggy import PluginManager, HookimplMarker, HookspecMarker
+
+hookspec = HookspecMarker("example")
+hookimpl = HookimplMarker("example")
+
+
+def test_result_deprecated():
+ r = _Result(10, None)
+ with pytest.deprecated_call():
+ assert r.result == 10
+
+
+def test_implprefix_deprecated():
+ with pytest.deprecated_call():
+ pm = PluginManager("blah", implprefix="blah_")
+
+ class Plugin:
+ def blah_myhook(self, arg1):
+ return arg1
+
+ with pytest.deprecated_call():
+ pm.register(Plugin())
+
+
+def test_callhistoric_proc_deprecated(pm):
+ """``proc`` kwarg to `PluginMananger.call_historic()` is now officially
+ deprecated.
+ """
+
+ class P1(object):
+ @hookspec(historic=True)
+ @hookimpl
+ def m(self, x):
+ pass
+
+ p1 = P1()
+ pm.add_hookspecs(p1)
+ pm.register(p1)
+ with pytest.deprecated_call():
+ pm.hook.m.call_historic(kwargs=dict(x=10), proc=lambda res: res)
+
+
+def test_multicall_deprecated(pm):
+ class P1(object):
+ @hookimpl
+ def m(self, __multicall__, x):
+ pass
+
+ pytest.deprecated_call(pm.register, P1())
diff --git a/contrib/python/pluggy/py2/tests/test_details.py b/contrib/python/pluggy/py2/tests/test_details.py
index fc49fc308d..4745a155df 100644
--- a/contrib/python/pluggy/py2/tests/test_details.py
+++ b/contrib/python/pluggy/py2/tests/test_details.py
@@ -1,135 +1,135 @@
-import warnings
-import pytest
-from pluggy import PluginManager, HookimplMarker, HookspecMarker
-
-hookspec = HookspecMarker("example")
-hookimpl = HookimplMarker("example")
-
-
-def test_parse_hookimpl_override():
- class MyPluginManager(PluginManager):
- def parse_hookimpl_opts(self, module_or_class, name):
- opts = PluginManager.parse_hookimpl_opts(self, module_or_class, name)
- if opts is None:
- if name.startswith("x1"):
- opts = {}
- return opts
-
- class Plugin(object):
- def x1meth(self):
- pass
-
- @hookimpl(hookwrapper=True, tryfirst=True)
- def x1meth2(self):
- pass
-
- class Spec(object):
- @hookspec
- def x1meth(self):
- pass
-
- @hookspec
- def x1meth2(self):
- pass
-
- pm = MyPluginManager(hookspec.project_name)
- pm.register(Plugin())
- pm.add_hookspecs(Spec)
- assert not pm.hook.x1meth._nonwrappers[0].hookwrapper
- assert not pm.hook.x1meth._nonwrappers[0].tryfirst
- assert not pm.hook.x1meth._nonwrappers[0].trylast
- assert not pm.hook.x1meth._nonwrappers[0].optionalhook
-
- assert pm.hook.x1meth2._wrappers[0].tryfirst
- assert pm.hook.x1meth2._wrappers[0].hookwrapper
-
-
-def test_warn_when_deprecated_specified(recwarn):
- warning = DeprecationWarning("foo is deprecated")
-
- class Spec(object):
- @hookspec(warn_on_impl=warning)
- def foo(self):
- pass
-
- class Plugin(object):
- @hookimpl
- def foo(self):
- pass
-
- pm = PluginManager(hookspec.project_name)
- pm.add_hookspecs(Spec)
-
- with pytest.warns(DeprecationWarning) as records:
- pm.register(Plugin())
- (record,) = records
- assert record.message is warning
- assert record.filename == Plugin.foo.__code__.co_filename
- assert record.lineno == Plugin.foo.__code__.co_firstlineno
-
-
-def test_plugin_getattr_raises_errors():
- """Pluggy must be able to handle plugins which raise weird exceptions
- when getattr() gets called (#11).
- """
-
- class DontTouchMe(object):
- def __getattr__(self, x):
- raise Exception("cant touch me")
-
- class Module(object):
- pass
-
- module = Module()
- module.x = DontTouchMe()
-
- pm = PluginManager(hookspec.project_name)
- # register() would raise an error
- pm.register(module, "donttouch")
- assert pm.get_plugin("donttouch") is module
-
-
-def test_warning_on_call_vs_hookspec_arg_mismatch():
- """Verify that is a hook is called with less arguments then defined in the
- spec that a warning is emitted.
- """
-
- class Spec:
- @hookspec
- def myhook(self, arg1, arg2):
- pass
-
- class Plugin:
- @hookimpl
- def myhook(self, arg1):
- pass
-
- pm = PluginManager(hookspec.project_name)
- pm.register(Plugin())
- pm.add_hookspecs(Spec())
-
- with warnings.catch_warnings(record=True) as warns:
- warnings.simplefilter("always")
-
- # calling should trigger a warning
- pm.hook.myhook(arg1=1)
-
- assert len(warns) == 1
- warning = warns[-1]
- assert issubclass(warning.category, Warning)
- assert "Argument(s) ('arg2',)" in str(warning.message)
-
-
-def test_repr():
- class Plugin:
- @hookimpl
- def myhook():
- raise NotImplementedError()
-
- pm = PluginManager(hookspec.project_name)
-
- plugin = Plugin()
- pname = pm.register(plugin)
- assert repr(pm.hook.myhook._nonwrappers[0]) == (
- "<HookImpl plugin_name=%r, plugin=%r>" % (pname, plugin)
- )
+import warnings
+import pytest
+from pluggy import PluginManager, HookimplMarker, HookspecMarker
+
+hookspec = HookspecMarker("example")
+hookimpl = HookimplMarker("example")
+
+
+def test_parse_hookimpl_override():
+ class MyPluginManager(PluginManager):
+ def parse_hookimpl_opts(self, module_or_class, name):
+ opts = PluginManager.parse_hookimpl_opts(self, module_or_class, name)
+ if opts is None:
+ if name.startswith("x1"):
+ opts = {}
+ return opts
+
+ class Plugin(object):
+ def x1meth(self):
+ pass
+
+ @hookimpl(hookwrapper=True, tryfirst=True)
+ def x1meth2(self):
+ pass
+
+ class Spec(object):
+ @hookspec
+ def x1meth(self):
+ pass
+
+ @hookspec
+ def x1meth2(self):
+ pass
+
+ pm = MyPluginManager(hookspec.project_name)
+ pm.register(Plugin())
+ pm.add_hookspecs(Spec)
+ assert not pm.hook.x1meth._nonwrappers[0].hookwrapper
+ assert not pm.hook.x1meth._nonwrappers[0].tryfirst
+ assert not pm.hook.x1meth._nonwrappers[0].trylast
+ assert not pm.hook.x1meth._nonwrappers[0].optionalhook
+
+ assert pm.hook.x1meth2._wrappers[0].tryfirst
+ assert pm.hook.x1meth2._wrappers[0].hookwrapper
+
+
+def test_warn_when_deprecated_specified(recwarn):
+ warning = DeprecationWarning("foo is deprecated")
+
+ class Spec(object):
+ @hookspec(warn_on_impl=warning)
+ def foo(self):
+ pass
+
+ class Plugin(object):
+ @hookimpl
+ def foo(self):
+ pass
+
+ pm = PluginManager(hookspec.project_name)
+ pm.add_hookspecs(Spec)
+
+ with pytest.warns(DeprecationWarning) as records:
+ pm.register(Plugin())
+ (record,) = records
+ assert record.message is warning
+ assert record.filename == Plugin.foo.__code__.co_filename
+ assert record.lineno == Plugin.foo.__code__.co_firstlineno
+
+
+def test_plugin_getattr_raises_errors():
+ """Pluggy must be able to handle plugins which raise weird exceptions
+ when getattr() gets called (#11).
+ """
+
+ class DontTouchMe(object):
+ def __getattr__(self, x):
+ raise Exception("cant touch me")
+
+ class Module(object):
+ pass
+
+ module = Module()
+ module.x = DontTouchMe()
+
+ pm = PluginManager(hookspec.project_name)
+ # register() would raise an error
+ pm.register(module, "donttouch")
+ assert pm.get_plugin("donttouch") is module
+
+
+def test_warning_on_call_vs_hookspec_arg_mismatch():
+ """Verify that is a hook is called with less arguments then defined in the
+ spec that a warning is emitted.
+ """
+
+ class Spec:
+ @hookspec
+ def myhook(self, arg1, arg2):
+ pass
+
+ class Plugin:
+ @hookimpl
+ def myhook(self, arg1):
+ pass
+
+ pm = PluginManager(hookspec.project_name)
+ pm.register(Plugin())
+ pm.add_hookspecs(Spec())
+
+ with warnings.catch_warnings(record=True) as warns:
+ warnings.simplefilter("always")
+
+ # calling should trigger a warning
+ pm.hook.myhook(arg1=1)
+
+ assert len(warns) == 1
+ warning = warns[-1]
+ assert issubclass(warning.category, Warning)
+ assert "Argument(s) ('arg2',)" in str(warning.message)
+
+
+def test_repr():
+ class Plugin:
+ @hookimpl
+ def myhook():
+ raise NotImplementedError()
+
+ pm = PluginManager(hookspec.project_name)
+
+ plugin = Plugin()
+ pname = pm.register(plugin)
+ assert repr(pm.hook.myhook._nonwrappers[0]) == (
+ "<HookImpl plugin_name=%r, plugin=%r>" % (pname, plugin)
+ )
diff --git a/contrib/python/pluggy/py2/tests/test_helpers.py b/contrib/python/pluggy/py2/tests/test_helpers.py
index 64c3edaa0a..63e56ebde8 100644
--- a/contrib/python/pluggy/py2/tests/test_helpers.py
+++ b/contrib/python/pluggy/py2/tests/test_helpers.py
@@ -1,90 +1,90 @@
-from pluggy.hooks import varnames
-from pluggy.manager import _formatdef
-
-import sys
-import pytest
-
-
-def test_varnames():
- def f(x):
- i = 3 # noqa
-
- class A(object):
- def f(self, y):
- pass
-
- class B(object):
- def __call__(self, z):
- pass
-
- assert varnames(f) == (("x",), ())
- assert varnames(A().f) == (("y",), ())
- assert varnames(B()) == (("z",), ())
-
-
-def test_varnames_default():
- def f(x, y=3):
- pass
-
- assert varnames(f) == (("x",), ("y",))
-
-
-def test_varnames_class():
- class C(object):
- def __init__(self, x):
- pass
-
- class D(object):
- pass
-
- class E(object):
- def __init__(self, x):
- pass
-
- class F(object):
- pass
-
- assert varnames(C) == (("x",), ())
- assert varnames(D) == ((), ())
- assert varnames(E) == (("x",), ())
- assert varnames(F) == ((), ())
-
-
-@pytest.mark.skipif(
- sys.version_info < (3,), reason="Keyword only arguments are Python 3 only"
-)
-def test_varnames_keyword_only():
- # SyntaxError on Python 2, so we exec
- ns = {}
- exec(
- "def f1(x, *, y): pass\n"
- "def f2(x, *, y=3): pass\n"
- "def f3(x=1, *, y=3): pass\n",
- ns,
- )
-
- assert varnames(ns["f1"]) == (("x",), ())
- assert varnames(ns["f2"]) == (("x",), ())
- assert varnames(ns["f3"]) == ((), ("x",))
-
-
-def test_formatdef():
- def function1():
- pass
-
- assert _formatdef(function1) == "function1()"
-
- def function2(arg1):
- pass
-
- assert _formatdef(function2) == "function2(arg1)"
-
- def function3(arg1, arg2="qwe"):
- pass
-
- assert _formatdef(function3) == "function3(arg1, arg2='qwe')"
-
- def function4(arg1, *args, **kwargs):
- pass
-
- assert _formatdef(function4) == "function4(arg1, *args, **kwargs)"
+from pluggy.hooks import varnames
+from pluggy.manager import _formatdef
+
+import sys
+import pytest
+
+
+def test_varnames():
+ def f(x):
+ i = 3 # noqa
+
+ class A(object):
+ def f(self, y):
+ pass
+
+ class B(object):
+ def __call__(self, z):
+ pass
+
+ assert varnames(f) == (("x",), ())
+ assert varnames(A().f) == (("y",), ())
+ assert varnames(B()) == (("z",), ())
+
+
+def test_varnames_default():
+ def f(x, y=3):
+ pass
+
+ assert varnames(f) == (("x",), ("y",))
+
+
+def test_varnames_class():
+ class C(object):
+ def __init__(self, x):
+ pass
+
+ class D(object):
+ pass
+
+ class E(object):
+ def __init__(self, x):
+ pass
+
+ class F(object):
+ pass
+
+ assert varnames(C) == (("x",), ())
+ assert varnames(D) == ((), ())
+ assert varnames(E) == (("x",), ())
+ assert varnames(F) == ((), ())
+
+
+@pytest.mark.skipif(
+ sys.version_info < (3,), reason="Keyword only arguments are Python 3 only"
+)
+def test_varnames_keyword_only():
+ # SyntaxError on Python 2, so we exec
+ ns = {}
+ exec(
+ "def f1(x, *, y): pass\n"
+ "def f2(x, *, y=3): pass\n"
+ "def f3(x=1, *, y=3): pass\n",
+ ns,
+ )
+
+ assert varnames(ns["f1"]) == (("x",), ())
+ assert varnames(ns["f2"]) == (("x",), ())
+ assert varnames(ns["f3"]) == ((), ("x",))
+
+
+def test_formatdef():
+ def function1():
+ pass
+
+ assert _formatdef(function1) == "function1()"
+
+ def function2(arg1):
+ pass
+
+ assert _formatdef(function2) == "function2(arg1)"
+
+ def function3(arg1, arg2="qwe"):
+ pass
+
+ assert _formatdef(function3) == "function3(arg1, arg2='qwe')"
+
+ def function4(arg1, *args, **kwargs):
+ pass
+
+ assert _formatdef(function4) == "function4(arg1, *args, **kwargs)"
diff --git a/contrib/python/pluggy/py2/tests/test_hookcaller.py b/contrib/python/pluggy/py2/tests/test_hookcaller.py
index 5664f2bbf6..87ab929f2f 100644
--- a/contrib/python/pluggy/py2/tests/test_hookcaller.py
+++ b/contrib/python/pluggy/py2/tests/test_hookcaller.py
@@ -1,215 +1,215 @@
-import pytest
-
-from pluggy import HookimplMarker, HookspecMarker
-from pluggy.hooks import HookImpl
-
-hookspec = HookspecMarker("example")
-hookimpl = HookimplMarker("example")
-
-
-@pytest.fixture
-def hc(pm):
- class Hooks(object):
- @hookspec
- def he_method1(self, arg):
- pass
-
- pm.add_hookspecs(Hooks)
- return pm.hook.he_method1
-
-
-@pytest.fixture
-def addmeth(hc):
- def addmeth(tryfirst=False, trylast=False, hookwrapper=False):
- def wrap(func):
- hookimpl(tryfirst=tryfirst, trylast=trylast, hookwrapper=hookwrapper)(func)
- hc._add_hookimpl(HookImpl(None, "<temp>", func, func.example_impl))
- return func
-
- return wrap
-
- return addmeth
-
-
-def funcs(hookmethods):
- return [hookmethod.function for hookmethod in hookmethods]
-
-
-def test_adding_nonwrappers(hc, addmeth):
- @addmeth()
- def he_method1():
- pass
-
- @addmeth()
- def he_method2():
- pass
-
- @addmeth()
- def he_method3():
- pass
-
- assert funcs(hc._nonwrappers) == [he_method1, he_method2, he_method3]
-
-
-def test_adding_nonwrappers_trylast(hc, addmeth):
- @addmeth()
- def he_method1_middle():
- pass
-
- @addmeth(trylast=True)
- def he_method1():
- pass
-
- @addmeth()
- def he_method1_b():
- pass
-
- assert funcs(hc._nonwrappers) == [he_method1, he_method1_middle, he_method1_b]
-
-
-def test_adding_nonwrappers_trylast3(hc, addmeth):
- @addmeth()
- def he_method1_a():
- pass
-
- @addmeth(trylast=True)
- def he_method1_b():
- pass
-
- @addmeth()
- def he_method1_c():
- pass
-
- @addmeth(trylast=True)
- def he_method1_d():
- pass
-
- assert funcs(hc._nonwrappers) == [
- he_method1_d,
- he_method1_b,
- he_method1_a,
- he_method1_c,
- ]
-
-
-def test_adding_nonwrappers_trylast2(hc, addmeth):
- @addmeth()
- def he_method1_middle():
- pass
-
- @addmeth()
- def he_method1_b():
- pass
-
- @addmeth(trylast=True)
- def he_method1():
- pass
-
- assert funcs(hc._nonwrappers) == [he_method1, he_method1_middle, he_method1_b]
-
-
-def test_adding_nonwrappers_tryfirst(hc, addmeth):
- @addmeth(tryfirst=True)
- def he_method1():
- pass
-
- @addmeth()
- def he_method1_middle():
- pass
-
- @addmeth()
- def he_method1_b():
- pass
-
- assert funcs(hc._nonwrappers) == [he_method1_middle, he_method1_b, he_method1]
-
-
-def test_adding_wrappers_ordering(hc, addmeth):
- @addmeth(hookwrapper=True)
- def he_method1():
- pass
-
- @addmeth()
- def he_method1_middle():
- pass
-
- @addmeth(hookwrapper=True)
- def he_method3():
- pass
-
- assert funcs(hc._nonwrappers) == [he_method1_middle]
- assert funcs(hc._wrappers) == [he_method1, he_method3]
-
-
-def test_adding_wrappers_ordering_tryfirst(hc, addmeth):
- @addmeth(hookwrapper=True, tryfirst=True)
- def he_method1():
- pass
-
- @addmeth(hookwrapper=True)
- def he_method2():
- pass
-
- assert hc._nonwrappers == []
- assert funcs(hc._wrappers) == [he_method2, he_method1]
-
-
-def test_hookspec(pm):
- class HookSpec(object):
- @hookspec()
- def he_myhook1(arg1):
- pass
-
- @hookspec(firstresult=True)
- def he_myhook2(arg1):
- pass
-
- @hookspec(firstresult=False)
- def he_myhook3(arg1):
- pass
-
- pm.add_hookspecs(HookSpec)
- assert not pm.hook.he_myhook1.spec.opts["firstresult"]
- assert pm.hook.he_myhook2.spec.opts["firstresult"]
- assert not pm.hook.he_myhook3.spec.opts["firstresult"]
-
-
-@pytest.mark.parametrize("name", ["hookwrapper", "optionalhook", "tryfirst", "trylast"])
-@pytest.mark.parametrize("val", [True, False])
-def test_hookimpl(name, val):
- @hookimpl(**{name: val})
- def he_myhook1(arg1):
- pass
-
- if val:
- assert he_myhook1.example_impl.get(name)
- else:
- assert not hasattr(he_myhook1, name)
-
-
-def test_hookrelay_registry(pm):
- """Verify hook caller instances are registered by name onto the relay
- and can be likewise unregistered."""
-
- class Api(object):
- @hookspec
- def hello(self, arg):
- "api hook 1"
-
- pm.add_hookspecs(Api)
- hook = pm.hook
- assert hasattr(hook, "hello")
- assert repr(hook.hello).find("hello") != -1
-
- class Plugin(object):
- @hookimpl
- def hello(self, arg):
- return arg + 1
-
- plugin = Plugin()
- pm.register(plugin)
- out = hook.hello(arg=3)
- assert out == [4]
- assert not hasattr(hook, "world")
- pm.unregister(plugin)
- assert hook.hello(arg=3) == []
+import pytest
+
+from pluggy import HookimplMarker, HookspecMarker
+from pluggy.hooks import HookImpl
+
+hookspec = HookspecMarker("example")
+hookimpl = HookimplMarker("example")
+
+
+@pytest.fixture
+def hc(pm):
+ class Hooks(object):
+ @hookspec
+ def he_method1(self, arg):
+ pass
+
+ pm.add_hookspecs(Hooks)
+ return pm.hook.he_method1
+
+
+@pytest.fixture
+def addmeth(hc):
+ def addmeth(tryfirst=False, trylast=False, hookwrapper=False):
+ def wrap(func):
+ hookimpl(tryfirst=tryfirst, trylast=trylast, hookwrapper=hookwrapper)(func)
+ hc._add_hookimpl(HookImpl(None, "<temp>", func, func.example_impl))
+ return func
+
+ return wrap
+
+ return addmeth
+
+
+def funcs(hookmethods):
+ return [hookmethod.function for hookmethod in hookmethods]
+
+
+def test_adding_nonwrappers(hc, addmeth):
+ @addmeth()
+ def he_method1():
+ pass
+
+ @addmeth()
+ def he_method2():
+ pass
+
+ @addmeth()
+ def he_method3():
+ pass
+
+ assert funcs(hc._nonwrappers) == [he_method1, he_method2, he_method3]
+
+
+def test_adding_nonwrappers_trylast(hc, addmeth):
+ @addmeth()
+ def he_method1_middle():
+ pass
+
+ @addmeth(trylast=True)
+ def he_method1():
+ pass
+
+ @addmeth()
+ def he_method1_b():
+ pass
+
+ assert funcs(hc._nonwrappers) == [he_method1, he_method1_middle, he_method1_b]
+
+
+def test_adding_nonwrappers_trylast3(hc, addmeth):
+ @addmeth()
+ def he_method1_a():
+ pass
+
+ @addmeth(trylast=True)
+ def he_method1_b():
+ pass
+
+ @addmeth()
+ def he_method1_c():
+ pass
+
+ @addmeth(trylast=True)
+ def he_method1_d():
+ pass
+
+ assert funcs(hc._nonwrappers) == [
+ he_method1_d,
+ he_method1_b,
+ he_method1_a,
+ he_method1_c,
+ ]
+
+
+def test_adding_nonwrappers_trylast2(hc, addmeth):
+ @addmeth()
+ def he_method1_middle():
+ pass
+
+ @addmeth()
+ def he_method1_b():
+ pass
+
+ @addmeth(trylast=True)
+ def he_method1():
+ pass
+
+ assert funcs(hc._nonwrappers) == [he_method1, he_method1_middle, he_method1_b]
+
+
+def test_adding_nonwrappers_tryfirst(hc, addmeth):
+ @addmeth(tryfirst=True)
+ def he_method1():
+ pass
+
+ @addmeth()
+ def he_method1_middle():
+ pass
+
+ @addmeth()
+ def he_method1_b():
+ pass
+
+ assert funcs(hc._nonwrappers) == [he_method1_middle, he_method1_b, he_method1]
+
+
+def test_adding_wrappers_ordering(hc, addmeth):
+ @addmeth(hookwrapper=True)
+ def he_method1():
+ pass
+
+ @addmeth()
+ def he_method1_middle():
+ pass
+
+ @addmeth(hookwrapper=True)
+ def he_method3():
+ pass
+
+ assert funcs(hc._nonwrappers) == [he_method1_middle]
+ assert funcs(hc._wrappers) == [he_method1, he_method3]
+
+
+def test_adding_wrappers_ordering_tryfirst(hc, addmeth):
+ @addmeth(hookwrapper=True, tryfirst=True)
+ def he_method1():
+ pass
+
+ @addmeth(hookwrapper=True)
+ def he_method2():
+ pass
+
+ assert hc._nonwrappers == []
+ assert funcs(hc._wrappers) == [he_method2, he_method1]
+
+
+def test_hookspec(pm):
+ class HookSpec(object):
+ @hookspec()
+ def he_myhook1(arg1):
+ pass
+
+ @hookspec(firstresult=True)
+ def he_myhook2(arg1):
+ pass
+
+ @hookspec(firstresult=False)
+ def he_myhook3(arg1):
+ pass
+
+ pm.add_hookspecs(HookSpec)
+ assert not pm.hook.he_myhook1.spec.opts["firstresult"]
+ assert pm.hook.he_myhook2.spec.opts["firstresult"]
+ assert not pm.hook.he_myhook3.spec.opts["firstresult"]
+
+
+@pytest.mark.parametrize("name", ["hookwrapper", "optionalhook", "tryfirst", "trylast"])
+@pytest.mark.parametrize("val", [True, False])
+def test_hookimpl(name, val):
+ @hookimpl(**{name: val})
+ def he_myhook1(arg1):
+ pass
+
+ if val:
+ assert he_myhook1.example_impl.get(name)
+ else:
+ assert not hasattr(he_myhook1, name)
+
+
+def test_hookrelay_registry(pm):
+ """Verify hook caller instances are registered by name onto the relay
+ and can be likewise unregistered."""
+
+ class Api(object):
+ @hookspec
+ def hello(self, arg):
+ "api hook 1"
+
+ pm.add_hookspecs(Api)
+ hook = pm.hook
+ assert hasattr(hook, "hello")
+ assert repr(hook.hello).find("hello") != -1
+
+ class Plugin(object):
+ @hookimpl
+ def hello(self, arg):
+ return arg + 1
+
+ plugin = Plugin()
+ pm.register(plugin)
+ out = hook.hello(arg=3)
+ assert out == [4]
+ assert not hasattr(hook, "world")
+ pm.unregister(plugin)
+ assert hook.hello(arg=3) == []
diff --git a/contrib/python/pluggy/py2/tests/test_invocations.py b/contrib/python/pluggy/py2/tests/test_invocations.py
index a6ec63cfc7..95d0be5bda 100644
--- a/contrib/python/pluggy/py2/tests/test_invocations.py
+++ b/contrib/python/pluggy/py2/tests/test_invocations.py
@@ -1,216 +1,216 @@
-import pytest
-from pluggy import PluginValidationError, HookimplMarker, HookspecMarker
-
-
-hookspec = HookspecMarker("example")
-hookimpl = HookimplMarker("example")
-
-
-def test_argmismatch(pm):
- class Api(object):
- @hookspec
- def hello(self, arg):
- "api hook 1"
-
- pm.add_hookspecs(Api)
-
- class Plugin(object):
- @hookimpl
- def hello(self, argwrong):
- pass
-
- with pytest.raises(PluginValidationError) as exc:
- pm.register(Plugin())
-
- assert "argwrong" in str(exc.value)
-
-
-def test_only_kwargs(pm):
- class Api(object):
- @hookspec
- def hello(self, arg):
- "api hook 1"
-
- pm.add_hookspecs(Api)
- with pytest.raises(TypeError) as exc:
- pm.hook.hello(3)
-
- comprehensible = "hook calling supports only keyword arguments"
- assert comprehensible in str(exc.value)
-
-
-def test_opt_in_args(pm):
- """Verfiy that two hookimpls with mutex args can serve
- under the same spec.
- """
-
- class Api(object):
- @hookspec
- def hello(self, arg1, arg2, common_arg):
- "api hook 1"
-
- class Plugin1(object):
- @hookimpl
- def hello(self, arg1, common_arg):
- return arg1 + common_arg
-
- class Plugin2(object):
- @hookimpl
- def hello(self, arg2, common_arg):
- return arg2 + common_arg
-
- pm.add_hookspecs(Api)
- pm.register(Plugin1())
- pm.register(Plugin2())
-
- results = pm.hook.hello(arg1=1, arg2=2, common_arg=0)
- assert results == [2, 1]
-
-
-def test_call_order(pm):
- class Api(object):
- @hookspec
- def hello(self, arg):
- "api hook 1"
-
- pm.add_hookspecs(Api)
-
- class Plugin1(object):
- @hookimpl
- def hello(self, arg):
- return 1
-
- class Plugin2(object):
- @hookimpl
- def hello(self, arg):
- return 2
-
- class Plugin3(object):
- @hookimpl
- def hello(self, arg):
- return 3
-
- class Plugin4(object):
- @hookimpl(hookwrapper=True)
- def hello(self, arg):
- assert arg == 0
- outcome = yield
- assert outcome.get_result() == [3, 2, 1]
-
- pm.register(Plugin1())
- pm.register(Plugin2())
- pm.register(Plugin3())
- pm.register(Plugin4()) # hookwrapper should get same list result
- res = pm.hook.hello(arg=0)
- assert res == [3, 2, 1]
-
-
-def test_firstresult_definition(pm):
- class Api(object):
- @hookspec(firstresult=True)
- def hello(self, arg):
- "api hook 1"
-
- pm.add_hookspecs(Api)
-
- class Plugin1(object):
- @hookimpl
- def hello(self, arg):
- return arg + 1
-
- class Plugin2(object):
- @hookimpl
- def hello(self, arg):
- return arg - 1
-
- class Plugin3(object):
- @hookimpl
- def hello(self, arg):
- return None
-
- class Plugin4(object):
- @hookimpl(hookwrapper=True)
- def hello(self, arg):
- assert arg == 3
- outcome = yield
- assert outcome.get_result() == 2
-
- pm.register(Plugin1()) # discarded - not the last registered plugin
- pm.register(Plugin2()) # used as result
- pm.register(Plugin3()) # None result is ignored
- pm.register(Plugin4()) # hookwrapper should get same non-list result
- res = pm.hook.hello(arg=3)
- assert res == 2
-
-
-def test_firstresult_force_result(pm):
- """Verify forcing a result in a wrapper.
- """
-
- class Api(object):
- @hookspec(firstresult=True)
- def hello(self, arg):
- "api hook 1"
-
- pm.add_hookspecs(Api)
-
- class Plugin1(object):
- @hookimpl
- def hello(self, arg):
- return arg + 1
-
- class Plugin2(object):
- @hookimpl(hookwrapper=True)
- def hello(self, arg):
- assert arg == 3
- outcome = yield
- assert outcome.get_result() == 4
- outcome.force_result(0)
-
- class Plugin3(object):
- @hookimpl
- def hello(self, arg):
- return None
-
- pm.register(Plugin1())
- pm.register(Plugin2()) # wrapper
- pm.register(Plugin3()) # ignored since returns None
- res = pm.hook.hello(arg=3)
- assert res == 0 # this result is forced and not a list
-
-
-def test_firstresult_returns_none(pm):
- """If None results are returned by underlying implementations ensure
- the multi-call loop returns a None value.
- """
-
- class Api(object):
- @hookspec(firstresult=True)
- def hello(self, arg):
- "api hook 1"
-
- pm.add_hookspecs(Api)
-
- class Plugin1(object):
- @hookimpl
- def hello(self, arg):
- return None
-
- pm.register(Plugin1())
- res = pm.hook.hello(arg=3)
- assert res is None
-
-
-def test_firstresult_no_plugin(pm):
- """If no implementations/plugins have been registered for a firstresult
- hook the multi-call loop should return a None value.
- """
-
- class Api(object):
- @hookspec(firstresult=True)
- def hello(self, arg):
- "api hook 1"
-
- pm.add_hookspecs(Api)
- res = pm.hook.hello(arg=3)
- assert res is None
+import pytest
+from pluggy import PluginValidationError, HookimplMarker, HookspecMarker
+
+
+hookspec = HookspecMarker("example")
+hookimpl = HookimplMarker("example")
+
+
+def test_argmismatch(pm):
+ class Api(object):
+ @hookspec
+ def hello(self, arg):
+ "api hook 1"
+
+ pm.add_hookspecs(Api)
+
+ class Plugin(object):
+ @hookimpl
+ def hello(self, argwrong):
+ pass
+
+ with pytest.raises(PluginValidationError) as exc:
+ pm.register(Plugin())
+
+ assert "argwrong" in str(exc.value)
+
+
+def test_only_kwargs(pm):
+ class Api(object):
+ @hookspec
+ def hello(self, arg):
+ "api hook 1"
+
+ pm.add_hookspecs(Api)
+ with pytest.raises(TypeError) as exc:
+ pm.hook.hello(3)
+
+ comprehensible = "hook calling supports only keyword arguments"
+ assert comprehensible in str(exc.value)
+
+
+def test_opt_in_args(pm):
+ """Verfiy that two hookimpls with mutex args can serve
+ under the same spec.
+ """
+
+ class Api(object):
+ @hookspec
+ def hello(self, arg1, arg2, common_arg):
+ "api hook 1"
+
+ class Plugin1(object):
+ @hookimpl
+ def hello(self, arg1, common_arg):
+ return arg1 + common_arg
+
+ class Plugin2(object):
+ @hookimpl
+ def hello(self, arg2, common_arg):
+ return arg2 + common_arg
+
+ pm.add_hookspecs(Api)
+ pm.register(Plugin1())
+ pm.register(Plugin2())
+
+ results = pm.hook.hello(arg1=1, arg2=2, common_arg=0)
+ assert results == [2, 1]
+
+
+def test_call_order(pm):
+ class Api(object):
+ @hookspec
+ def hello(self, arg):
+ "api hook 1"
+
+ pm.add_hookspecs(Api)
+
+ class Plugin1(object):
+ @hookimpl
+ def hello(self, arg):
+ return 1
+
+ class Plugin2(object):
+ @hookimpl
+ def hello(self, arg):
+ return 2
+
+ class Plugin3(object):
+ @hookimpl
+ def hello(self, arg):
+ return 3
+
+ class Plugin4(object):
+ @hookimpl(hookwrapper=True)
+ def hello(self, arg):
+ assert arg == 0
+ outcome = yield
+ assert outcome.get_result() == [3, 2, 1]
+
+ pm.register(Plugin1())
+ pm.register(Plugin2())
+ pm.register(Plugin3())
+ pm.register(Plugin4()) # hookwrapper should get same list result
+ res = pm.hook.hello(arg=0)
+ assert res == [3, 2, 1]
+
+
+def test_firstresult_definition(pm):
+ class Api(object):
+ @hookspec(firstresult=True)
+ def hello(self, arg):
+ "api hook 1"
+
+ pm.add_hookspecs(Api)
+
+ class Plugin1(object):
+ @hookimpl
+ def hello(self, arg):
+ return arg + 1
+
+ class Plugin2(object):
+ @hookimpl
+ def hello(self, arg):
+ return arg - 1
+
+ class Plugin3(object):
+ @hookimpl
+ def hello(self, arg):
+ return None
+
+ class Plugin4(object):
+ @hookimpl(hookwrapper=True)
+ def hello(self, arg):
+ assert arg == 3
+ outcome = yield
+ assert outcome.get_result() == 2
+
+ pm.register(Plugin1()) # discarded - not the last registered plugin
+ pm.register(Plugin2()) # used as result
+ pm.register(Plugin3()) # None result is ignored
+ pm.register(Plugin4()) # hookwrapper should get same non-list result
+ res = pm.hook.hello(arg=3)
+ assert res == 2
+
+
+def test_firstresult_force_result(pm):
+ """Verify forcing a result in a wrapper.
+ """
+
+ class Api(object):
+ @hookspec(firstresult=True)
+ def hello(self, arg):
+ "api hook 1"
+
+ pm.add_hookspecs(Api)
+
+ class Plugin1(object):
+ @hookimpl
+ def hello(self, arg):
+ return arg + 1
+
+ class Plugin2(object):
+ @hookimpl(hookwrapper=True)
+ def hello(self, arg):
+ assert arg == 3
+ outcome = yield
+ assert outcome.get_result() == 4
+ outcome.force_result(0)
+
+ class Plugin3(object):
+ @hookimpl
+ def hello(self, arg):
+ return None
+
+ pm.register(Plugin1())
+ pm.register(Plugin2()) # wrapper
+ pm.register(Plugin3()) # ignored since returns None
+ res = pm.hook.hello(arg=3)
+ assert res == 0 # this result is forced and not a list
+
+
+def test_firstresult_returns_none(pm):
+ """If None results are returned by underlying implementations ensure
+ the multi-call loop returns a None value.
+ """
+
+ class Api(object):
+ @hookspec(firstresult=True)
+ def hello(self, arg):
+ "api hook 1"
+
+ pm.add_hookspecs(Api)
+
+ class Plugin1(object):
+ @hookimpl
+ def hello(self, arg):
+ return None
+
+ pm.register(Plugin1())
+ res = pm.hook.hello(arg=3)
+ assert res is None
+
+
+def test_firstresult_no_plugin(pm):
+ """If no implementations/plugins have been registered for a firstresult
+ hook the multi-call loop should return a None value.
+ """
+
+ class Api(object):
+ @hookspec(firstresult=True)
+ def hello(self, arg):
+ "api hook 1"
+
+ pm.add_hookspecs(Api)
+ res = pm.hook.hello(arg=3)
+ assert res is None
diff --git a/contrib/python/pluggy/py2/tests/test_multicall.py b/contrib/python/pluggy/py2/tests/test_multicall.py
index eaf534a016..7a6f0a87ec 100644
--- a/contrib/python/pluggy/py2/tests/test_multicall.py
+++ b/contrib/python/pluggy/py2/tests/test_multicall.py
@@ -1,186 +1,186 @@
-import pytest
-from pluggy import HookCallError, HookspecMarker, HookimplMarker
-from pluggy.hooks import HookImpl
-from pluggy.callers import _multicall, _legacymulticall
-
-
-hookspec = HookspecMarker("example")
-hookimpl = HookimplMarker("example")
-
-
-def MC(methods, kwargs, firstresult=False):
- caller = _multicall
- hookfuncs = []
- for method in methods:
- f = HookImpl(None, "<temp>", method, method.example_impl)
- hookfuncs.append(f)
- if "__multicall__" in f.argnames:
- caller = _legacymulticall
- return caller(hookfuncs, kwargs, firstresult=firstresult)
-
-
-def test_call_passing():
- class P1(object):
- @hookimpl
- def m(self, __multicall__, x):
- assert len(__multicall__.results) == 1
- assert not __multicall__.hook_impls
- return 17
-
- class P2(object):
- @hookimpl
- def m(self, __multicall__, x):
- assert __multicall__.results == []
- assert __multicall__.hook_impls
- return 23
-
- p1 = P1()
- p2 = P2()
- reslist = MC([p1.m, p2.m], {"x": 23})
- assert len(reslist) == 2
- # ensure reversed order
- assert reslist == [23, 17]
-
-
-def test_keyword_args():
- @hookimpl
- def f(x):
- return x + 1
-
- class A(object):
- @hookimpl
- def f(self, x, y):
- return x + y
-
- reslist = MC([f, A().f], dict(x=23, y=24))
- assert reslist == [24 + 23, 24]
-
-
-def test_keyword_args_with_defaultargs():
- @hookimpl
- def f(x, z=1):
- return x + z
-
- reslist = MC([f], dict(x=23, y=24))
- assert reslist == [24]
-
-
-def test_tags_call_error():
- @hookimpl
- def f(x):
- return x
-
- with pytest.raises(HookCallError):
- MC([f], {})
-
-
-def test_call_subexecute():
- @hookimpl
- def m(__multicall__):
- subresult = __multicall__.execute()
- return subresult + 1
-
- @hookimpl
- def n():
- return 1
-
- res = MC([n, m], {}, firstresult=True)
- assert res == 2
-
-
-def test_call_none_is_no_result():
- @hookimpl
- def m1():
- return 1
-
- @hookimpl
- def m2():
- return None
-
- res = MC([m1, m2], {}, firstresult=True)
- assert res == 1
- res = MC([m1, m2], {}, {})
- assert res == [1]
-
-
-def test_hookwrapper():
- out = []
-
- @hookimpl(hookwrapper=True)
- def m1():
- out.append("m1 init")
- yield None
- out.append("m1 finish")
-
- @hookimpl
- def m2():
- out.append("m2")
- return 2
-
- res = MC([m2, m1], {})
- assert res == [2]
- assert out == ["m1 init", "m2", "m1 finish"]
- out[:] = []
- res = MC([m2, m1], {}, firstresult=True)
- assert res == 2
- assert out == ["m1 init", "m2", "m1 finish"]
-
-
-def test_hookwrapper_order():
- out = []
-
- @hookimpl(hookwrapper=True)
- def m1():
- out.append("m1 init")
- yield 1
- out.append("m1 finish")
-
- @hookimpl(hookwrapper=True)
- def m2():
- out.append("m2 init")
- yield 2
- out.append("m2 finish")
-
- res = MC([m2, m1], {})
- assert res == []
- assert out == ["m1 init", "m2 init", "m2 finish", "m1 finish"]
-
-
-def test_hookwrapper_not_yield():
- @hookimpl(hookwrapper=True)
- def m1():
- pass
-
- with pytest.raises(TypeError):
- MC([m1], {})
-
-
-def test_hookwrapper_too_many_yield():
- @hookimpl(hookwrapper=True)
- def m1():
- yield 1
- yield 2
-
- with pytest.raises(RuntimeError) as ex:
- MC([m1], {})
- assert "m1" in str(ex.value)
- assert (__file__ + ":") in str(ex.value)
-
-
-@pytest.mark.parametrize("exc", [ValueError, SystemExit])
-def test_hookwrapper_exception(exc):
- out = []
-
- @hookimpl(hookwrapper=True)
- def m1():
- out.append("m1 init")
- yield None
- out.append("m1 finish")
-
- @hookimpl
- def m2():
- raise exc
-
- with pytest.raises(exc):
- MC([m2, m1], {})
- assert out == ["m1 init", "m1 finish"]
+import pytest
+from pluggy import HookCallError, HookspecMarker, HookimplMarker
+from pluggy.hooks import HookImpl
+from pluggy.callers import _multicall, _legacymulticall
+
+
+hookspec = HookspecMarker("example")
+hookimpl = HookimplMarker("example")
+
+
+def MC(methods, kwargs, firstresult=False):
+ caller = _multicall
+ hookfuncs = []
+ for method in methods:
+ f = HookImpl(None, "<temp>", method, method.example_impl)
+ hookfuncs.append(f)
+ if "__multicall__" in f.argnames:
+ caller = _legacymulticall
+ return caller(hookfuncs, kwargs, firstresult=firstresult)
+
+
+def test_call_passing():
+ class P1(object):
+ @hookimpl
+ def m(self, __multicall__, x):
+ assert len(__multicall__.results) == 1
+ assert not __multicall__.hook_impls
+ return 17
+
+ class P2(object):
+ @hookimpl
+ def m(self, __multicall__, x):
+ assert __multicall__.results == []
+ assert __multicall__.hook_impls
+ return 23
+
+ p1 = P1()
+ p2 = P2()
+ reslist = MC([p1.m, p2.m], {"x": 23})
+ assert len(reslist) == 2
+ # ensure reversed order
+ assert reslist == [23, 17]
+
+
+def test_keyword_args():
+ @hookimpl
+ def f(x):
+ return x + 1
+
+ class A(object):
+ @hookimpl
+ def f(self, x, y):
+ return x + y
+
+ reslist = MC([f, A().f], dict(x=23, y=24))
+ assert reslist == [24 + 23, 24]
+
+
+def test_keyword_args_with_defaultargs():
+ @hookimpl
+ def f(x, z=1):
+ return x + z
+
+ reslist = MC([f], dict(x=23, y=24))
+ assert reslist == [24]
+
+
+def test_tags_call_error():
+ @hookimpl
+ def f(x):
+ return x
+
+ with pytest.raises(HookCallError):
+ MC([f], {})
+
+
+def test_call_subexecute():
+ @hookimpl
+ def m(__multicall__):
+ subresult = __multicall__.execute()
+ return subresult + 1
+
+ @hookimpl
+ def n():
+ return 1
+
+ res = MC([n, m], {}, firstresult=True)
+ assert res == 2
+
+
+def test_call_none_is_no_result():
+ @hookimpl
+ def m1():
+ return 1
+
+ @hookimpl
+ def m2():
+ return None
+
+ res = MC([m1, m2], {}, firstresult=True)
+ assert res == 1
+ res = MC([m1, m2], {}, {})
+ assert res == [1]
+
+
+def test_hookwrapper():
+ out = []
+
+ @hookimpl(hookwrapper=True)
+ def m1():
+ out.append("m1 init")
+ yield None
+ out.append("m1 finish")
+
+ @hookimpl
+ def m2():
+ out.append("m2")
+ return 2
+
+ res = MC([m2, m1], {})
+ assert res == [2]
+ assert out == ["m1 init", "m2", "m1 finish"]
+ out[:] = []
+ res = MC([m2, m1], {}, firstresult=True)
+ assert res == 2
+ assert out == ["m1 init", "m2", "m1 finish"]
+
+
+def test_hookwrapper_order():
+ out = []
+
+ @hookimpl(hookwrapper=True)
+ def m1():
+ out.append("m1 init")
+ yield 1
+ out.append("m1 finish")
+
+ @hookimpl(hookwrapper=True)
+ def m2():
+ out.append("m2 init")
+ yield 2
+ out.append("m2 finish")
+
+ res = MC([m2, m1], {})
+ assert res == []
+ assert out == ["m1 init", "m2 init", "m2 finish", "m1 finish"]
+
+
+def test_hookwrapper_not_yield():
+ @hookimpl(hookwrapper=True)
+ def m1():
+ pass
+
+ with pytest.raises(TypeError):
+ MC([m1], {})
+
+
+def test_hookwrapper_too_many_yield():
+ @hookimpl(hookwrapper=True)
+ def m1():
+ yield 1
+ yield 2
+
+ with pytest.raises(RuntimeError) as ex:
+ MC([m1], {})
+ assert "m1" in str(ex.value)
+ assert (__file__ + ":") in str(ex.value)
+
+
+@pytest.mark.parametrize("exc", [ValueError, SystemExit])
+def test_hookwrapper_exception(exc):
+ out = []
+
+ @hookimpl(hookwrapper=True)
+ def m1():
+ out.append("m1 init")
+ yield None
+ out.append("m1 finish")
+
+ @hookimpl
+ def m2():
+ raise exc
+
+ with pytest.raises(exc):
+ MC([m2, m1], {})
+ assert out == ["m1 init", "m1 finish"]
diff --git a/contrib/python/pluggy/py2/tests/test_pluginmanager.py b/contrib/python/pluggy/py2/tests/test_pluginmanager.py
index 67261aaa7b..09d83ab9d4 100644
--- a/contrib/python/pluggy/py2/tests/test_pluginmanager.py
+++ b/contrib/python/pluggy/py2/tests/test_pluginmanager.py
@@ -1,600 +1,600 @@
-"""
-``PluginManager`` unit and public API testing.
-"""
-import pytest
-import types
-
-from pluggy import (
- PluginManager,
- PluginValidationError,
- HookCallError,
- HookimplMarker,
- HookspecMarker,
-)
-from pluggy.manager import importlib_metadata
-
-
-hookspec = HookspecMarker("example")
-hookimpl = HookimplMarker("example")
-
-
-def test_plugin_double_register(pm):
- """Registering the same plugin more then once isn't allowed"""
- pm.register(42, name="abc")
- with pytest.raises(ValueError):
- pm.register(42, name="abc")
- with pytest.raises(ValueError):
- pm.register(42, name="def")
-
-
-def test_pm(pm):
- """Basic registration with objects"""
-
- class A(object):
- pass
-
- a1, a2 = A(), A()
- pm.register(a1)
- assert pm.is_registered(a1)
- pm.register(a2, "hello")
- assert pm.is_registered(a2)
- out = pm.get_plugins()
- assert a1 in out
- assert a2 in out
- assert pm.get_plugin("hello") == a2
- assert pm.unregister(a1) == a1
- assert not pm.is_registered(a1)
-
- out = pm.list_name_plugin()
- assert len(out) == 1
- assert out == [("hello", a2)]
-
-
-def test_has_plugin(pm):
- class A(object):
- pass
-
- a1 = A()
- pm.register(a1, "hello")
- assert pm.is_registered(a1)
- assert pm.has_plugin("hello")
-
-
-def test_register_dynamic_attr(he_pm):
- class A(object):
- def __getattr__(self, name):
- if name[0] != "_":
- return 42
- raise AttributeError()
-
- a = A()
- he_pm.register(a)
- assert not he_pm.get_hookcallers(a)
-
-
-def test_pm_name(pm):
- class A(object):
- pass
-
- a1 = A()
- name = pm.register(a1, name="hello")
- assert name == "hello"
- pm.unregister(a1)
- assert pm.get_plugin(a1) is None
- assert not pm.is_registered(a1)
- assert not pm.get_plugins()
- name2 = pm.register(a1, name="hello")
- assert name2 == name
- pm.unregister(name="hello")
- assert pm.get_plugin(a1) is None
- assert not pm.is_registered(a1)
- assert not pm.get_plugins()
-
-
-def test_set_blocked(pm):
- class A(object):
- pass
-
- a1 = A()
- name = pm.register(a1)
- assert pm.is_registered(a1)
- assert not pm.is_blocked(name)
- pm.set_blocked(name)
- assert pm.is_blocked(name)
- assert not pm.is_registered(a1)
-
- pm.set_blocked("somename")
- assert pm.is_blocked("somename")
- assert not pm.register(A(), "somename")
- pm.unregister(name="somename")
- assert pm.is_blocked("somename")
-
-
-def test_register_mismatch_method(he_pm):
- class hello(object):
- @hookimpl
- def he_method_notexists(self):
- pass
-
- plugin = hello()
-
- he_pm.register(plugin)
- with pytest.raises(PluginValidationError) as excinfo:
- he_pm.check_pending()
- assert excinfo.value.plugin is plugin
-
-
-def test_register_mismatch_arg(he_pm):
- class hello(object):
- @hookimpl
- def he_method1(self, qlwkje):
- pass
-
- plugin = hello()
-
- with pytest.raises(PluginValidationError) as excinfo:
- he_pm.register(plugin)
- assert excinfo.value.plugin is plugin
-
-
-def test_register(pm):
- class MyPlugin(object):
- pass
-
- my = MyPlugin()
- pm.register(my)
- assert my in pm.get_plugins()
- my2 = MyPlugin()
- pm.register(my2)
- assert set([my, my2]).issubset(pm.get_plugins())
-
- assert pm.is_registered(my)
- assert pm.is_registered(my2)
- pm.unregister(my)
- assert not pm.is_registered(my)
- assert my not in pm.get_plugins()
-
-
-def test_register_unknown_hooks(pm):
- class Plugin1(object):
- @hookimpl
- def he_method1(self, arg):
- return arg + 1
-
- pname = pm.register(Plugin1())
-
- class Hooks(object):
- @hookspec
- def he_method1(self, arg):
- pass
-
- pm.add_hookspecs(Hooks)
- # assert not pm._unverified_hooks
- assert pm.hook.he_method1(arg=1) == [2]
- assert len(pm.get_hookcallers(pm.get_plugin(pname))) == 1
-
-
-def test_register_historic(pm):
- class Hooks(object):
- @hookspec(historic=True)
- def he_method1(self, arg):
- pass
-
- pm.add_hookspecs(Hooks)
-
- pm.hook.he_method1.call_historic(kwargs=dict(arg=1))
- out = []
-
- class Plugin(object):
- @hookimpl
- def he_method1(self, arg):
- out.append(arg)
-
- pm.register(Plugin())
- assert out == [1]
-
- class Plugin2(object):
- @hookimpl
- def he_method1(self, arg):
- out.append(arg * 10)
-
- pm.register(Plugin2())
- assert out == [1, 10]
- pm.hook.he_method1.call_historic(kwargs=dict(arg=12))
- assert out == [1, 10, 120, 12]
-
-
-@pytest.mark.parametrize("result_callback", [True, False])
-def test_with_result_memorized(pm, result_callback):
- """Verify that ``_HookCaller._maybe_apply_history()`
- correctly applies the ``result_callback`` function, when provided,
- to the result from calling each newly registered hook.
- """
- out = []
- if result_callback:
-
- def callback(res):
- out.append(res)
-
- else:
- callback = None
-
- class Hooks(object):
- @hookspec(historic=True)
- def he_method1(self, arg):
- pass
-
- pm.add_hookspecs(Hooks)
-
- class Plugin1(object):
- @hookimpl
- def he_method1(self, arg):
- return arg * 10
-
- pm.register(Plugin1())
-
- he_method1 = pm.hook.he_method1
- he_method1.call_historic(result_callback=callback, kwargs=dict(arg=1))
-
- class Plugin2(object):
- @hookimpl
- def he_method1(self, arg):
- return arg * 10
-
- pm.register(Plugin2())
- if result_callback:
- assert out == [10, 10]
- else:
- assert out == []
-
-
-def test_with_callbacks_immediately_executed(pm):
- class Hooks(object):
- @hookspec(historic=True)
- def he_method1(self, arg):
- pass
-
- pm.add_hookspecs(Hooks)
-
- class Plugin1(object):
- @hookimpl
- def he_method1(self, arg):
- return arg * 10
-
- class Plugin2(object):
- @hookimpl
- def he_method1(self, arg):
- return arg * 20
-
- class Plugin3(object):
- @hookimpl
- def he_method1(self, arg):
- return arg * 30
-
- out = []
- pm.register(Plugin1())
- pm.register(Plugin2())
-
- he_method1 = pm.hook.he_method1
- he_method1.call_historic(lambda res: out.append(res), dict(arg=1))
- assert out == [20, 10]
- pm.register(Plugin3())
- assert out == [20, 10, 30]
-
-
-def test_register_historic_incompat_hookwrapper(pm):
- class Hooks(object):
- @hookspec(historic=True)
- def he_method1(self, arg):
- pass
-
- pm.add_hookspecs(Hooks)
-
- out = []
-
- class Plugin(object):
- @hookimpl(hookwrapper=True)
- def he_method1(self, arg):
- out.append(arg)
-
- with pytest.raises(PluginValidationError):
- pm.register(Plugin())
-
-
-def test_call_extra(pm):
- class Hooks(object):
- @hookspec
- def he_method1(self, arg):
- pass
-
- pm.add_hookspecs(Hooks)
-
- def he_method1(arg):
- return arg * 10
-
- out = pm.hook.he_method1.call_extra([he_method1], dict(arg=1))
- assert out == [10]
-
-
-def test_call_with_too_few_args(pm):
- class Hooks(object):
- @hookspec
- def he_method1(self, arg):
- pass
-
- pm.add_hookspecs(Hooks)
-
- class Plugin1(object):
- @hookimpl
- def he_method1(self, arg):
- 0 / 0
-
- pm.register(Plugin1())
- with pytest.raises(HookCallError):
- with pytest.warns(UserWarning):
- pm.hook.he_method1()
-
-
-def test_subset_hook_caller(pm):
- class Hooks(object):
- @hookspec
- def he_method1(self, arg):
- pass
-
- pm.add_hookspecs(Hooks)
-
- out = []
-
- class Plugin1(object):
- @hookimpl
- def he_method1(self, arg):
- out.append(arg)
-
- class Plugin2(object):
- @hookimpl
- def he_method1(self, arg):
- out.append(arg * 10)
-
- class PluginNo(object):
- pass
-
- plugin1, plugin2, plugin3 = Plugin1(), Plugin2(), PluginNo()
- pm.register(plugin1)
- pm.register(plugin2)
- pm.register(plugin3)
- pm.hook.he_method1(arg=1)
- assert out == [10, 1]
- out[:] = []
-
- hc = pm.subset_hook_caller("he_method1", [plugin1])
- hc(arg=2)
- assert out == [20]
- out[:] = []
-
- hc = pm.subset_hook_caller("he_method1", [plugin2])
- hc(arg=2)
- assert out == [2]
- out[:] = []
-
- pm.unregister(plugin1)
- hc(arg=2)
- assert out == []
- out[:] = []
-
- pm.hook.he_method1(arg=1)
- assert out == [10]
-
-
-def test_get_hookimpls(pm):
- class Hooks(object):
- @hookspec
- def he_method1(self, arg):
- pass
-
- pm.add_hookspecs(Hooks)
- assert pm.hook.he_method1.get_hookimpls() == []
-
- class Plugin1(object):
- @hookimpl
- def he_method1(self, arg):
- pass
-
- class Plugin2(object):
- @hookimpl
- def he_method1(self, arg):
- pass
-
- class PluginNo(object):
- pass
-
- plugin1, plugin2, plugin3 = Plugin1(), Plugin2(), PluginNo()
- pm.register(plugin1)
- pm.register(plugin2)
- pm.register(plugin3)
-
- hookimpls = pm.hook.he_method1.get_hookimpls()
- hook_plugins = [item.plugin for item in hookimpls]
- assert hook_plugins == [plugin1, plugin2]
-
-
-def test_add_hookspecs_nohooks(pm):
- with pytest.raises(ValueError):
- pm.add_hookspecs(10)
-
-
-def test_reject_prefixed_module(pm):
- """Verify that a module type attribute that contains the project
- prefix in its name (in this case `'example_*'` isn't collected
- when registering a module which imports it.
- """
- pm._implprefix = "example"
- conftest = types.ModuleType("conftest")
- src = """
-def example_hook():
- pass
-"""
- exec(src, conftest.__dict__)
- conftest.example_blah = types.ModuleType("example_blah")
- with pytest.deprecated_call():
- name = pm.register(conftest)
- assert name == "conftest"
- assert getattr(pm.hook, "example_blah", None) is None
- assert getattr(
- pm.hook, "example_hook", None
- ) # conftest.example_hook should be collected
- with pytest.deprecated_call():
- assert pm.parse_hookimpl_opts(conftest, "example_blah") is None
- assert pm.parse_hookimpl_opts(conftest, "example_hook") == {}
-
-
-def test_load_setuptools_instantiation(monkeypatch, pm):
- class EntryPoint(object):
- name = "myname"
- group = "hello"
- value = "myname:foo"
-
- def load(self):
- class PseudoPlugin(object):
- x = 42
-
- return PseudoPlugin()
-
- class Distribution(object):
- entry_points = (EntryPoint(),)
-
- dist = Distribution()
-
- def my_distributions():
- return (dist,)
-
- monkeypatch.setattr(importlib_metadata, "distributions", my_distributions)
- num = pm.load_setuptools_entrypoints("hello")
- assert num == 1
- plugin = pm.get_plugin("myname")
- assert plugin.x == 42
- ret = pm.list_plugin_distinfo()
- # poor man's `assert ret == [(plugin, mock.ANY)]`
- assert len(ret) == 1
- assert len(ret[0]) == 2
- assert ret[0][0] == plugin
- assert ret[0][1]._dist == dist
- num = pm.load_setuptools_entrypoints("hello")
- assert num == 0 # no plugin loaded by this call
-
-
-def test_add_tracefuncs(he_pm):
- out = []
-
- class api1(object):
- @hookimpl
- def he_method1(self):
- out.append("he_method1-api1")
-
- class api2(object):
- @hookimpl
- def he_method1(self):
- out.append("he_method1-api2")
-
- he_pm.register(api1())
- he_pm.register(api2())
-
- def before(hook_name, hook_impls, kwargs):
- out.append((hook_name, list(hook_impls), kwargs))
-
- def after(outcome, hook_name, hook_impls, kwargs):
- out.append((outcome, hook_name, list(hook_impls), kwargs))
-
- undo = he_pm.add_hookcall_monitoring(before, after)
-
- he_pm.hook.he_method1(arg=1)
- assert len(out) == 4
- assert out[0][0] == "he_method1"
- assert len(out[0][1]) == 2
- assert isinstance(out[0][2], dict)
- assert out[1] == "he_method1-api2"
- assert out[2] == "he_method1-api1"
- assert len(out[3]) == 4
- assert out[3][1] == out[0][0]
-
- undo()
- he_pm.hook.he_method1(arg=1)
- assert len(out) == 4 + 2
-
-
-def test_hook_tracing(he_pm):
- saveindent = []
-
- class api1(object):
- @hookimpl
- def he_method1(self):
- saveindent.append(he_pm.trace.root.indent)
-
- class api2(object):
- @hookimpl
- def he_method1(self):
- saveindent.append(he_pm.trace.root.indent)
- raise ValueError()
-
- he_pm.register(api1())
- out = []
- he_pm.trace.root.setwriter(out.append)
- undo = he_pm.enable_tracing()
- try:
- indent = he_pm.trace.root.indent
- he_pm.hook.he_method1(arg=1)
- assert indent == he_pm.trace.root.indent
- assert len(out) == 2
- assert "he_method1" in out[0]
- assert "finish" in out[1]
-
- out[:] = []
- he_pm.register(api2())
-
- with pytest.raises(ValueError):
- he_pm.hook.he_method1(arg=1)
- assert he_pm.trace.root.indent == indent
- assert saveindent[0] > indent
- finally:
- undo()
-
-
-def test_implprefix_warning(recwarn):
- PluginManager(hookspec.project_name, "hello_")
- w = recwarn.pop(DeprecationWarning)
- assert "test_pluginmanager.py" in w.filename
-
-
-@pytest.mark.parametrize("include_hookspec", [True, False])
-def test_prefix_hookimpl(include_hookspec):
- with pytest.deprecated_call():
- pm = PluginManager(hookspec.project_name, "hello_")
-
- if include_hookspec:
-
- class HookSpec(object):
- @hookspec
- def hello_myhook(self, arg1):
- """ add to arg1 """
-
- pm.add_hookspecs(HookSpec)
-
- class Plugin(object):
- def hello_myhook(self, arg1):
- return arg1 + 1
-
- with pytest.deprecated_call():
- pm.register(Plugin())
- pm.register(Plugin())
- results = pm.hook.hello_myhook(arg1=17)
- assert results == [18, 18]
-
-
-def test_prefix_hookimpl_dontmatch_module():
- with pytest.deprecated_call():
- pm = PluginManager(hookspec.project_name, "hello_")
-
- class BadPlugin(object):
- hello_module = __import__("email")
-
- pm.register(BadPlugin())
- pm.check_pending()
+"""
+``PluginManager`` unit and public API testing.
+"""
+import pytest
+import types
+
+from pluggy import (
+ PluginManager,
+ PluginValidationError,
+ HookCallError,
+ HookimplMarker,
+ HookspecMarker,
+)
+from pluggy.manager import importlib_metadata
+
+
+hookspec = HookspecMarker("example")
+hookimpl = HookimplMarker("example")
+
+
+def test_plugin_double_register(pm):
+ """Registering the same plugin more then once isn't allowed"""
+ pm.register(42, name="abc")
+ with pytest.raises(ValueError):
+ pm.register(42, name="abc")
+ with pytest.raises(ValueError):
+ pm.register(42, name="def")
+
+
+def test_pm(pm):
+ """Basic registration with objects"""
+
+ class A(object):
+ pass
+
+ a1, a2 = A(), A()
+ pm.register(a1)
+ assert pm.is_registered(a1)
+ pm.register(a2, "hello")
+ assert pm.is_registered(a2)
+ out = pm.get_plugins()
+ assert a1 in out
+ assert a2 in out
+ assert pm.get_plugin("hello") == a2
+ assert pm.unregister(a1) == a1
+ assert not pm.is_registered(a1)
+
+ out = pm.list_name_plugin()
+ assert len(out) == 1
+ assert out == [("hello", a2)]
+
+
+def test_has_plugin(pm):
+ class A(object):
+ pass
+
+ a1 = A()
+ pm.register(a1, "hello")
+ assert pm.is_registered(a1)
+ assert pm.has_plugin("hello")
+
+
+def test_register_dynamic_attr(he_pm):
+ class A(object):
+ def __getattr__(self, name):
+ if name[0] != "_":
+ return 42
+ raise AttributeError()
+
+ a = A()
+ he_pm.register(a)
+ assert not he_pm.get_hookcallers(a)
+
+
+def test_pm_name(pm):
+ class A(object):
+ pass
+
+ a1 = A()
+ name = pm.register(a1, name="hello")
+ assert name == "hello"
+ pm.unregister(a1)
+ assert pm.get_plugin(a1) is None
+ assert not pm.is_registered(a1)
+ assert not pm.get_plugins()
+ name2 = pm.register(a1, name="hello")
+ assert name2 == name
+ pm.unregister(name="hello")
+ assert pm.get_plugin(a1) is None
+ assert not pm.is_registered(a1)
+ assert not pm.get_plugins()
+
+
+def test_set_blocked(pm):
+ class A(object):
+ pass
+
+ a1 = A()
+ name = pm.register(a1)
+ assert pm.is_registered(a1)
+ assert not pm.is_blocked(name)
+ pm.set_blocked(name)
+ assert pm.is_blocked(name)
+ assert not pm.is_registered(a1)
+
+ pm.set_blocked("somename")
+ assert pm.is_blocked("somename")
+ assert not pm.register(A(), "somename")
+ pm.unregister(name="somename")
+ assert pm.is_blocked("somename")
+
+
+def test_register_mismatch_method(he_pm):
+ class hello(object):
+ @hookimpl
+ def he_method_notexists(self):
+ pass
+
+ plugin = hello()
+
+ he_pm.register(plugin)
+ with pytest.raises(PluginValidationError) as excinfo:
+ he_pm.check_pending()
+ assert excinfo.value.plugin is plugin
+
+
+def test_register_mismatch_arg(he_pm):
+ class hello(object):
+ @hookimpl
+ def he_method1(self, qlwkje):
+ pass
+
+ plugin = hello()
+
+ with pytest.raises(PluginValidationError) as excinfo:
+ he_pm.register(plugin)
+ assert excinfo.value.plugin is plugin
+
+
+def test_register(pm):
+ class MyPlugin(object):
+ pass
+
+ my = MyPlugin()
+ pm.register(my)
+ assert my in pm.get_plugins()
+ my2 = MyPlugin()
+ pm.register(my2)
+ assert set([my, my2]).issubset(pm.get_plugins())
+
+ assert pm.is_registered(my)
+ assert pm.is_registered(my2)
+ pm.unregister(my)
+ assert not pm.is_registered(my)
+ assert my not in pm.get_plugins()
+
+
+def test_register_unknown_hooks(pm):
+ class Plugin1(object):
+ @hookimpl
+ def he_method1(self, arg):
+ return arg + 1
+
+ pname = pm.register(Plugin1())
+
+ class Hooks(object):
+ @hookspec
+ def he_method1(self, arg):
+ pass
+
+ pm.add_hookspecs(Hooks)
+ # assert not pm._unverified_hooks
+ assert pm.hook.he_method1(arg=1) == [2]
+ assert len(pm.get_hookcallers(pm.get_plugin(pname))) == 1
+
+
+def test_register_historic(pm):
+ class Hooks(object):
+ @hookspec(historic=True)
+ def he_method1(self, arg):
+ pass
+
+ pm.add_hookspecs(Hooks)
+
+ pm.hook.he_method1.call_historic(kwargs=dict(arg=1))
+ out = []
+
+ class Plugin(object):
+ @hookimpl
+ def he_method1(self, arg):
+ out.append(arg)
+
+ pm.register(Plugin())
+ assert out == [1]
+
+ class Plugin2(object):
+ @hookimpl
+ def he_method1(self, arg):
+ out.append(arg * 10)
+
+ pm.register(Plugin2())
+ assert out == [1, 10]
+ pm.hook.he_method1.call_historic(kwargs=dict(arg=12))
+ assert out == [1, 10, 120, 12]
+
+
+@pytest.mark.parametrize("result_callback", [True, False])
+def test_with_result_memorized(pm, result_callback):
+ """Verify that ``_HookCaller._maybe_apply_history()`
+ correctly applies the ``result_callback`` function, when provided,
+ to the result from calling each newly registered hook.
+ """
+ out = []
+ if result_callback:
+
+ def callback(res):
+ out.append(res)
+
+ else:
+ callback = None
+
+ class Hooks(object):
+ @hookspec(historic=True)
+ def he_method1(self, arg):
+ pass
+
+ pm.add_hookspecs(Hooks)
+
+ class Plugin1(object):
+ @hookimpl
+ def he_method1(self, arg):
+ return arg * 10
+
+ pm.register(Plugin1())
+
+ he_method1 = pm.hook.he_method1
+ he_method1.call_historic(result_callback=callback, kwargs=dict(arg=1))
+
+ class Plugin2(object):
+ @hookimpl
+ def he_method1(self, arg):
+ return arg * 10
+
+ pm.register(Plugin2())
+ if result_callback:
+ assert out == [10, 10]
+ else:
+ assert out == []
+
+
+def test_with_callbacks_immediately_executed(pm):
+ class Hooks(object):
+ @hookspec(historic=True)
+ def he_method1(self, arg):
+ pass
+
+ pm.add_hookspecs(Hooks)
+
+ class Plugin1(object):
+ @hookimpl
+ def he_method1(self, arg):
+ return arg * 10
+
+ class Plugin2(object):
+ @hookimpl
+ def he_method1(self, arg):
+ return arg * 20
+
+ class Plugin3(object):
+ @hookimpl
+ def he_method1(self, arg):
+ return arg * 30
+
+ out = []
+ pm.register(Plugin1())
+ pm.register(Plugin2())
+
+ he_method1 = pm.hook.he_method1
+ he_method1.call_historic(lambda res: out.append(res), dict(arg=1))
+ assert out == [20, 10]
+ pm.register(Plugin3())
+ assert out == [20, 10, 30]
+
+
+def test_register_historic_incompat_hookwrapper(pm):
+ class Hooks(object):
+ @hookspec(historic=True)
+ def he_method1(self, arg):
+ pass
+
+ pm.add_hookspecs(Hooks)
+
+ out = []
+
+ class Plugin(object):
+ @hookimpl(hookwrapper=True)
+ def he_method1(self, arg):
+ out.append(arg)
+
+ with pytest.raises(PluginValidationError):
+ pm.register(Plugin())
+
+
+def test_call_extra(pm):
+ class Hooks(object):
+ @hookspec
+ def he_method1(self, arg):
+ pass
+
+ pm.add_hookspecs(Hooks)
+
+ def he_method1(arg):
+ return arg * 10
+
+ out = pm.hook.he_method1.call_extra([he_method1], dict(arg=1))
+ assert out == [10]
+
+
+def test_call_with_too_few_args(pm):
+ class Hooks(object):
+ @hookspec
+ def he_method1(self, arg):
+ pass
+
+ pm.add_hookspecs(Hooks)
+
+ class Plugin1(object):
+ @hookimpl
+ def he_method1(self, arg):
+ 0 / 0
+
+ pm.register(Plugin1())
+ with pytest.raises(HookCallError):
+ with pytest.warns(UserWarning):
+ pm.hook.he_method1()
+
+
+def test_subset_hook_caller(pm):
+ class Hooks(object):
+ @hookspec
+ def he_method1(self, arg):
+ pass
+
+ pm.add_hookspecs(Hooks)
+
+ out = []
+
+ class Plugin1(object):
+ @hookimpl
+ def he_method1(self, arg):
+ out.append(arg)
+
+ class Plugin2(object):
+ @hookimpl
+ def he_method1(self, arg):
+ out.append(arg * 10)
+
+ class PluginNo(object):
+ pass
+
+ plugin1, plugin2, plugin3 = Plugin1(), Plugin2(), PluginNo()
+ pm.register(plugin1)
+ pm.register(plugin2)
+ pm.register(plugin3)
+ pm.hook.he_method1(arg=1)
+ assert out == [10, 1]
+ out[:] = []
+
+ hc = pm.subset_hook_caller("he_method1", [plugin1])
+ hc(arg=2)
+ assert out == [20]
+ out[:] = []
+
+ hc = pm.subset_hook_caller("he_method1", [plugin2])
+ hc(arg=2)
+ assert out == [2]
+ out[:] = []
+
+ pm.unregister(plugin1)
+ hc(arg=2)
+ assert out == []
+ out[:] = []
+
+ pm.hook.he_method1(arg=1)
+ assert out == [10]
+
+
+def test_get_hookimpls(pm):
+ class Hooks(object):
+ @hookspec
+ def he_method1(self, arg):
+ pass
+
+ pm.add_hookspecs(Hooks)
+ assert pm.hook.he_method1.get_hookimpls() == []
+
+ class Plugin1(object):
+ @hookimpl
+ def he_method1(self, arg):
+ pass
+
+ class Plugin2(object):
+ @hookimpl
+ def he_method1(self, arg):
+ pass
+
+ class PluginNo(object):
+ pass
+
+ plugin1, plugin2, plugin3 = Plugin1(), Plugin2(), PluginNo()
+ pm.register(plugin1)
+ pm.register(plugin2)
+ pm.register(plugin3)
+
+ hookimpls = pm.hook.he_method1.get_hookimpls()
+ hook_plugins = [item.plugin for item in hookimpls]
+ assert hook_plugins == [plugin1, plugin2]
+
+
+def test_add_hookspecs_nohooks(pm):
+ with pytest.raises(ValueError):
+ pm.add_hookspecs(10)
+
+
+def test_reject_prefixed_module(pm):
+ """Verify that a module type attribute that contains the project
+ prefix in its name (in this case `'example_*'` isn't collected
+ when registering a module which imports it.
+ """
+ pm._implprefix = "example"
+ conftest = types.ModuleType("conftest")
+ src = """
+def example_hook():
+ pass
+"""
+ exec(src, conftest.__dict__)
+ conftest.example_blah = types.ModuleType("example_blah")
+ with pytest.deprecated_call():
+ name = pm.register(conftest)
+ assert name == "conftest"
+ assert getattr(pm.hook, "example_blah", None) is None
+ assert getattr(
+ pm.hook, "example_hook", None
+ ) # conftest.example_hook should be collected
+ with pytest.deprecated_call():
+ assert pm.parse_hookimpl_opts(conftest, "example_blah") is None
+ assert pm.parse_hookimpl_opts(conftest, "example_hook") == {}
+
+
+def test_load_setuptools_instantiation(monkeypatch, pm):
+ class EntryPoint(object):
+ name = "myname"
+ group = "hello"
+ value = "myname:foo"
+
+ def load(self):
+ class PseudoPlugin(object):
+ x = 42
+
+ return PseudoPlugin()
+
+ class Distribution(object):
+ entry_points = (EntryPoint(),)
+
+ dist = Distribution()
+
+ def my_distributions():
+ return (dist,)
+
+ monkeypatch.setattr(importlib_metadata, "distributions", my_distributions)
+ num = pm.load_setuptools_entrypoints("hello")
+ assert num == 1
+ plugin = pm.get_plugin("myname")
+ assert plugin.x == 42
+ ret = pm.list_plugin_distinfo()
+ # poor man's `assert ret == [(plugin, mock.ANY)]`
+ assert len(ret) == 1
+ assert len(ret[0]) == 2
+ assert ret[0][0] == plugin
+ assert ret[0][1]._dist == dist
+ num = pm.load_setuptools_entrypoints("hello")
+ assert num == 0 # no plugin loaded by this call
+
+
+def test_add_tracefuncs(he_pm):
+ out = []
+
+ class api1(object):
+ @hookimpl
+ def he_method1(self):
+ out.append("he_method1-api1")
+
+ class api2(object):
+ @hookimpl
+ def he_method1(self):
+ out.append("he_method1-api2")
+
+ he_pm.register(api1())
+ he_pm.register(api2())
+
+ def before(hook_name, hook_impls, kwargs):
+ out.append((hook_name, list(hook_impls), kwargs))
+
+ def after(outcome, hook_name, hook_impls, kwargs):
+ out.append((outcome, hook_name, list(hook_impls), kwargs))
+
+ undo = he_pm.add_hookcall_monitoring(before, after)
+
+ he_pm.hook.he_method1(arg=1)
+ assert len(out) == 4
+ assert out[0][0] == "he_method1"
+ assert len(out[0][1]) == 2
+ assert isinstance(out[0][2], dict)
+ assert out[1] == "he_method1-api2"
+ assert out[2] == "he_method1-api1"
+ assert len(out[3]) == 4
+ assert out[3][1] == out[0][0]
+
+ undo()
+ he_pm.hook.he_method1(arg=1)
+ assert len(out) == 4 + 2
+
+
+def test_hook_tracing(he_pm):
+ saveindent = []
+
+ class api1(object):
+ @hookimpl
+ def he_method1(self):
+ saveindent.append(he_pm.trace.root.indent)
+
+ class api2(object):
+ @hookimpl
+ def he_method1(self):
+ saveindent.append(he_pm.trace.root.indent)
+ raise ValueError()
+
+ he_pm.register(api1())
+ out = []
+ he_pm.trace.root.setwriter(out.append)
+ undo = he_pm.enable_tracing()
+ try:
+ indent = he_pm.trace.root.indent
+ he_pm.hook.he_method1(arg=1)
+ assert indent == he_pm.trace.root.indent
+ assert len(out) == 2
+ assert "he_method1" in out[0]
+ assert "finish" in out[1]
+
+ out[:] = []
+ he_pm.register(api2())
+
+ with pytest.raises(ValueError):
+ he_pm.hook.he_method1(arg=1)
+ assert he_pm.trace.root.indent == indent
+ assert saveindent[0] > indent
+ finally:
+ undo()
+
+
+def test_implprefix_warning(recwarn):
+ PluginManager(hookspec.project_name, "hello_")
+ w = recwarn.pop(DeprecationWarning)
+ assert "test_pluginmanager.py" in w.filename
+
+
+@pytest.mark.parametrize("include_hookspec", [True, False])
+def test_prefix_hookimpl(include_hookspec):
+ with pytest.deprecated_call():
+ pm = PluginManager(hookspec.project_name, "hello_")
+
+ if include_hookspec:
+
+ class HookSpec(object):
+ @hookspec
+ def hello_myhook(self, arg1):
+ """ add to arg1 """
+
+ pm.add_hookspecs(HookSpec)
+
+ class Plugin(object):
+ def hello_myhook(self, arg1):
+ return arg1 + 1
+
+ with pytest.deprecated_call():
+ pm.register(Plugin())
+ pm.register(Plugin())
+ results = pm.hook.hello_myhook(arg1=17)
+ assert results == [18, 18]
+
+
+def test_prefix_hookimpl_dontmatch_module():
+ with pytest.deprecated_call():
+ pm = PluginManager(hookspec.project_name, "hello_")
+
+ class BadPlugin(object):
+ hello_module = __import__("email")
+
+ pm.register(BadPlugin())
+ pm.check_pending()
diff --git a/contrib/python/pluggy/py2/tests/test_tracer.py b/contrib/python/pluggy/py2/tests/test_tracer.py
index 992ec67914..7915f0ee75 100644
--- a/contrib/python/pluggy/py2/tests/test_tracer.py
+++ b/contrib/python/pluggy/py2/tests/test_tracer.py
@@ -1,78 +1,78 @@
-from pluggy._tracing import TagTracer
-
-import pytest
-
-
-@pytest.fixture
-def rootlogger():
- return TagTracer()
-
-
-def test_simple(rootlogger):
- log = rootlogger.get("pytest")
- log("hello")
- out = []
- rootlogger.setwriter(out.append)
- log("world")
- assert len(out) == 1
- assert out[0] == "world [pytest]\n"
- sublog = log.get("collection")
- sublog("hello")
- assert out[1] == "hello [pytest:collection]\n"
-
-
-def test_indent(rootlogger):
- log = rootlogger.get("1")
- out = []
- log.root.setwriter(lambda arg: out.append(arg))
- log("hello")
- log.root.indent += 1
- log("line1")
- log("line2")
- log.root.indent += 1
- log("line3")
- log("line4")
- log.root.indent -= 1
- log("line5")
- log.root.indent -= 1
- log("last")
- assert len(out) == 7
- names = [x[: x.rfind(" [")] for x in out]
- assert names == [
- "hello",
- " line1",
- " line2",
- " line3",
- " line4",
- " line5",
- "last",
- ]
-
-
-def test_readable_output_dictargs(rootlogger):
-
- out = rootlogger._format_message(["test"], [1])
- assert out == "1 [test]\n"
-
- out2 = rootlogger._format_message(["test"], ["test", {"a": 1}])
- assert out2 == "test [test]\n a: 1\n"
-
-
-def test_setprocessor(rootlogger):
- log = rootlogger.get("1")
- log2 = log.get("2")
- assert log2.tags == tuple("12")
- out = []
- rootlogger.setprocessor(tuple("12"), lambda *args: out.append(args))
- log("not seen")
- log2("seen")
- assert len(out) == 1
- tags, args = out[0]
- assert "1" in tags
- assert "2" in tags
- assert args == ("seen",)
- l2 = []
- rootlogger.setprocessor("1:2", lambda *args: l2.append(args))
- log2("seen")
- tags, args = l2[0]
- assert args == ("seen",)
+from pluggy._tracing import TagTracer
+
+import pytest
+
+
+@pytest.fixture
+def rootlogger():
+ return TagTracer()
+
+
+def test_simple(rootlogger):
+ log = rootlogger.get("pytest")
+ log("hello")
+ out = []
+ rootlogger.setwriter(out.append)
+ log("world")
+ assert len(out) == 1
+ assert out[0] == "world [pytest]\n"
+ sublog = log.get("collection")
+ sublog("hello")
+ assert out[1] == "hello [pytest:collection]\n"
+
+
+def test_indent(rootlogger):
+ log = rootlogger.get("1")
+ out = []
+ log.root.setwriter(lambda arg: out.append(arg))
+ log("hello")
+ log.root.indent += 1
+ log("line1")
+ log("line2")
+ log.root.indent += 1
+ log("line3")
+ log("line4")
+ log.root.indent -= 1
+ log("line5")
+ log.root.indent -= 1
+ log("last")
+ assert len(out) == 7
+ names = [x[: x.rfind(" [")] for x in out]
+ assert names == [
+ "hello",
+ " line1",
+ " line2",
+ " line3",
+ " line4",
+ " line5",
+ "last",
+ ]
+
+
+def test_readable_output_dictargs(rootlogger):
+
+ out = rootlogger._format_message(["test"], [1])
+ assert out == "1 [test]\n"
+
+ out2 = rootlogger._format_message(["test"], ["test", {"a": 1}])
+ assert out2 == "test [test]\n a: 1\n"
+
+
+def test_setprocessor(rootlogger):
+ log = rootlogger.get("1")
+ log2 = log.get("2")
+ assert log2.tags == tuple("12")
+ out = []
+ rootlogger.setprocessor(tuple("12"), lambda *args: out.append(args))
+ log("not seen")
+ log2("seen")
+ assert len(out) == 1
+ tags, args = out[0]
+ assert "1" in tags
+ assert "2" in tags
+ assert args == ("seen",)
+ l2 = []
+ rootlogger.setprocessor("1:2", lambda *args: l2.append(args))
+ log2("seen")
+ tags, args = l2[0]
+ assert args == ("seen",)
diff --git a/contrib/python/pluggy/py2/tests/ya.make b/contrib/python/pluggy/py2/tests/ya.make
index a0bd221149..2091a66adf 100644
--- a/contrib/python/pluggy/py2/tests/ya.make
+++ b/contrib/python/pluggy/py2/tests/ya.make
@@ -1,23 +1,23 @@
-PY2TEST()
-
-OWNER(g:python-contrib)
-
-PEERDIR (
- contrib/python/pluggy
-)
-
-TEST_SRCS(
- conftest.py
- test_deprecations.py
- test_details.py
- test_helpers.py
- test_hookcaller.py
- test_invocations.py
- test_multicall.py
- test_pluginmanager.py
- test_tracer.py
-)
-
-NO_LINT()
-
-END()
+PY2TEST()
+
+OWNER(g:python-contrib)
+
+PEERDIR (
+ contrib/python/pluggy
+)
+
+TEST_SRCS(
+ conftest.py
+ test_deprecations.py
+ test_details.py
+ test_helpers.py
+ test_hookcaller.py
+ test_invocations.py
+ test_multicall.py
+ test_pluginmanager.py
+ test_tracer.py
+)
+
+NO_LINT()
+
+END()
diff --git a/contrib/python/pluggy/py2/ya.make b/contrib/python/pluggy/py2/ya.make
index 40ae96f0bf..9e48d90b96 100644
--- a/contrib/python/pluggy/py2/ya.make
+++ b/contrib/python/pluggy/py2/ya.make
@@ -1,15 +1,15 @@
-PY2_LIBRARY()
+PY2_LIBRARY()
OWNER(g:python-contrib)
-VERSION(0.13.1)
-
-LICENSE(MIT)
-
-PEERDIR(
- contrib/python/importlib-metadata
-)
+VERSION(0.13.1)
+LICENSE(MIT)
+
+PEERDIR(
+ contrib/python/importlib-metadata
+)
+
NO_LINT()
PY_SRCS(
@@ -22,14 +22,14 @@ PY_SRCS(
pluggy/manager.py
)
-RESOURCE_FILES(
- PREFIX contrib/python/pluggy/py2/
- .dist-info/METADATA
- .dist-info/top_level.txt
-)
-
+RESOURCE_FILES(
+ PREFIX contrib/python/pluggy/py2/
+ .dist-info/METADATA
+ .dist-info/top_level.txt
+)
+
END()
-
-RECURSE_FOR_TESTS(
- tests
-)
+
+RECURSE_FOR_TESTS(
+ tests
+)
diff --git a/contrib/python/pluggy/py3/.dist-info/METADATA b/contrib/python/pluggy/py3/.dist-info/METADATA
index 976c06cddb..dcbd076ac1 100644
--- a/contrib/python/pluggy/py3/.dist-info/METADATA
+++ b/contrib/python/pluggy/py3/.dist-info/METADATA
@@ -1,143 +1,143 @@
-Metadata-Version: 2.1
-Name: pluggy
-Version: 1.0.0
-Summary: plugin and hook calling mechanisms for python
-Home-page: https://github.com/pytest-dev/pluggy
-Author: Holger Krekel
-Author-email: holger@merlinux.eu
-License: MIT
-Platform: unix
-Platform: linux
-Platform: osx
-Platform: win32
-Classifier: Development Status :: 6 - Mature
-Classifier: Intended Audience :: Developers
-Classifier: License :: OSI Approved :: MIT License
-Classifier: Operating System :: POSIX
-Classifier: Operating System :: Microsoft :: Windows
-Classifier: Operating System :: MacOS :: MacOS X
-Classifier: Topic :: Software Development :: Testing
-Classifier: Topic :: Software Development :: Libraries
-Classifier: Topic :: Utilities
-Classifier: Programming Language :: Python :: Implementation :: CPython
-Classifier: Programming Language :: Python :: Implementation :: PyPy
-Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3 :: Only
-Classifier: Programming Language :: Python :: 3.6
-Classifier: Programming Language :: Python :: 3.7
-Classifier: Programming Language :: Python :: 3.8
-Classifier: Programming Language :: Python :: 3.9
-Requires-Python: >=3.6
-Description-Content-Type: text/x-rst
-License-File: LICENSE
-Requires-Dist: importlib-metadata (>=0.12) ; python_version < "3.8"
-Provides-Extra: dev
-Requires-Dist: pre-commit ; extra == 'dev'
-Requires-Dist: tox ; extra == 'dev'
-Provides-Extra: testing
-Requires-Dist: pytest ; extra == 'testing'
-Requires-Dist: pytest-benchmark ; extra == 'testing'
-
-====================================================
-pluggy - A minimalist production ready plugin system
-====================================================
-
-|pypi| |conda-forge| |versions| |github-actions| |gitter| |black| |codecov|
-
-This is the core framework used by the `pytest`_, `tox`_, and `devpi`_ projects.
-
-Please `read the docs`_ to learn more!
-
-A definitive example
-====================
-.. code-block:: python
-
- 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]
-
-
-.. badges
-
-.. |pypi| image:: https://img.shields.io/pypi/v/pluggy.svg
- :target: https://pypi.org/pypi/pluggy
-
-.. |versions| image:: https://img.shields.io/pypi/pyversions/pluggy.svg
- :target: https://pypi.org/pypi/pluggy
-
-.. |github-actions| image:: https://github.com/pytest-dev/pluggy/workflows/main/badge.svg
- :target: https://github.com/pytest-dev/pluggy/actions
-
-.. |conda-forge| image:: https://img.shields.io/conda/vn/conda-forge/pluggy.svg
- :target: https://anaconda.org/conda-forge/pytest
-
-.. |gitter| image:: https://badges.gitter.im/pytest-dev/pluggy.svg
- :alt: Join the chat at https://gitter.im/pytest-dev/pluggy
- :target: https://gitter.im/pytest-dev/pluggy?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
-
-.. |black| image:: https://img.shields.io/badge/code%20style-black-000000.svg
- :target: https://github.com/ambv/black
-
-.. |codecov| image:: https://codecov.io/gh/pytest-dev/pluggy/branch/master/graph/badge.svg
- :target: https://codecov.io/gh/pytest-dev/pluggy
- :alt: Code coverage Status
-
-.. links
-.. _pytest:
- http://pytest.org
-.. _tox:
- https://tox.readthedocs.org
-.. _devpi:
- http://doc.devpi.net
-.. _read the docs:
- https://pluggy.readthedocs.io/en/latest/
-
-
+Metadata-Version: 2.1
+Name: pluggy
+Version: 1.0.0
+Summary: plugin and hook calling mechanisms for python
+Home-page: https://github.com/pytest-dev/pluggy
+Author: Holger Krekel
+Author-email: holger@merlinux.eu
+License: MIT
+Platform: unix
+Platform: linux
+Platform: osx
+Platform: win32
+Classifier: Development Status :: 6 - Mature
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Operating System :: POSIX
+Classifier: Operating System :: Microsoft :: Windows
+Classifier: Operating System :: MacOS :: MacOS X
+Classifier: Topic :: Software Development :: Testing
+Classifier: Topic :: Software Development :: Libraries
+Classifier: Topic :: Utilities
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3 :: Only
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: 3.8
+Classifier: Programming Language :: Python :: 3.9
+Requires-Python: >=3.6
+Description-Content-Type: text/x-rst
+License-File: LICENSE
+Requires-Dist: importlib-metadata (>=0.12) ; python_version < "3.8"
+Provides-Extra: dev
+Requires-Dist: pre-commit ; extra == 'dev'
+Requires-Dist: tox ; extra == 'dev'
+Provides-Extra: testing
+Requires-Dist: pytest ; extra == 'testing'
+Requires-Dist: pytest-benchmark ; extra == 'testing'
+
+====================================================
+pluggy - A minimalist production ready plugin system
+====================================================
+
+|pypi| |conda-forge| |versions| |github-actions| |gitter| |black| |codecov|
+
+This is the core framework used by the `pytest`_, `tox`_, and `devpi`_ projects.
+
+Please `read the docs`_ to learn more!
+
+A definitive example
+====================
+.. code-block:: python
+
+ 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]
+
+
+.. badges
+
+.. |pypi| image:: https://img.shields.io/pypi/v/pluggy.svg
+ :target: https://pypi.org/pypi/pluggy
+
+.. |versions| image:: https://img.shields.io/pypi/pyversions/pluggy.svg
+ :target: https://pypi.org/pypi/pluggy
+
+.. |github-actions| image:: https://github.com/pytest-dev/pluggy/workflows/main/badge.svg
+ :target: https://github.com/pytest-dev/pluggy/actions
+
+.. |conda-forge| image:: https://img.shields.io/conda/vn/conda-forge/pluggy.svg
+ :target: https://anaconda.org/conda-forge/pytest
+
+.. |gitter| image:: https://badges.gitter.im/pytest-dev/pluggy.svg
+ :alt: Join the chat at https://gitter.im/pytest-dev/pluggy
+ :target: https://gitter.im/pytest-dev/pluggy?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
+
+.. |black| image:: https://img.shields.io/badge/code%20style-black-000000.svg
+ :target: https://github.com/ambv/black
+
+.. |codecov| image:: https://codecov.io/gh/pytest-dev/pluggy/branch/master/graph/badge.svg
+ :target: https://codecov.io/gh/pytest-dev/pluggy
+ :alt: Code coverage Status
+
+.. links
+.. _pytest:
+ http://pytest.org
+.. _tox:
+ https://tox.readthedocs.org
+.. _devpi:
+ http://doc.devpi.net
+.. _read the docs:
+ https://pluggy.readthedocs.io/en/latest/
+
+
diff --git a/contrib/python/pluggy/py3/.dist-info/top_level.txt b/contrib/python/pluggy/py3/.dist-info/top_level.txt
index 11bdb5c1f5..b78af1723b 100644
--- a/contrib/python/pluggy/py3/.dist-info/top_level.txt
+++ b/contrib/python/pluggy/py3/.dist-info/top_level.txt
@@ -1 +1 @@
-pluggy
+pluggy
diff --git a/contrib/python/pluggy/py3/LICENSE b/contrib/python/pluggy/py3/LICENSE
index 85f4dd63d2..58f08de42e 100644
--- a/contrib/python/pluggy/py3/LICENSE
+++ b/contrib/python/pluggy/py3/LICENSE
@@ -1,21 +1,21 @@
-The MIT License (MIT)
-
-Copyright (c) 2015 holger krekel (rather uses bitbucket/hpk42)
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
+The MIT License (MIT)
+
+Copyright (c) 2015 holger krekel (rather uses bitbucket/hpk42)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/contrib/python/pluggy/py3/README.rst b/contrib/python/pluggy/py3/README.rst
index 3496617e1e..7040442bf8 100644
--- a/contrib/python/pluggy/py3/README.rst
+++ b/contrib/python/pluggy/py3/README.rst
@@ -1,101 +1,101 @@
-====================================================
-pluggy - A minimalist production ready plugin system
-====================================================
-
-|pypi| |conda-forge| |versions| |github-actions| |gitter| |black| |codecov|
-
-This is the core framework used by the `pytest`_, `tox`_, and `devpi`_ projects.
-
-Please `read the docs`_ to learn more!
-
-A definitive example
-====================
-.. code-block:: python
-
- 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]
-
-
-.. badges
-
-.. |pypi| image:: https://img.shields.io/pypi/v/pluggy.svg
- :target: https://pypi.org/pypi/pluggy
-
-.. |versions| image:: https://img.shields.io/pypi/pyversions/pluggy.svg
- :target: https://pypi.org/pypi/pluggy
-
-.. |github-actions| image:: https://github.com/pytest-dev/pluggy/workflows/main/badge.svg
- :target: https://github.com/pytest-dev/pluggy/actions
-
-.. |conda-forge| image:: https://img.shields.io/conda/vn/conda-forge/pluggy.svg
- :target: https://anaconda.org/conda-forge/pytest
-
-.. |gitter| image:: https://badges.gitter.im/pytest-dev/pluggy.svg
- :alt: Join the chat at https://gitter.im/pytest-dev/pluggy
- :target: https://gitter.im/pytest-dev/pluggy?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
-
-.. |black| image:: https://img.shields.io/badge/code%20style-black-000000.svg
- :target: https://github.com/ambv/black
-
-.. |codecov| image:: https://codecov.io/gh/pytest-dev/pluggy/branch/master/graph/badge.svg
- :target: https://codecov.io/gh/pytest-dev/pluggy
- :alt: Code coverage Status
-
-.. links
-.. _pytest:
- http://pytest.org
-.. _tox:
- https://tox.readthedocs.org
-.. _devpi:
- http://doc.devpi.net
-.. _read the docs:
- https://pluggy.readthedocs.io/en/latest/
+====================================================
+pluggy - A minimalist production ready plugin system
+====================================================
+
+|pypi| |conda-forge| |versions| |github-actions| |gitter| |black| |codecov|
+
+This is the core framework used by the `pytest`_, `tox`_, and `devpi`_ projects.
+
+Please `read the docs`_ to learn more!
+
+A definitive example
+====================
+.. code-block:: python
+
+ 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]
+
+
+.. badges
+
+.. |pypi| image:: https://img.shields.io/pypi/v/pluggy.svg
+ :target: https://pypi.org/pypi/pluggy
+
+.. |versions| image:: https://img.shields.io/pypi/pyversions/pluggy.svg
+ :target: https://pypi.org/pypi/pluggy
+
+.. |github-actions| image:: https://github.com/pytest-dev/pluggy/workflows/main/badge.svg
+ :target: https://github.com/pytest-dev/pluggy/actions
+
+.. |conda-forge| image:: https://img.shields.io/conda/vn/conda-forge/pluggy.svg
+ :target: https://anaconda.org/conda-forge/pytest
+
+.. |gitter| image:: https://badges.gitter.im/pytest-dev/pluggy.svg
+ :alt: Join the chat at https://gitter.im/pytest-dev/pluggy
+ :target: https://gitter.im/pytest-dev/pluggy?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
+
+.. |black| image:: https://img.shields.io/badge/code%20style-black-000000.svg
+ :target: https://github.com/ambv/black
+
+.. |codecov| image:: https://codecov.io/gh/pytest-dev/pluggy/branch/master/graph/badge.svg
+ :target: https://codecov.io/gh/pytest-dev/pluggy
+ :alt: Code coverage Status
+
+.. links
+.. _pytest:
+ http://pytest.org
+.. _tox:
+ https://tox.readthedocs.org
+.. _devpi:
+ http://doc.devpi.net
+.. _read the docs:
+ https://pluggy.readthedocs.io/en/latest/
diff --git a/contrib/python/pluggy/py3/pluggy/__init__.py b/contrib/python/pluggy/py3/pluggy/__init__.py
index 979028f759..a874cc0965 100644
--- a/contrib/python/pluggy/py3/pluggy/__init__.py
+++ b/contrib/python/pluggy/py3/pluggy/__init__.py
@@ -1,18 +1,18 @@
-try:
- from ._version import version as __version__
-except ImportError:
- # broken installation, we don't even try
- # unknown only works because we do poor mans version compare
- __version__ = "unknown"
-
-__all__ = [
- "PluginManager",
- "PluginValidationError",
- "HookCallError",
- "HookspecMarker",
- "HookimplMarker",
-]
-
-from ._manager import PluginManager, PluginValidationError
-from ._callers import HookCallError
-from ._hooks import HookspecMarker, HookimplMarker
+try:
+ from ._version import version as __version__
+except ImportError:
+ # broken installation, we don't even try
+ # unknown only works because we do poor mans version compare
+ __version__ = "unknown"
+
+__all__ = [
+ "PluginManager",
+ "PluginValidationError",
+ "HookCallError",
+ "HookspecMarker",
+ "HookimplMarker",
+]
+
+from ._manager import PluginManager, PluginValidationError
+from ._callers import HookCallError
+from ._hooks import HookspecMarker, HookimplMarker
diff --git a/contrib/python/pluggy/py3/pluggy/_callers.py b/contrib/python/pluggy/py3/pluggy/_callers.py
index 7a16f3bdd4..b64b85a23b 100644
--- a/contrib/python/pluggy/py3/pluggy/_callers.py
+++ b/contrib/python/pluggy/py3/pluggy/_callers.py
@@ -1,60 +1,60 @@
-"""
-Call loop machinery
-"""
-import sys
-
-from ._result import HookCallError, _Result, _raise_wrapfail
-
-
-def _multicall(hook_name, hook_impls, caller_kwargs, firstresult):
- """Execute a call into multiple python functions/methods and return the
- result(s).
-
- ``caller_kwargs`` comes from _HookCaller.__call__().
- """
- __tracebackhide__ = True
- results = []
- excinfo = None
- try: # run impl and wrapper setup functions in a loop
- teardowns = []
- try:
- for hook_impl in reversed(hook_impls):
- try:
- args = [caller_kwargs[argname] for argname in hook_impl.argnames]
- except KeyError:
- for argname in hook_impl.argnames:
- if argname not in caller_kwargs:
- raise HookCallError(
- f"hook call must provide argument {argname!r}"
- )
-
- if hook_impl.hookwrapper:
- try:
- gen = hook_impl.function(*args)
- next(gen) # first yield
- teardowns.append(gen)
- except StopIteration:
- _raise_wrapfail(gen, "did not yield")
- else:
- res = hook_impl.function(*args)
- if res is not None:
- results.append(res)
- if firstresult: # halt further impl calls
- break
- except BaseException:
- excinfo = sys.exc_info()
- finally:
- if firstresult: # first result hooks return a single value
- outcome = _Result(results[0] if results else None, excinfo)
- else:
- outcome = _Result(results, excinfo)
-
- # run all wrapper post-yield blocks
- for gen in reversed(teardowns):
- try:
- gen.send(outcome)
- _raise_wrapfail(gen, "has second yield")
- except StopIteration:
- pass
-
- return outcome.get_result()
+"""
+Call loop machinery
+"""
+import sys
+
+from ._result import HookCallError, _Result, _raise_wrapfail
+
+
+def _multicall(hook_name, hook_impls, caller_kwargs, firstresult):
+ """Execute a call into multiple python functions/methods and return the
+ result(s).
+
+ ``caller_kwargs`` comes from _HookCaller.__call__().
+ """
+ __tracebackhide__ = True
+ results = []
+ excinfo = None
+ try: # run impl and wrapper setup functions in a loop
+ teardowns = []
+ try:
+ for hook_impl in reversed(hook_impls):
+ try:
+ args = [caller_kwargs[argname] for argname in hook_impl.argnames]
+ except KeyError:
+ for argname in hook_impl.argnames:
+ if argname not in caller_kwargs:
+ raise HookCallError(
+ f"hook call must provide argument {argname!r}"
+ )
+
+ if hook_impl.hookwrapper:
+ try:
+ gen = hook_impl.function(*args)
+ next(gen) # first yield
+ teardowns.append(gen)
+ except StopIteration:
+ _raise_wrapfail(gen, "did not yield")
+ else:
+ res = hook_impl.function(*args)
+ if res is not None:
+ results.append(res)
+ if firstresult: # halt further impl calls
+ break
+ except BaseException:
+ excinfo = sys.exc_info()
+ finally:
+ if firstresult: # first result hooks return a single value
+ outcome = _Result(results[0] if results else None, excinfo)
+ else:
+ outcome = _Result(results, excinfo)
+
+ # run all wrapper post-yield blocks
+ for gen in reversed(teardowns):
+ try:
+ gen.send(outcome)
+ _raise_wrapfail(gen, "has second yield")
+ except StopIteration:
+ pass
+
+ return outcome.get_result()
diff --git a/contrib/python/pluggy/py3/pluggy/_hooks.py b/contrib/python/pluggy/py3/pluggy/_hooks.py
index 1e5fbb7595..a057b0aae2 100644
--- a/contrib/python/pluggy/py3/pluggy/_hooks.py
+++ b/contrib/python/pluggy/py3/pluggy/_hooks.py
@@ -1,325 +1,325 @@
-"""
-Internal hook annotation, representation and calling machinery.
-"""
-import inspect
-import sys
-import warnings
-
-
-class HookspecMarker:
- """Decorator helper class for marking functions as hook specifications.
-
- You can instantiate it with a project_name to get a decorator.
- Calling :py:meth:`.PluginManager.add_hookspecs` later will discover all marked functions
- if the :py:class:`.PluginManager` uses the same project_name.
- """
-
- def __init__(self, project_name):
- self.project_name = project_name
-
- def __call__(
- self, function=None, firstresult=False, historic=False, warn_on_impl=None
- ):
- """if passed a function, directly sets attributes on the function
- which will make it discoverable to :py:meth:`.PluginManager.add_hookspecs`.
- If passed no function, returns a decorator which can be applied to a function
- later using the attributes supplied.
-
- If ``firstresult`` is ``True`` the 1:N hook call (N being the number of registered
- hook implementation functions) will stop at I<=N when the I'th function
- returns a non-``None`` result.
-
- If ``historic`` is ``True`` calls to a hook will be memorized and replayed
- on later registered plugins.
-
- """
-
- def setattr_hookspec_opts(func):
- if historic and firstresult:
- raise ValueError("cannot have a historic firstresult hook")
- setattr(
- func,
- self.project_name + "_spec",
- dict(
- firstresult=firstresult,
- historic=historic,
- warn_on_impl=warn_on_impl,
- ),
- )
- return func
-
- if function is not None:
- return setattr_hookspec_opts(function)
- else:
- return setattr_hookspec_opts
-
-
-class HookimplMarker:
- """Decorator helper class for marking functions as hook implementations.
-
- You can instantiate with a ``project_name`` to get a decorator.
- Calling :py:meth:`.PluginManager.register` later will discover all marked functions
- if the :py:class:`.PluginManager` uses the same project_name.
- """
-
- def __init__(self, project_name):
- self.project_name = project_name
-
- def __call__(
- self,
- function=None,
- hookwrapper=False,
- optionalhook=False,
- tryfirst=False,
- trylast=False,
- specname=None,
- ):
-
- """if passed a function, directly sets attributes on the function
- which will make it discoverable to :py:meth:`.PluginManager.register`.
- If passed no function, returns a decorator which can be applied to a
- function later using the attributes supplied.
-
- If ``optionalhook`` is ``True`` a missing matching hook specification will not result
- in an error (by default it is an error if no matching spec is found).
-
- If ``tryfirst`` is ``True`` this hook implementation will run as early as possible
- in the chain of N hook implementations for a specification.
-
- If ``trylast`` is ``True`` this hook implementation will run as late as possible
- in the chain of N hook implementations.
-
- If ``hookwrapper`` is ``True`` the hook implementations needs to execute exactly
- one ``yield``. The code before the ``yield`` is run early before any non-hookwrapper
- function is run. The code after the ``yield`` is run after all non-hookwrapper
- function have run. The ``yield`` receives a :py:class:`.callers._Result` object
- representing the exception or result outcome of the inner calls (including other
- hookwrapper calls).
-
- If ``specname`` is provided, it will be used instead of the function name when
- matching this hook implementation to a hook specification during registration.
-
- """
-
- def setattr_hookimpl_opts(func):
- setattr(
- func,
- self.project_name + "_impl",
- dict(
- hookwrapper=hookwrapper,
- optionalhook=optionalhook,
- tryfirst=tryfirst,
- trylast=trylast,
- specname=specname,
- ),
- )
- return func
-
- if function is None:
- return setattr_hookimpl_opts
- else:
- return setattr_hookimpl_opts(function)
-
-
-def normalize_hookimpl_opts(opts):
- opts.setdefault("tryfirst", False)
- opts.setdefault("trylast", False)
- opts.setdefault("hookwrapper", False)
- opts.setdefault("optionalhook", False)
- opts.setdefault("specname", None)
-
-
-_PYPY = hasattr(sys, "pypy_version_info")
-
-
-def varnames(func):
- """Return tuple of positional and keywrord argument names for a function,
- method, class or callable.
-
- In case of a class, its ``__init__`` method is considered.
- For methods the ``self`` parameter is not included.
- """
- if inspect.isclass(func):
- try:
- func = func.__init__
- except AttributeError:
- return (), ()
- elif not inspect.isroutine(func): # callable object?
- try:
- func = getattr(func, "__call__", func)
- except Exception:
- return (), ()
-
- try: # func MUST be a function or method here or we won't parse any args
- spec = inspect.getfullargspec(func)
- except TypeError:
- return (), ()
-
- args, defaults = tuple(spec.args), spec.defaults
- if defaults:
- index = -len(defaults)
- args, kwargs = args[:index], tuple(args[index:])
- else:
- kwargs = ()
-
- # strip any implicit instance arg
- # pypy3 uses "obj" instead of "self" for default dunder methods
- implicit_names = ("self",) if not _PYPY else ("self", "obj")
- if args:
- if inspect.ismethod(func) or (
- "." in getattr(func, "__qualname__", ()) and args[0] in implicit_names
- ):
- args = args[1:]
-
- return args, kwargs
-
-
-class _HookRelay:
- """hook holder object for performing 1:N hook calls where N is the number
- of registered plugins.
-
- """
-
-
-class _HookCaller:
- def __init__(self, name, hook_execute, specmodule_or_class=None, spec_opts=None):
- self.name = name
- self._wrappers = []
- self._nonwrappers = []
- self._hookexec = hook_execute
- self._call_history = None
- self.spec = None
- if specmodule_or_class is not None:
- assert spec_opts is not None
- self.set_specification(specmodule_or_class, spec_opts)
-
- def has_spec(self):
- return self.spec is not None
-
- def set_specification(self, specmodule_or_class, spec_opts):
- assert not self.has_spec()
- self.spec = HookSpec(specmodule_or_class, self.name, spec_opts)
- if spec_opts.get("historic"):
- self._call_history = []
-
- def is_historic(self):
- return self._call_history is not None
-
- def _remove_plugin(self, plugin):
- def remove(wrappers):
- for i, method in enumerate(wrappers):
- if method.plugin == plugin:
- del wrappers[i]
- return True
-
- if remove(self._wrappers) is None:
- if remove(self._nonwrappers) is None:
- raise ValueError(f"plugin {plugin!r} not found")
-
- def get_hookimpls(self):
- # Order is important for _hookexec
- return self._nonwrappers + self._wrappers
-
- def _add_hookimpl(self, hookimpl):
- """Add an implementation to the callback chain."""
- if hookimpl.hookwrapper:
- methods = self._wrappers
- else:
- methods = self._nonwrappers
-
- if hookimpl.trylast:
- methods.insert(0, hookimpl)
- elif hookimpl.tryfirst:
- methods.append(hookimpl)
- else:
- # find last non-tryfirst method
- i = len(methods) - 1
- while i >= 0 and methods[i].tryfirst:
- i -= 1
- methods.insert(i + 1, hookimpl)
-
- def __repr__(self):
- return f"<_HookCaller {self.name!r}>"
-
- def __call__(self, *args, **kwargs):
- if args:
- raise TypeError("hook calling supports only keyword arguments")
- assert not self.is_historic()
-
- # This is written to avoid expensive operations when not needed.
- if self.spec:
- for argname in self.spec.argnames:
- if argname not in kwargs:
- notincall = tuple(set(self.spec.argnames) - kwargs.keys())
- warnings.warn(
- "Argument(s) {} which are declared in the hookspec "
- "can not be found in this hook call".format(notincall),
- stacklevel=2,
- )
- break
-
- firstresult = self.spec.opts.get("firstresult")
- else:
- firstresult = False
-
- return self._hookexec(self.name, self.get_hookimpls(), kwargs, firstresult)
-
- def call_historic(self, result_callback=None, kwargs=None):
- """Call the hook with given ``kwargs`` for all registered plugins and
- for all plugins which will be registered afterwards.
-
- If ``result_callback`` is not ``None`` it will be called for for each
- non-``None`` result obtained from a hook implementation.
- """
- self._call_history.append((kwargs or {}, result_callback))
- # Historizing hooks don't return results.
- # Remember firstresult isn't compatible with historic.
- res = self._hookexec(self.name, self.get_hookimpls(), kwargs, False)
- if result_callback is None:
- return
- for x in res or []:
- result_callback(x)
-
- def call_extra(self, methods, kwargs):
- """Call the hook with some additional temporarily participating
- methods using the specified ``kwargs`` as call parameters."""
- old = list(self._nonwrappers), list(self._wrappers)
- for method in methods:
- opts = dict(hookwrapper=False, trylast=False, tryfirst=False)
- hookimpl = HookImpl(None, "<temp>", method, opts)
- self._add_hookimpl(hookimpl)
- try:
- return self(**kwargs)
- finally:
- self._nonwrappers, self._wrappers = old
-
- def _maybe_apply_history(self, method):
- """Apply call history to a new hookimpl if it is marked as historic."""
- if self.is_historic():
- for kwargs, result_callback in self._call_history:
- res = self._hookexec(self.name, [method], kwargs, False)
- if res and result_callback is not None:
- result_callback(res[0])
-
-
-class HookImpl:
- def __init__(self, plugin, plugin_name, function, hook_impl_opts):
- self.function = function
- self.argnames, self.kwargnames = varnames(self.function)
- self.plugin = plugin
- self.opts = hook_impl_opts
- self.plugin_name = plugin_name
- self.__dict__.update(hook_impl_opts)
-
- def __repr__(self):
- return f"<HookImpl plugin_name={self.plugin_name!r}, plugin={self.plugin!r}>"
-
-
-class HookSpec:
- def __init__(self, namespace, name, opts):
- self.namespace = namespace
- self.function = function = getattr(namespace, name)
- self.name = name
- self.argnames, self.kwargnames = varnames(function)
- self.opts = opts
- self.warn_on_impl = opts.get("warn_on_impl")
+"""
+Internal hook annotation, representation and calling machinery.
+"""
+import inspect
+import sys
+import warnings
+
+
+class HookspecMarker:
+ """Decorator helper class for marking functions as hook specifications.
+
+ You can instantiate it with a project_name to get a decorator.
+ Calling :py:meth:`.PluginManager.add_hookspecs` later will discover all marked functions
+ if the :py:class:`.PluginManager` uses the same project_name.
+ """
+
+ def __init__(self, project_name):
+ self.project_name = project_name
+
+ def __call__(
+ self, function=None, firstresult=False, historic=False, warn_on_impl=None
+ ):
+ """if passed a function, directly sets attributes on the function
+ which will make it discoverable to :py:meth:`.PluginManager.add_hookspecs`.
+ If passed no function, returns a decorator which can be applied to a function
+ later using the attributes supplied.
+
+ If ``firstresult`` is ``True`` the 1:N hook call (N being the number of registered
+ hook implementation functions) will stop at I<=N when the I'th function
+ returns a non-``None`` result.
+
+ If ``historic`` is ``True`` calls to a hook will be memorized and replayed
+ on later registered plugins.
+
+ """
+
+ def setattr_hookspec_opts(func):
+ if historic and firstresult:
+ raise ValueError("cannot have a historic firstresult hook")
+ setattr(
+ func,
+ self.project_name + "_spec",
+ dict(
+ firstresult=firstresult,
+ historic=historic,
+ warn_on_impl=warn_on_impl,
+ ),
+ )
+ return func
+
+ if function is not None:
+ return setattr_hookspec_opts(function)
+ else:
+ return setattr_hookspec_opts
+
+
+class HookimplMarker:
+ """Decorator helper class for marking functions as hook implementations.
+
+ You can instantiate with a ``project_name`` to get a decorator.
+ Calling :py:meth:`.PluginManager.register` later will discover all marked functions
+ if the :py:class:`.PluginManager` uses the same project_name.
+ """
+
+ def __init__(self, project_name):
+ self.project_name = project_name
+
+ def __call__(
+ self,
+ function=None,
+ hookwrapper=False,
+ optionalhook=False,
+ tryfirst=False,
+ trylast=False,
+ specname=None,
+ ):
+
+ """if passed a function, directly sets attributes on the function
+ which will make it discoverable to :py:meth:`.PluginManager.register`.
+ If passed no function, returns a decorator which can be applied to a
+ function later using the attributes supplied.
+
+ If ``optionalhook`` is ``True`` a missing matching hook specification will not result
+ in an error (by default it is an error if no matching spec is found).
+
+ If ``tryfirst`` is ``True`` this hook implementation will run as early as possible
+ in the chain of N hook implementations for a specification.
+
+ If ``trylast`` is ``True`` this hook implementation will run as late as possible
+ in the chain of N hook implementations.
+
+ If ``hookwrapper`` is ``True`` the hook implementations needs to execute exactly
+ one ``yield``. The code before the ``yield`` is run early before any non-hookwrapper
+ function is run. The code after the ``yield`` is run after all non-hookwrapper
+ function have run. The ``yield`` receives a :py:class:`.callers._Result` object
+ representing the exception or result outcome of the inner calls (including other
+ hookwrapper calls).
+
+ If ``specname`` is provided, it will be used instead of the function name when
+ matching this hook implementation to a hook specification during registration.
+
+ """
+
+ def setattr_hookimpl_opts(func):
+ setattr(
+ func,
+ self.project_name + "_impl",
+ dict(
+ hookwrapper=hookwrapper,
+ optionalhook=optionalhook,
+ tryfirst=tryfirst,
+ trylast=trylast,
+ specname=specname,
+ ),
+ )
+ return func
+
+ if function is None:
+ return setattr_hookimpl_opts
+ else:
+ return setattr_hookimpl_opts(function)
+
+
+def normalize_hookimpl_opts(opts):
+ opts.setdefault("tryfirst", False)
+ opts.setdefault("trylast", False)
+ opts.setdefault("hookwrapper", False)
+ opts.setdefault("optionalhook", False)
+ opts.setdefault("specname", None)
+
+
+_PYPY = hasattr(sys, "pypy_version_info")
+
+
+def varnames(func):
+ """Return tuple of positional and keywrord argument names for a function,
+ method, class or callable.
+
+ In case of a class, its ``__init__`` method is considered.
+ For methods the ``self`` parameter is not included.
+ """
+ if inspect.isclass(func):
+ try:
+ func = func.__init__
+ except AttributeError:
+ return (), ()
+ elif not inspect.isroutine(func): # callable object?
+ try:
+ func = getattr(func, "__call__", func)
+ except Exception:
+ return (), ()
+
+ try: # func MUST be a function or method here or we won't parse any args
+ spec = inspect.getfullargspec(func)
+ except TypeError:
+ return (), ()
+
+ args, defaults = tuple(spec.args), spec.defaults
+ if defaults:
+ index = -len(defaults)
+ args, kwargs = args[:index], tuple(args[index:])
+ else:
+ kwargs = ()
+
+ # strip any implicit instance arg
+ # pypy3 uses "obj" instead of "self" for default dunder methods
+ implicit_names = ("self",) if not _PYPY else ("self", "obj")
+ if args:
+ if inspect.ismethod(func) or (
+ "." in getattr(func, "__qualname__", ()) and args[0] in implicit_names
+ ):
+ args = args[1:]
+
+ return args, kwargs
+
+
+class _HookRelay:
+ """hook holder object for performing 1:N hook calls where N is the number
+ of registered plugins.
+
+ """
+
+
+class _HookCaller:
+ def __init__(self, name, hook_execute, specmodule_or_class=None, spec_opts=None):
+ self.name = name
+ self._wrappers = []
+ self._nonwrappers = []
+ self._hookexec = hook_execute
+ self._call_history = None
+ self.spec = None
+ if specmodule_or_class is not None:
+ assert spec_opts is not None
+ self.set_specification(specmodule_or_class, spec_opts)
+
+ def has_spec(self):
+ return self.spec is not None
+
+ def set_specification(self, specmodule_or_class, spec_opts):
+ assert not self.has_spec()
+ self.spec = HookSpec(specmodule_or_class, self.name, spec_opts)
+ if spec_opts.get("historic"):
+ self._call_history = []
+
+ def is_historic(self):
+ return self._call_history is not None
+
+ def _remove_plugin(self, plugin):
+ def remove(wrappers):
+ for i, method in enumerate(wrappers):
+ if method.plugin == plugin:
+ del wrappers[i]
+ return True
+
+ if remove(self._wrappers) is None:
+ if remove(self._nonwrappers) is None:
+ raise ValueError(f"plugin {plugin!r} not found")
+
+ def get_hookimpls(self):
+ # Order is important for _hookexec
+ return self._nonwrappers + self._wrappers
+
+ def _add_hookimpl(self, hookimpl):
+ """Add an implementation to the callback chain."""
+ if hookimpl.hookwrapper:
+ methods = self._wrappers
+ else:
+ methods = self._nonwrappers
+
+ if hookimpl.trylast:
+ methods.insert(0, hookimpl)
+ elif hookimpl.tryfirst:
+ methods.append(hookimpl)
+ else:
+ # find last non-tryfirst method
+ i = len(methods) - 1
+ while i >= 0 and methods[i].tryfirst:
+ i -= 1
+ methods.insert(i + 1, hookimpl)
+
+ def __repr__(self):
+ return f"<_HookCaller {self.name!r}>"
+
+ def __call__(self, *args, **kwargs):
+ if args:
+ raise TypeError("hook calling supports only keyword arguments")
+ assert not self.is_historic()
+
+ # This is written to avoid expensive operations when not needed.
+ if self.spec:
+ for argname in self.spec.argnames:
+ if argname not in kwargs:
+ notincall = tuple(set(self.spec.argnames) - kwargs.keys())
+ warnings.warn(
+ "Argument(s) {} which are declared in the hookspec "
+ "can not be found in this hook call".format(notincall),
+ stacklevel=2,
+ )
+ break
+
+ firstresult = self.spec.opts.get("firstresult")
+ else:
+ firstresult = False
+
+ return self._hookexec(self.name, self.get_hookimpls(), kwargs, firstresult)
+
+ def call_historic(self, result_callback=None, kwargs=None):
+ """Call the hook with given ``kwargs`` for all registered plugins and
+ for all plugins which will be registered afterwards.
+
+ If ``result_callback`` is not ``None`` it will be called for for each
+ non-``None`` result obtained from a hook implementation.
+ """
+ self._call_history.append((kwargs or {}, result_callback))
+ # Historizing hooks don't return results.
+ # Remember firstresult isn't compatible with historic.
+ res = self._hookexec(self.name, self.get_hookimpls(), kwargs, False)
+ if result_callback is None:
+ return
+ for x in res or []:
+ result_callback(x)
+
+ def call_extra(self, methods, kwargs):
+ """Call the hook with some additional temporarily participating
+ methods using the specified ``kwargs`` as call parameters."""
+ old = list(self._nonwrappers), list(self._wrappers)
+ for method in methods:
+ opts = dict(hookwrapper=False, trylast=False, tryfirst=False)
+ hookimpl = HookImpl(None, "<temp>", method, opts)
+ self._add_hookimpl(hookimpl)
+ try:
+ return self(**kwargs)
+ finally:
+ self._nonwrappers, self._wrappers = old
+
+ def _maybe_apply_history(self, method):
+ """Apply call history to a new hookimpl if it is marked as historic."""
+ if self.is_historic():
+ for kwargs, result_callback in self._call_history:
+ res = self._hookexec(self.name, [method], kwargs, False)
+ if res and result_callback is not None:
+ result_callback(res[0])
+
+
+class HookImpl:
+ def __init__(self, plugin, plugin_name, function, hook_impl_opts):
+ self.function = function
+ self.argnames, self.kwargnames = varnames(self.function)
+ self.plugin = plugin
+ self.opts = hook_impl_opts
+ self.plugin_name = plugin_name
+ self.__dict__.update(hook_impl_opts)
+
+ def __repr__(self):
+ return f"<HookImpl plugin_name={self.plugin_name!r}, plugin={self.plugin!r}>"
+
+
+class HookSpec:
+ def __init__(self, namespace, name, opts):
+ self.namespace = namespace
+ self.function = function = getattr(namespace, name)
+ self.name = name
+ self.argnames, self.kwargnames = varnames(function)
+ self.opts = opts
+ self.warn_on_impl = opts.get("warn_on_impl")
diff --git a/contrib/python/pluggy/py3/pluggy/_manager.py b/contrib/python/pluggy/py3/pluggy/_manager.py
index 65f4e50842..1d5cefcadc 100644
--- a/contrib/python/pluggy/py3/pluggy/_manager.py
+++ b/contrib/python/pluggy/py3/pluggy/_manager.py
@@ -1,373 +1,373 @@
-import inspect
-import sys
-import warnings
-
-from . import _tracing
-from ._callers import _Result, _multicall
-from ._hooks import HookImpl, _HookRelay, _HookCaller, normalize_hookimpl_opts
-
-if sys.version_info >= (3, 8):
- from importlib import metadata as importlib_metadata
-else:
- import importlib_metadata
-
-
-def _warn_for_function(warning, function):
- warnings.warn_explicit(
- warning,
- type(warning),
- lineno=function.__code__.co_firstlineno,
- filename=function.__code__.co_filename,
- )
-
-
-class PluginValidationError(Exception):
- """plugin failed validation.
-
- :param object plugin: the plugin which failed validation,
- may be a module or an arbitrary object.
- """
-
- def __init__(self, plugin, message):
- self.plugin = plugin
- super(Exception, self).__init__(message)
-
-
-class DistFacade:
- """Emulate a pkg_resources Distribution"""
-
- def __init__(self, dist):
- self._dist = dist
-
- @property
- def project_name(self):
- return self.metadata["name"]
-
- def __getattr__(self, attr, default=None):
- return getattr(self._dist, attr, default)
-
- def __dir__(self):
- return sorted(dir(self._dist) + ["_dist", "project_name"])
-
-
-class PluginManager:
- """Core :py:class:`.PluginManager` class which manages registration
- of plugin objects and 1:N hook calling.
-
- You can register new hooks by calling :py:meth:`add_hookspecs(module_or_class)
- <.PluginManager.add_hookspecs>`.
- You can register plugin objects (which contain hooks) by calling
- :py:meth:`register(plugin) <.PluginManager.register>`. The :py:class:`.PluginManager`
- is initialized with a prefix that is searched for in the names of the dict
- of registered plugin objects.
-
- For debugging purposes you can call :py:meth:`.PluginManager.enable_tracing`
- which will subsequently send debug information to the trace helper.
- """
-
- def __init__(self, project_name):
- self.project_name = project_name
- self._name2plugin = {}
- self._plugin2hookcallers = {}
- self._plugin_distinfo = []
- self.trace = _tracing.TagTracer().get("pluginmanage")
- self.hook = _HookRelay()
- self._inner_hookexec = _multicall
-
- def _hookexec(self, hook_name, methods, kwargs, firstresult):
- # called from all hookcaller instances.
- # enable_tracing will set its own wrapping function at self._inner_hookexec
- return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
-
- def register(self, plugin, name=None):
- """Register a plugin and return its canonical name or ``None`` if the name
- is blocked from registering. Raise a :py:class:`ValueError` if the plugin
- is already registered."""
- plugin_name = name or self.get_canonical_name(plugin)
-
- if plugin_name in self._name2plugin or plugin in self._plugin2hookcallers:
- if self._name2plugin.get(plugin_name, -1) is None:
- return # blocked plugin, return None to indicate no registration
- raise ValueError(
- "Plugin already registered: %s=%s\n%s"
- % (plugin_name, plugin, self._name2plugin)
- )
-
- # XXX if an error happens we should make sure no state has been
- # changed at point of return
- self._name2plugin[plugin_name] = plugin
-
- # register matching hook implementations of the plugin
- self._plugin2hookcallers[plugin] = hookcallers = []
- for name in dir(plugin):
- hookimpl_opts = self.parse_hookimpl_opts(plugin, name)
- if hookimpl_opts is not None:
- normalize_hookimpl_opts(hookimpl_opts)
- method = getattr(plugin, name)
- hookimpl = HookImpl(plugin, plugin_name, method, hookimpl_opts)
- name = hookimpl_opts.get("specname") or name
- hook = getattr(self.hook, name, None)
- if hook is None:
- hook = _HookCaller(name, self._hookexec)
- setattr(self.hook, name, hook)
- elif hook.has_spec():
- self._verify_hook(hook, hookimpl)
- hook._maybe_apply_history(hookimpl)
- hook._add_hookimpl(hookimpl)
- hookcallers.append(hook)
- return plugin_name
-
- def parse_hookimpl_opts(self, plugin, name):
- method = getattr(plugin, name)
- if not inspect.isroutine(method):
- return
- try:
- res = getattr(method, self.project_name + "_impl", None)
- except Exception:
- res = {}
- if res is not None and not isinstance(res, dict):
- # false positive
- res = None
- return res
-
- def unregister(self, plugin=None, name=None):
- """unregister a plugin object and all its contained hook implementations
- from internal data structures."""
- if name is None:
- assert plugin is not None, "one of name or plugin needs to be specified"
- name = self.get_name(plugin)
-
- if plugin is None:
- plugin = self.get_plugin(name)
-
- # if self._name2plugin[name] == None registration was blocked: ignore
- if self._name2plugin.get(name):
- del self._name2plugin[name]
-
- for hookcaller in self._plugin2hookcallers.pop(plugin, []):
- hookcaller._remove_plugin(plugin)
-
- return plugin
-
- def set_blocked(self, name):
- """block registrations of the given name, unregister if already registered."""
- self.unregister(name=name)
- self._name2plugin[name] = None
-
- def is_blocked(self, name):
- """return ``True`` if the given plugin name is blocked."""
- return name in self._name2plugin and self._name2plugin[name] is None
-
- def add_hookspecs(self, module_or_class):
- """add new hook specifications defined in the given ``module_or_class``.
- Functions are recognized if they have been decorated accordingly."""
- names = []
- for name in dir(module_or_class):
- spec_opts = self.parse_hookspec_opts(module_or_class, name)
- if spec_opts is not None:
- hc = getattr(self.hook, name, None)
- if hc is None:
- hc = _HookCaller(name, self._hookexec, module_or_class, spec_opts)
- setattr(self.hook, name, hc)
- else:
- # plugins registered this hook without knowing the spec
- hc.set_specification(module_or_class, spec_opts)
- for hookfunction in hc.get_hookimpls():
- self._verify_hook(hc, hookfunction)
- names.append(name)
-
- if not names:
- raise ValueError(
- f"did not find any {self.project_name!r} hooks in {module_or_class!r}"
- )
-
- def parse_hookspec_opts(self, module_or_class, name):
- method = getattr(module_or_class, name)
- return getattr(method, self.project_name + "_spec", None)
-
- def get_plugins(self):
- """return the set of registered plugins."""
- return set(self._plugin2hookcallers)
-
- def is_registered(self, plugin):
- """Return ``True`` if the plugin is already registered."""
- return plugin in self._plugin2hookcallers
-
- def get_canonical_name(self, plugin):
- """Return canonical name for a plugin object. Note that a plugin
- may be registered under a different name which was specified
- by the caller of :py:meth:`register(plugin, name) <.PluginManager.register>`.
- To obtain the name of an registered plugin use :py:meth:`get_name(plugin)
- <.PluginManager.get_name>` instead."""
- return getattr(plugin, "__name__", None) or str(id(plugin))
-
- def get_plugin(self, name):
- """Return a plugin or ``None`` for the given name."""
- return self._name2plugin.get(name)
-
- def has_plugin(self, name):
- """Return ``True`` if a plugin with the given name is registered."""
- return self.get_plugin(name) is not None
-
- def get_name(self, plugin):
- """Return name for registered plugin or ``None`` if not registered."""
- for name, val in self._name2plugin.items():
- if plugin == val:
- return name
-
- def _verify_hook(self, hook, hookimpl):
- if hook.is_historic() and hookimpl.hookwrapper:
- raise PluginValidationError(
- hookimpl.plugin,
- "Plugin %r\nhook %r\nhistoric incompatible to hookwrapper"
- % (hookimpl.plugin_name, hook.name),
- )
-
- if hook.spec.warn_on_impl:
- _warn_for_function(hook.spec.warn_on_impl, hookimpl.function)
-
- # positional arg checking
- notinspec = set(hookimpl.argnames) - set(hook.spec.argnames)
- if notinspec:
- raise PluginValidationError(
- hookimpl.plugin,
- "Plugin %r for hook %r\nhookimpl definition: %s\n"
- "Argument(s) %s are declared in the hookimpl but "
- "can not be found in the hookspec"
- % (
- hookimpl.plugin_name,
- hook.name,
- _formatdef(hookimpl.function),
- notinspec,
- ),
- )
-
- if hookimpl.hookwrapper and not inspect.isgeneratorfunction(hookimpl.function):
- raise PluginValidationError(
- hookimpl.plugin,
- "Plugin %r for hook %r\nhookimpl definition: %s\n"
- "Declared as hookwrapper=True but function is not a generator function"
- % (hookimpl.plugin_name, hook.name, _formatdef(hookimpl.function)),
- )
-
- def check_pending(self):
- """Verify that all hooks which have not been verified against
- a hook specification are optional, otherwise raise :py:class:`.PluginValidationError`."""
- for name in self.hook.__dict__:
- if name[0] != "_":
- hook = getattr(self.hook, name)
- if not hook.has_spec():
- for hookimpl in hook.get_hookimpls():
- if not hookimpl.optionalhook:
- raise PluginValidationError(
- hookimpl.plugin,
- "unknown hook %r in plugin %r"
- % (name, hookimpl.plugin),
- )
-
- def load_setuptools_entrypoints(self, group, name=None):
- """Load modules from querying the specified setuptools ``group``.
-
- :param str group: entry point group to load plugins
- :param str name: if given, loads only plugins with the given ``name``.
- :rtype: int
- :return: return the number of loaded plugins by this call.
- """
- count = 0
- for dist in list(importlib_metadata.distributions()):
- for ep in dist.entry_points:
- if (
- ep.group != group
- or (name is not None and ep.name != name)
- # already registered
- or self.get_plugin(ep.name)
- or self.is_blocked(ep.name)
- ):
- continue
- plugin = ep.load()
- self.register(plugin, name=ep.name)
- self._plugin_distinfo.append((plugin, DistFacade(dist)))
- count += 1
- return count
-
- def list_plugin_distinfo(self):
- """return list of distinfo/plugin tuples for all setuptools registered
- plugins."""
- return list(self._plugin_distinfo)
-
- def list_name_plugin(self):
- """return list of name/plugin pairs."""
- return list(self._name2plugin.items())
-
- def get_hookcallers(self, plugin):
- """get all hook callers for the specified plugin."""
- return self._plugin2hookcallers.get(plugin)
-
- def add_hookcall_monitoring(self, before, after):
- """add before/after tracing functions for all hooks
- and return an undo function which, when called,
- will remove the added tracers.
-
- ``before(hook_name, hook_impls, kwargs)`` will be called ahead
- of all hook calls and receive a hookcaller instance, a list
- of HookImpl instances and the keyword arguments for the hook call.
-
- ``after(outcome, hook_name, hook_impls, kwargs)`` receives the
- same arguments as ``before`` but also a :py:class:`pluggy._callers._Result` object
- which represents the result of the overall hook call.
- """
- oldcall = self._inner_hookexec
-
- def traced_hookexec(hook_name, hook_impls, kwargs, firstresult):
- before(hook_name, hook_impls, kwargs)
- outcome = _Result.from_call(
- lambda: oldcall(hook_name, hook_impls, kwargs, firstresult)
- )
- after(outcome, hook_name, hook_impls, kwargs)
- return outcome.get_result()
-
- self._inner_hookexec = traced_hookexec
-
- def undo():
- self._inner_hookexec = oldcall
-
- return undo
-
- def enable_tracing(self):
- """enable tracing of hook calls and return an undo function."""
- hooktrace = self.trace.root.get("hook")
-
- def before(hook_name, methods, kwargs):
- hooktrace.root.indent += 1
- hooktrace(hook_name, kwargs)
-
- def after(outcome, hook_name, methods, kwargs):
- if outcome.excinfo is None:
- hooktrace("finish", hook_name, "-->", outcome.get_result())
- hooktrace.root.indent -= 1
-
- return self.add_hookcall_monitoring(before, after)
-
- def subset_hook_caller(self, name, remove_plugins):
- """Return a new :py:class:`._hooks._HookCaller` instance for the named method
- which manages calls to all registered plugins except the
- ones from remove_plugins."""
- orig = getattr(self.hook, name)
- plugins_to_remove = [plug for plug in remove_plugins if hasattr(plug, name)]
- if plugins_to_remove:
- hc = _HookCaller(
- orig.name, orig._hookexec, orig.spec.namespace, orig.spec.opts
- )
- for hookimpl in orig.get_hookimpls():
- plugin = hookimpl.plugin
- if plugin not in plugins_to_remove:
- hc._add_hookimpl(hookimpl)
- # we also keep track of this hook caller so it
- # gets properly removed on plugin unregistration
- self._plugin2hookcallers.setdefault(plugin, []).append(hc)
- return hc
- return orig
-
-
-def _formatdef(func):
- return f"{func.__name__}{inspect.signature(func)}"
+import inspect
+import sys
+import warnings
+
+from . import _tracing
+from ._callers import _Result, _multicall
+from ._hooks import HookImpl, _HookRelay, _HookCaller, normalize_hookimpl_opts
+
+if sys.version_info >= (3, 8):
+ from importlib import metadata as importlib_metadata
+else:
+ import importlib_metadata
+
+
+def _warn_for_function(warning, function):
+ warnings.warn_explicit(
+ warning,
+ type(warning),
+ lineno=function.__code__.co_firstlineno,
+ filename=function.__code__.co_filename,
+ )
+
+
+class PluginValidationError(Exception):
+ """plugin failed validation.
+
+ :param object plugin: the plugin which failed validation,
+ may be a module or an arbitrary object.
+ """
+
+ def __init__(self, plugin, message):
+ self.plugin = plugin
+ super(Exception, self).__init__(message)
+
+
+class DistFacade:
+ """Emulate a pkg_resources Distribution"""
+
+ def __init__(self, dist):
+ self._dist = dist
+
+ @property
+ def project_name(self):
+ return self.metadata["name"]
+
+ def __getattr__(self, attr, default=None):
+ return getattr(self._dist, attr, default)
+
+ def __dir__(self):
+ return sorted(dir(self._dist) + ["_dist", "project_name"])
+
+
+class PluginManager:
+ """Core :py:class:`.PluginManager` class which manages registration
+ of plugin objects and 1:N hook calling.
+
+ You can register new hooks by calling :py:meth:`add_hookspecs(module_or_class)
+ <.PluginManager.add_hookspecs>`.
+ You can register plugin objects (which contain hooks) by calling
+ :py:meth:`register(plugin) <.PluginManager.register>`. The :py:class:`.PluginManager`
+ is initialized with a prefix that is searched for in the names of the dict
+ of registered plugin objects.
+
+ For debugging purposes you can call :py:meth:`.PluginManager.enable_tracing`
+ which will subsequently send debug information to the trace helper.
+ """
+
+ def __init__(self, project_name):
+ self.project_name = project_name
+ self._name2plugin = {}
+ self._plugin2hookcallers = {}
+ self._plugin_distinfo = []
+ self.trace = _tracing.TagTracer().get("pluginmanage")
+ self.hook = _HookRelay()
+ self._inner_hookexec = _multicall
+
+ def _hookexec(self, hook_name, methods, kwargs, firstresult):
+ # called from all hookcaller instances.
+ # enable_tracing will set its own wrapping function at self._inner_hookexec
+ return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
+
+ def register(self, plugin, name=None):
+ """Register a plugin and return its canonical name or ``None`` if the name
+ is blocked from registering. Raise a :py:class:`ValueError` if the plugin
+ is already registered."""
+ plugin_name = name or self.get_canonical_name(plugin)
+
+ if plugin_name in self._name2plugin or plugin in self._plugin2hookcallers:
+ if self._name2plugin.get(plugin_name, -1) is None:
+ return # blocked plugin, return None to indicate no registration
+ raise ValueError(
+ "Plugin already registered: %s=%s\n%s"
+ % (plugin_name, plugin, self._name2plugin)
+ )
+
+ # XXX if an error happens we should make sure no state has been
+ # changed at point of return
+ self._name2plugin[plugin_name] = plugin
+
+ # register matching hook implementations of the plugin
+ self._plugin2hookcallers[plugin] = hookcallers = []
+ for name in dir(plugin):
+ hookimpl_opts = self.parse_hookimpl_opts(plugin, name)
+ if hookimpl_opts is not None:
+ normalize_hookimpl_opts(hookimpl_opts)
+ method = getattr(plugin, name)
+ hookimpl = HookImpl(plugin, plugin_name, method, hookimpl_opts)
+ name = hookimpl_opts.get("specname") or name
+ hook = getattr(self.hook, name, None)
+ if hook is None:
+ hook = _HookCaller(name, self._hookexec)
+ setattr(self.hook, name, hook)
+ elif hook.has_spec():
+ self._verify_hook(hook, hookimpl)
+ hook._maybe_apply_history(hookimpl)
+ hook._add_hookimpl(hookimpl)
+ hookcallers.append(hook)
+ return plugin_name
+
+ def parse_hookimpl_opts(self, plugin, name):
+ method = getattr(plugin, name)
+ if not inspect.isroutine(method):
+ return
+ try:
+ res = getattr(method, self.project_name + "_impl", None)
+ except Exception:
+ res = {}
+ if res is not None and not isinstance(res, dict):
+ # false positive
+ res = None
+ return res
+
+ def unregister(self, plugin=None, name=None):
+ """unregister a plugin object and all its contained hook implementations
+ from internal data structures."""
+ if name is None:
+ assert plugin is not None, "one of name or plugin needs to be specified"
+ name = self.get_name(plugin)
+
+ if plugin is None:
+ plugin = self.get_plugin(name)
+
+ # if self._name2plugin[name] == None registration was blocked: ignore
+ if self._name2plugin.get(name):
+ del self._name2plugin[name]
+
+ for hookcaller in self._plugin2hookcallers.pop(plugin, []):
+ hookcaller._remove_plugin(plugin)
+
+ return plugin
+
+ def set_blocked(self, name):
+ """block registrations of the given name, unregister if already registered."""
+ self.unregister(name=name)
+ self._name2plugin[name] = None
+
+ def is_blocked(self, name):
+ """return ``True`` if the given plugin name is blocked."""
+ return name in self._name2plugin and self._name2plugin[name] is None
+
+ def add_hookspecs(self, module_or_class):
+ """add new hook specifications defined in the given ``module_or_class``.
+ Functions are recognized if they have been decorated accordingly."""
+ names = []
+ for name in dir(module_or_class):
+ spec_opts = self.parse_hookspec_opts(module_or_class, name)
+ if spec_opts is not None:
+ hc = getattr(self.hook, name, None)
+ if hc is None:
+ hc = _HookCaller(name, self._hookexec, module_or_class, spec_opts)
+ setattr(self.hook, name, hc)
+ else:
+ # plugins registered this hook without knowing the spec
+ hc.set_specification(module_or_class, spec_opts)
+ for hookfunction in hc.get_hookimpls():
+ self._verify_hook(hc, hookfunction)
+ names.append(name)
+
+ if not names:
+ raise ValueError(
+ f"did not find any {self.project_name!r} hooks in {module_or_class!r}"
+ )
+
+ def parse_hookspec_opts(self, module_or_class, name):
+ method = getattr(module_or_class, name)
+ return getattr(method, self.project_name + "_spec", None)
+
+ def get_plugins(self):
+ """return the set of registered plugins."""
+ return set(self._plugin2hookcallers)
+
+ def is_registered(self, plugin):
+ """Return ``True`` if the plugin is already registered."""
+ return plugin in self._plugin2hookcallers
+
+ def get_canonical_name(self, plugin):
+ """Return canonical name for a plugin object. Note that a plugin
+ may be registered under a different name which was specified
+ by the caller of :py:meth:`register(plugin, name) <.PluginManager.register>`.
+ To obtain the name of an registered plugin use :py:meth:`get_name(plugin)
+ <.PluginManager.get_name>` instead."""
+ return getattr(plugin, "__name__", None) or str(id(plugin))
+
+ def get_plugin(self, name):
+ """Return a plugin or ``None`` for the given name."""
+ return self._name2plugin.get(name)
+
+ def has_plugin(self, name):
+ """Return ``True`` if a plugin with the given name is registered."""
+ return self.get_plugin(name) is not None
+
+ def get_name(self, plugin):
+ """Return name for registered plugin or ``None`` if not registered."""
+ for name, val in self._name2plugin.items():
+ if plugin == val:
+ return name
+
+ def _verify_hook(self, hook, hookimpl):
+ if hook.is_historic() and hookimpl.hookwrapper:
+ raise PluginValidationError(
+ hookimpl.plugin,
+ "Plugin %r\nhook %r\nhistoric incompatible to hookwrapper"
+ % (hookimpl.plugin_name, hook.name),
+ )
+
+ if hook.spec.warn_on_impl:
+ _warn_for_function(hook.spec.warn_on_impl, hookimpl.function)
+
+ # positional arg checking
+ notinspec = set(hookimpl.argnames) - set(hook.spec.argnames)
+ if notinspec:
+ raise PluginValidationError(
+ hookimpl.plugin,
+ "Plugin %r for hook %r\nhookimpl definition: %s\n"
+ "Argument(s) %s are declared in the hookimpl but "
+ "can not be found in the hookspec"
+ % (
+ hookimpl.plugin_name,
+ hook.name,
+ _formatdef(hookimpl.function),
+ notinspec,
+ ),
+ )
+
+ if hookimpl.hookwrapper and not inspect.isgeneratorfunction(hookimpl.function):
+ raise PluginValidationError(
+ hookimpl.plugin,
+ "Plugin %r for hook %r\nhookimpl definition: %s\n"
+ "Declared as hookwrapper=True but function is not a generator function"
+ % (hookimpl.plugin_name, hook.name, _formatdef(hookimpl.function)),
+ )
+
+ def check_pending(self):
+ """Verify that all hooks which have not been verified against
+ a hook specification are optional, otherwise raise :py:class:`.PluginValidationError`."""
+ for name in self.hook.__dict__:
+ if name[0] != "_":
+ hook = getattr(self.hook, name)
+ if not hook.has_spec():
+ for hookimpl in hook.get_hookimpls():
+ if not hookimpl.optionalhook:
+ raise PluginValidationError(
+ hookimpl.plugin,
+ "unknown hook %r in plugin %r"
+ % (name, hookimpl.plugin),
+ )
+
+ def load_setuptools_entrypoints(self, group, name=None):
+ """Load modules from querying the specified setuptools ``group``.
+
+ :param str group: entry point group to load plugins
+ :param str name: if given, loads only plugins with the given ``name``.
+ :rtype: int
+ :return: return the number of loaded plugins by this call.
+ """
+ count = 0
+ for dist in list(importlib_metadata.distributions()):
+ for ep in dist.entry_points:
+ if (
+ ep.group != group
+ or (name is not None and ep.name != name)
+ # already registered
+ or self.get_plugin(ep.name)
+ or self.is_blocked(ep.name)
+ ):
+ continue
+ plugin = ep.load()
+ self.register(plugin, name=ep.name)
+ self._plugin_distinfo.append((plugin, DistFacade(dist)))
+ count += 1
+ return count
+
+ def list_plugin_distinfo(self):
+ """return list of distinfo/plugin tuples for all setuptools registered
+ plugins."""
+ return list(self._plugin_distinfo)
+
+ def list_name_plugin(self):
+ """return list of name/plugin pairs."""
+ return list(self._name2plugin.items())
+
+ def get_hookcallers(self, plugin):
+ """get all hook callers for the specified plugin."""
+ return self._plugin2hookcallers.get(plugin)
+
+ def add_hookcall_monitoring(self, before, after):
+ """add before/after tracing functions for all hooks
+ and return an undo function which, when called,
+ will remove the added tracers.
+
+ ``before(hook_name, hook_impls, kwargs)`` will be called ahead
+ of all hook calls and receive a hookcaller instance, a list
+ of HookImpl instances and the keyword arguments for the hook call.
+
+ ``after(outcome, hook_name, hook_impls, kwargs)`` receives the
+ same arguments as ``before`` but also a :py:class:`pluggy._callers._Result` object
+ which represents the result of the overall hook call.
+ """
+ oldcall = self._inner_hookexec
+
+ def traced_hookexec(hook_name, hook_impls, kwargs, firstresult):
+ before(hook_name, hook_impls, kwargs)
+ outcome = _Result.from_call(
+ lambda: oldcall(hook_name, hook_impls, kwargs, firstresult)
+ )
+ after(outcome, hook_name, hook_impls, kwargs)
+ return outcome.get_result()
+
+ self._inner_hookexec = traced_hookexec
+
+ def undo():
+ self._inner_hookexec = oldcall
+
+ return undo
+
+ def enable_tracing(self):
+ """enable tracing of hook calls and return an undo function."""
+ hooktrace = self.trace.root.get("hook")
+
+ def before(hook_name, methods, kwargs):
+ hooktrace.root.indent += 1
+ hooktrace(hook_name, kwargs)
+
+ def after(outcome, hook_name, methods, kwargs):
+ if outcome.excinfo is None:
+ hooktrace("finish", hook_name, "-->", outcome.get_result())
+ hooktrace.root.indent -= 1
+
+ return self.add_hookcall_monitoring(before, after)
+
+ def subset_hook_caller(self, name, remove_plugins):
+ """Return a new :py:class:`._hooks._HookCaller` instance for the named method
+ which manages calls to all registered plugins except the
+ ones from remove_plugins."""
+ orig = getattr(self.hook, name)
+ plugins_to_remove = [plug for plug in remove_plugins if hasattr(plug, name)]
+ if plugins_to_remove:
+ hc = _HookCaller(
+ orig.name, orig._hookexec, orig.spec.namespace, orig.spec.opts
+ )
+ for hookimpl in orig.get_hookimpls():
+ plugin = hookimpl.plugin
+ if plugin not in plugins_to_remove:
+ hc._add_hookimpl(hookimpl)
+ # we also keep track of this hook caller so it
+ # gets properly removed on plugin unregistration
+ self._plugin2hookcallers.setdefault(plugin, []).append(hc)
+ return hc
+ return orig
+
+
+def _formatdef(func):
+ return f"{func.__name__}{inspect.signature(func)}"
diff --git a/contrib/python/pluggy/py3/pluggy/_result.py b/contrib/python/pluggy/py3/pluggy/_result.py
index 4c1f7f1f3c..b8a8520772 100644
--- a/contrib/python/pluggy/py3/pluggy/_result.py
+++ b/contrib/python/pluggy/py3/pluggy/_result.py
@@ -1,60 +1,60 @@
-"""
-Hook wrapper "result" utilities.
-"""
-import sys
-
-
-def _raise_wrapfail(wrap_controller, msg):
- co = wrap_controller.gi_code
- raise RuntimeError(
- "wrap_controller at %r %s:%d %s"
- % (co.co_name, co.co_filename, co.co_firstlineno, msg)
- )
-
-
-class HookCallError(Exception):
- """Hook was called wrongly."""
-
-
-class _Result:
- def __init__(self, result, excinfo):
- self._result = result
- self._excinfo = excinfo
-
- @property
- def excinfo(self):
- return self._excinfo
-
- @classmethod
- def from_call(cls, func):
- __tracebackhide__ = True
- result = excinfo = None
- try:
- result = func()
- except BaseException:
- excinfo = sys.exc_info()
-
- return cls(result, excinfo)
-
- def force_result(self, result):
- """Force the result(s) to ``result``.
-
- If the hook was marked as a ``firstresult`` a single value should
- be set otherwise set a (modified) list of results. Any exceptions
- found during invocation will be deleted.
- """
- self._result = result
- self._excinfo = None
-
- def get_result(self):
- """Get the result(s) for this hook call.
-
- If the hook was marked as a ``firstresult`` only a single value
- will be returned otherwise a list of results.
- """
- __tracebackhide__ = True
- if self._excinfo is None:
- return self._result
- else:
- ex = self._excinfo
- raise ex[1].with_traceback(ex[2])
+"""
+Hook wrapper "result" utilities.
+"""
+import sys
+
+
+def _raise_wrapfail(wrap_controller, msg):
+ co = wrap_controller.gi_code
+ raise RuntimeError(
+ "wrap_controller at %r %s:%d %s"
+ % (co.co_name, co.co_filename, co.co_firstlineno, msg)
+ )
+
+
+class HookCallError(Exception):
+ """Hook was called wrongly."""
+
+
+class _Result:
+ def __init__(self, result, excinfo):
+ self._result = result
+ self._excinfo = excinfo
+
+ @property
+ def excinfo(self):
+ return self._excinfo
+
+ @classmethod
+ def from_call(cls, func):
+ __tracebackhide__ = True
+ result = excinfo = None
+ try:
+ result = func()
+ except BaseException:
+ excinfo = sys.exc_info()
+
+ return cls(result, excinfo)
+
+ def force_result(self, result):
+ """Force the result(s) to ``result``.
+
+ If the hook was marked as a ``firstresult`` a single value should
+ be set otherwise set a (modified) list of results. Any exceptions
+ found during invocation will be deleted.
+ """
+ self._result = result
+ self._excinfo = None
+
+ def get_result(self):
+ """Get the result(s) for this hook call.
+
+ If the hook was marked as a ``firstresult`` only a single value
+ will be returned otherwise a list of results.
+ """
+ __tracebackhide__ = True
+ if self._excinfo is None:
+ return self._result
+ else:
+ ex = self._excinfo
+ raise ex[1].with_traceback(ex[2])
diff --git a/contrib/python/pluggy/py3/pluggy/_tracing.py b/contrib/python/pluggy/py3/pluggy/_tracing.py
index 82c016271e..0e0d3d7175 100644
--- a/contrib/python/pluggy/py3/pluggy/_tracing.py
+++ b/contrib/python/pluggy/py3/pluggy/_tracing.py
@@ -1,62 +1,62 @@
-"""
-Tracing utils
-"""
-
-
-class TagTracer:
- def __init__(self):
- self._tags2proc = {}
- self._writer = None
- self.indent = 0
-
- def get(self, name):
- return TagTracerSub(self, (name,))
-
- def _format_message(self, tags, args):
- if isinstance(args[-1], dict):
- extra = args[-1]
- args = args[:-1]
- else:
- extra = {}
-
- content = " ".join(map(str, args))
- indent = " " * self.indent
-
- lines = ["{}{} [{}]\n".format(indent, content, ":".join(tags))]
-
- for name, value in extra.items():
- lines.append(f"{indent} {name}: {value}\n")
-
- return "".join(lines)
-
- def _processmessage(self, tags, args):
- if self._writer is not None and args:
- self._writer(self._format_message(tags, args))
- try:
- processor = self._tags2proc[tags]
- except KeyError:
- pass
- else:
- processor(tags, args)
-
- def setwriter(self, writer):
- self._writer = writer
-
- def setprocessor(self, tags, processor):
- if isinstance(tags, str):
- tags = tuple(tags.split(":"))
- else:
- assert isinstance(tags, tuple)
- self._tags2proc[tags] = processor
-
-
-class TagTracerSub:
- def __init__(self, root, tags):
- self.root = root
- self.tags = tags
-
- def __call__(self, *args):
- self.root._processmessage(self.tags, args)
-
- def get(self, name):
- return self.__class__(self.root, self.tags + (name,))
+"""
+Tracing utils
+"""
+
+
+class TagTracer:
+ def __init__(self):
+ self._tags2proc = {}
+ self._writer = None
+ self.indent = 0
+
+ def get(self, name):
+ return TagTracerSub(self, (name,))
+
+ def _format_message(self, tags, args):
+ if isinstance(args[-1], dict):
+ extra = args[-1]
+ args = args[:-1]
+ else:
+ extra = {}
+
+ content = " ".join(map(str, args))
+ indent = " " * self.indent
+
+ lines = ["{}{} [{}]\n".format(indent, content, ":".join(tags))]
+
+ for name, value in extra.items():
+ lines.append(f"{indent} {name}: {value}\n")
+
+ return "".join(lines)
+
+ def _processmessage(self, tags, args):
+ if self._writer is not None and args:
+ self._writer(self._format_message(tags, args))
+ try:
+ processor = self._tags2proc[tags]
+ except KeyError:
+ pass
+ else:
+ processor(tags, args)
+
+ def setwriter(self, writer):
+ self._writer = writer
+
+ def setprocessor(self, tags, processor):
+ if isinstance(tags, str):
+ tags = tuple(tags.split(":"))
+ else:
+ assert isinstance(tags, tuple)
+ self._tags2proc[tags] = processor
+
+
+class TagTracerSub:
+ def __init__(self, root, tags):
+ self.root = root
+ self.tags = tags
+
+ def __call__(self, *args):
+ self.root._processmessage(self.tags, args)
+
+ def get(self, name):
+ return self.__class__(self.root, self.tags + (name,))
diff --git a/contrib/python/pluggy/py3/pluggy/_version.py b/contrib/python/pluggy/py3/pluggy/_version.py
index 7f0e082d0d..89929710b2 100644
--- a/contrib/python/pluggy/py3/pluggy/_version.py
+++ b/contrib/python/pluggy/py3/pluggy/_version.py
@@ -1,5 +1,5 @@
-# coding: utf-8
-# file generated by setuptools_scm
-# don't change, don't track in version control
-version = '1.0.0'
-version_tuple = (1, 0, 0)
+# coding: utf-8
+# file generated by setuptools_scm
+# don't change, don't track in version control
+version = '1.0.0'
+version_tuple = (1, 0, 0)
diff --git a/contrib/python/pluggy/py3/tests/benchmark.py b/contrib/python/pluggy/py3/tests/benchmark.py
index b0d4b9536a..ed7a18720d 100644
--- a/contrib/python/pluggy/py3/tests/benchmark.py
+++ b/contrib/python/pluggy/py3/tests/benchmark.py
@@ -1,102 +1,102 @@
-"""
-Benchmarking and performance tests.
-"""
-import pytest
-from pluggy import HookspecMarker, HookimplMarker, PluginManager
-from pluggy._hooks import HookImpl
-from pluggy._callers import _multicall
-
-
-hookspec = HookspecMarker("example")
-hookimpl = HookimplMarker("example")
-
-
-@hookimpl
-def hook(arg1, arg2, arg3):
- return arg1, arg2, arg3
-
-
-@hookimpl(hookwrapper=True)
-def wrapper(arg1, arg2, arg3):
- yield
-
-
-@pytest.fixture(params=[10, 100], ids="hooks={}".format)
-def hooks(request):
- return [hook for i in range(request.param)]
-
-
-@pytest.fixture(params=[10, 100], ids="wrappers={}".format)
-def wrappers(request):
- return [wrapper for i in range(request.param)]
-
-
-def test_hook_and_wrappers_speed(benchmark, hooks, wrappers):
- def setup():
- hook_name = "foo"
- hook_impls = []
- for method in hooks + wrappers:
- f = HookImpl(None, "<temp>", method, method.example_impl)
- hook_impls.append(f)
- caller_kwargs = {"arg1": 1, "arg2": 2, "arg3": 3}
- firstresult = False
- return (hook_name, hook_impls, caller_kwargs, firstresult), {}
-
- benchmark.pedantic(_multicall, setup=setup)
-
-
-@pytest.mark.parametrize(
- ("plugins, wrappers, nesting"),
- [
- (1, 1, 0),
- (1, 1, 1),
- (1, 1, 5),
- (1, 5, 1),
- (1, 5, 5),
- (5, 1, 1),
- (5, 1, 5),
- (5, 5, 1),
- (5, 5, 5),
- (20, 20, 0),
- (100, 100, 0),
- ],
-)
-def test_call_hook(benchmark, plugins, wrappers, nesting):
- pm = PluginManager("example")
-
- class HookSpec:
- @hookspec
- def fun(self, hooks, nesting: int):
- yield
-
- class Plugin:
- def __init__(self, num):
- self.num = num
-
- def __repr__(self):
- return f"<Plugin {self.num}>"
-
- @hookimpl
- def fun(self, hooks, nesting: int):
- if nesting:
- hooks.fun(hooks=hooks, nesting=nesting - 1)
-
- class PluginWrap:
- def __init__(self, num):
- self.num = num
-
- def __repr__(self):
- return f"<PluginWrap {self.num}>"
-
- @hookimpl(hookwrapper=True)
- def fun(self):
- yield
-
- pm.add_hookspecs(HookSpec)
-
- for i in range(plugins):
- pm.register(Plugin(i), name=f"plug_{i}")
- for i in range(wrappers):
- pm.register(PluginWrap(i), name=f"wrap_plug_{i}")
-
- benchmark(pm.hook.fun, hooks=pm.hook, nesting=nesting)
+"""
+Benchmarking and performance tests.
+"""
+import pytest
+from pluggy import HookspecMarker, HookimplMarker, PluginManager
+from pluggy._hooks import HookImpl
+from pluggy._callers import _multicall
+
+
+hookspec = HookspecMarker("example")
+hookimpl = HookimplMarker("example")
+
+
+@hookimpl
+def hook(arg1, arg2, arg3):
+ return arg1, arg2, arg3
+
+
+@hookimpl(hookwrapper=True)
+def wrapper(arg1, arg2, arg3):
+ yield
+
+
+@pytest.fixture(params=[10, 100], ids="hooks={}".format)
+def hooks(request):
+ return [hook for i in range(request.param)]
+
+
+@pytest.fixture(params=[10, 100], ids="wrappers={}".format)
+def wrappers(request):
+ return [wrapper for i in range(request.param)]
+
+
+def test_hook_and_wrappers_speed(benchmark, hooks, wrappers):
+ def setup():
+ hook_name = "foo"
+ hook_impls = []
+ for method in hooks + wrappers:
+ f = HookImpl(None, "<temp>", method, method.example_impl)
+ hook_impls.append(f)
+ caller_kwargs = {"arg1": 1, "arg2": 2, "arg3": 3}
+ firstresult = False
+ return (hook_name, hook_impls, caller_kwargs, firstresult), {}
+
+ benchmark.pedantic(_multicall, setup=setup)
+
+
+@pytest.mark.parametrize(
+ ("plugins, wrappers, nesting"),
+ [
+ (1, 1, 0),
+ (1, 1, 1),
+ (1, 1, 5),
+ (1, 5, 1),
+ (1, 5, 5),
+ (5, 1, 1),
+ (5, 1, 5),
+ (5, 5, 1),
+ (5, 5, 5),
+ (20, 20, 0),
+ (100, 100, 0),
+ ],
+)
+def test_call_hook(benchmark, plugins, wrappers, nesting):
+ pm = PluginManager("example")
+
+ class HookSpec:
+ @hookspec
+ def fun(self, hooks, nesting: int):
+ yield
+
+ class Plugin:
+ def __init__(self, num):
+ self.num = num
+
+ def __repr__(self):
+ return f"<Plugin {self.num}>"
+
+ @hookimpl
+ def fun(self, hooks, nesting: int):
+ if nesting:
+ hooks.fun(hooks=hooks, nesting=nesting - 1)
+
+ class PluginWrap:
+ def __init__(self, num):
+ self.num = num
+
+ def __repr__(self):
+ return f"<PluginWrap {self.num}>"
+
+ @hookimpl(hookwrapper=True)
+ def fun(self):
+ yield
+
+ pm.add_hookspecs(HookSpec)
+
+ for i in range(plugins):
+ pm.register(Plugin(i), name=f"plug_{i}")
+ for i in range(wrappers):
+ pm.register(PluginWrap(i), name=f"wrap_plug_{i}")
+
+ benchmark(pm.hook.fun, hooks=pm.hook, nesting=nesting)
diff --git a/contrib/python/pluggy/py3/tests/conftest.py b/contrib/python/pluggy/py3/tests/conftest.py
index 1fd4ecd5bd..e4cbe346e3 100644
--- a/contrib/python/pluggy/py3/tests/conftest.py
+++ b/contrib/python/pluggy/py3/tests/conftest.py
@@ -1,26 +1,26 @@
-import pytest
-
-
-@pytest.fixture(
- params=[lambda spec: spec, lambda spec: spec()],
- ids=["spec-is-class", "spec-is-instance"],
-)
-def he_pm(request, pm):
- from pluggy import HookspecMarker
-
- hookspec = HookspecMarker("example")
-
- class Hooks:
- @hookspec
- def he_method1(self, arg):
- return arg + 1
-
- pm.add_hookspecs(request.param(Hooks))
- return pm
-
-
-@pytest.fixture
-def pm():
- from pluggy import PluginManager
-
- return PluginManager("example")
+import pytest
+
+
+@pytest.fixture(
+ params=[lambda spec: spec, lambda spec: spec()],
+ ids=["spec-is-class", "spec-is-instance"],
+)
+def he_pm(request, pm):
+ from pluggy import HookspecMarker
+
+ hookspec = HookspecMarker("example")
+
+ class Hooks:
+ @hookspec
+ def he_method1(self, arg):
+ return arg + 1
+
+ pm.add_hookspecs(request.param(Hooks))
+ return pm
+
+
+@pytest.fixture
+def pm():
+ from pluggy import PluginManager
+
+ return PluginManager("example")
diff --git a/contrib/python/pluggy/py3/tests/test_details.py b/contrib/python/pluggy/py3/tests/test_details.py
index 0ceb3b3eb1..a4d4d777a2 100644
--- a/contrib/python/pluggy/py3/tests/test_details.py
+++ b/contrib/python/pluggy/py3/tests/test_details.py
@@ -1,135 +1,135 @@
-import warnings
-import pytest
-from pluggy import PluginManager, HookimplMarker, HookspecMarker
-
-hookspec = HookspecMarker("example")
-hookimpl = HookimplMarker("example")
-
-
-def test_parse_hookimpl_override():
- class MyPluginManager(PluginManager):
- def parse_hookimpl_opts(self, module_or_class, name):
- opts = PluginManager.parse_hookimpl_opts(self, module_or_class, name)
- if opts is None:
- if name.startswith("x1"):
- opts = {}
- return opts
-
- class Plugin:
- def x1meth(self):
- pass
-
- @hookimpl(hookwrapper=True, tryfirst=True)
- def x1meth2(self):
- yield # pragma: no cover
-
- class Spec:
- @hookspec
- def x1meth(self):
- pass
-
- @hookspec
- def x1meth2(self):
- pass
-
- pm = MyPluginManager(hookspec.project_name)
- pm.register(Plugin())
- pm.add_hookspecs(Spec)
- assert not pm.hook.x1meth._nonwrappers[0].hookwrapper
- assert not pm.hook.x1meth._nonwrappers[0].tryfirst
- assert not pm.hook.x1meth._nonwrappers[0].trylast
- assert not pm.hook.x1meth._nonwrappers[0].optionalhook
-
- assert pm.hook.x1meth2._wrappers[0].tryfirst
- assert pm.hook.x1meth2._wrappers[0].hookwrapper
-
-
-def test_warn_when_deprecated_specified(recwarn):
- warning = DeprecationWarning("foo is deprecated")
-
- class Spec:
- @hookspec(warn_on_impl=warning)
- def foo(self):
- pass
-
- class Plugin:
- @hookimpl
- def foo(self):
- pass
-
- pm = PluginManager(hookspec.project_name)
- pm.add_hookspecs(Spec)
-
- with pytest.warns(DeprecationWarning) as records:
- pm.register(Plugin())
- (record,) = records
- assert record.message is warning
- assert record.filename == Plugin.foo.__code__.co_filename
- assert record.lineno == Plugin.foo.__code__.co_firstlineno
-
-
-def test_plugin_getattr_raises_errors():
- """Pluggy must be able to handle plugins which raise weird exceptions
- when getattr() gets called (#11).
- """
-
- class DontTouchMe:
- def __getattr__(self, x):
- raise Exception("cant touch me")
-
- class Module:
- pass
-
- module = Module()
- module.x = DontTouchMe()
-
- pm = PluginManager(hookspec.project_name)
- # register() would raise an error
- pm.register(module, "donttouch")
- assert pm.get_plugin("donttouch") is module
-
-
-def test_warning_on_call_vs_hookspec_arg_mismatch():
- """Verify that is a hook is called with less arguments then defined in the
- spec that a warning is emitted.
- """
-
- class Spec:
- @hookspec
- def myhook(self, arg1, arg2):
- pass
-
- class Plugin:
- @hookimpl
- def myhook(self, arg1):
- pass
-
- pm = PluginManager(hookspec.project_name)
- pm.register(Plugin())
- pm.add_hookspecs(Spec())
-
- with warnings.catch_warnings(record=True) as warns:
- warnings.simplefilter("always")
-
- # calling should trigger a warning
- pm.hook.myhook(arg1=1)
-
- assert len(warns) == 1
- warning = warns[-1]
- assert issubclass(warning.category, Warning)
- assert "Argument(s) ('arg2',)" in str(warning.message)
-
-
-def test_repr():
- class Plugin:
- @hookimpl
- def myhook(self):
- raise NotImplementedError()
-
- pm = PluginManager(hookspec.project_name)
-
- plugin = Plugin()
- pname = pm.register(plugin)
- assert repr(pm.hook.myhook._nonwrappers[0]) == (
- f"<HookImpl plugin_name={pname!r}, plugin={plugin!r}>"
- )
+import warnings
+import pytest
+from pluggy import PluginManager, HookimplMarker, HookspecMarker
+
+hookspec = HookspecMarker("example")
+hookimpl = HookimplMarker("example")
+
+
+def test_parse_hookimpl_override():
+ class MyPluginManager(PluginManager):
+ def parse_hookimpl_opts(self, module_or_class, name):
+ opts = PluginManager.parse_hookimpl_opts(self, module_or_class, name)
+ if opts is None:
+ if name.startswith("x1"):
+ opts = {}
+ return opts
+
+ class Plugin:
+ def x1meth(self):
+ pass
+
+ @hookimpl(hookwrapper=True, tryfirst=True)
+ def x1meth2(self):
+ yield # pragma: no cover
+
+ class Spec:
+ @hookspec
+ def x1meth(self):
+ pass
+
+ @hookspec
+ def x1meth2(self):
+ pass
+
+ pm = MyPluginManager(hookspec.project_name)
+ pm.register(Plugin())
+ pm.add_hookspecs(Spec)
+ assert not pm.hook.x1meth._nonwrappers[0].hookwrapper
+ assert not pm.hook.x1meth._nonwrappers[0].tryfirst
+ assert not pm.hook.x1meth._nonwrappers[0].trylast
+ assert not pm.hook.x1meth._nonwrappers[0].optionalhook
+
+ assert pm.hook.x1meth2._wrappers[0].tryfirst
+ assert pm.hook.x1meth2._wrappers[0].hookwrapper
+
+
+def test_warn_when_deprecated_specified(recwarn):
+ warning = DeprecationWarning("foo is deprecated")
+
+ class Spec:
+ @hookspec(warn_on_impl=warning)
+ def foo(self):
+ pass
+
+ class Plugin:
+ @hookimpl
+ def foo(self):
+ pass
+
+ pm = PluginManager(hookspec.project_name)
+ pm.add_hookspecs(Spec)
+
+ with pytest.warns(DeprecationWarning) as records:
+ pm.register(Plugin())
+ (record,) = records
+ assert record.message is warning
+ assert record.filename == Plugin.foo.__code__.co_filename
+ assert record.lineno == Plugin.foo.__code__.co_firstlineno
+
+
+def test_plugin_getattr_raises_errors():
+ """Pluggy must be able to handle plugins which raise weird exceptions
+ when getattr() gets called (#11).
+ """
+
+ class DontTouchMe:
+ def __getattr__(self, x):
+ raise Exception("cant touch me")
+
+ class Module:
+ pass
+
+ module = Module()
+ module.x = DontTouchMe()
+
+ pm = PluginManager(hookspec.project_name)
+ # register() would raise an error
+ pm.register(module, "donttouch")
+ assert pm.get_plugin("donttouch") is module
+
+
+def test_warning_on_call_vs_hookspec_arg_mismatch():
+ """Verify that is a hook is called with less arguments then defined in the
+ spec that a warning is emitted.
+ """
+
+ class Spec:
+ @hookspec
+ def myhook(self, arg1, arg2):
+ pass
+
+ class Plugin:
+ @hookimpl
+ def myhook(self, arg1):
+ pass
+
+ pm = PluginManager(hookspec.project_name)
+ pm.register(Plugin())
+ pm.add_hookspecs(Spec())
+
+ with warnings.catch_warnings(record=True) as warns:
+ warnings.simplefilter("always")
+
+ # calling should trigger a warning
+ pm.hook.myhook(arg1=1)
+
+ assert len(warns) == 1
+ warning = warns[-1]
+ assert issubclass(warning.category, Warning)
+ assert "Argument(s) ('arg2',)" in str(warning.message)
+
+
+def test_repr():
+ class Plugin:
+ @hookimpl
+ def myhook(self):
+ raise NotImplementedError()
+
+ pm = PluginManager(hookspec.project_name)
+
+ plugin = Plugin()
+ pname = pm.register(plugin)
+ assert repr(pm.hook.myhook._nonwrappers[0]) == (
+ f"<HookImpl plugin_name={pname!r}, plugin={plugin!r}>"
+ )
diff --git a/contrib/python/pluggy/py3/tests/test_helpers.py b/contrib/python/pluggy/py3/tests/test_helpers.py
index 465858c499..d5972ac543 100644
--- a/contrib/python/pluggy/py3/tests/test_helpers.py
+++ b/contrib/python/pluggy/py3/tests/test_helpers.py
@@ -1,84 +1,84 @@
-from pluggy._hooks import varnames
-from pluggy._manager import _formatdef
-
-
-def test_varnames():
- def f(x):
- i = 3 # noqa
-
- class A:
- def f(self, y):
- pass
-
- class B:
- def __call__(self, z):
- pass
-
- assert varnames(f) == (("x",), ())
- assert varnames(A().f) == (("y",), ())
- assert varnames(B()) == (("z",), ())
-
-
-def test_varnames_default():
- def f(x, y=3):
- pass
-
- assert varnames(f) == (("x",), ("y",))
-
-
-def test_varnames_class():
- class C:
- def __init__(self, x):
- pass
-
- class D:
- pass
-
- class E:
- def __init__(self, x):
- pass
-
- class F:
- pass
-
- assert varnames(C) == (("x",), ())
- assert varnames(D) == ((), ())
- assert varnames(E) == (("x",), ())
- assert varnames(F) == ((), ())
-
-
-def test_varnames_keyword_only():
- def f1(x, *, y):
- pass
-
- def f2(x, *, y=3):
- pass
-
- def f3(x=1, *, y=3):
- pass
-
- assert varnames(f1) == (("x",), ())
- assert varnames(f2) == (("x",), ())
- assert varnames(f3) == ((), ("x",))
-
-
-def test_formatdef():
- def function1():
- pass
-
- assert _formatdef(function1) == "function1()"
-
- def function2(arg1):
- pass
-
- assert _formatdef(function2) == "function2(arg1)"
-
- def function3(arg1, arg2="qwe"):
- pass
-
- assert _formatdef(function3) == "function3(arg1, arg2='qwe')"
-
- def function4(arg1, *args, **kwargs):
- pass
-
- assert _formatdef(function4) == "function4(arg1, *args, **kwargs)"
+from pluggy._hooks import varnames
+from pluggy._manager import _formatdef
+
+
+def test_varnames():
+ def f(x):
+ i = 3 # noqa
+
+ class A:
+ def f(self, y):
+ pass
+
+ class B:
+ def __call__(self, z):
+ pass
+
+ assert varnames(f) == (("x",), ())
+ assert varnames(A().f) == (("y",), ())
+ assert varnames(B()) == (("z",), ())
+
+
+def test_varnames_default():
+ def f(x, y=3):
+ pass
+
+ assert varnames(f) == (("x",), ("y",))
+
+
+def test_varnames_class():
+ class C:
+ def __init__(self, x):
+ pass
+
+ class D:
+ pass
+
+ class E:
+ def __init__(self, x):
+ pass
+
+ class F:
+ pass
+
+ assert varnames(C) == (("x",), ())
+ assert varnames(D) == ((), ())
+ assert varnames(E) == (("x",), ())
+ assert varnames(F) == ((), ())
+
+
+def test_varnames_keyword_only():
+ def f1(x, *, y):
+ pass
+
+ def f2(x, *, y=3):
+ pass
+
+ def f3(x=1, *, y=3):
+ pass
+
+ assert varnames(f1) == (("x",), ())
+ assert varnames(f2) == (("x",), ())
+ assert varnames(f3) == ((), ("x",))
+
+
+def test_formatdef():
+ def function1():
+ pass
+
+ assert _formatdef(function1) == "function1()"
+
+ def function2(arg1):
+ pass
+
+ assert _formatdef(function2) == "function2(arg1)"
+
+ def function3(arg1, arg2="qwe"):
+ pass
+
+ assert _formatdef(function3) == "function3(arg1, arg2='qwe')"
+
+ def function4(arg1, *args, **kwargs):
+ pass
+
+ assert _formatdef(function4) == "function4(arg1, *args, **kwargs)"
diff --git a/contrib/python/pluggy/py3/tests/test_hookcaller.py b/contrib/python/pluggy/py3/tests/test_hookcaller.py
index 9eeaef8666..7120a1c721 100644
--- a/contrib/python/pluggy/py3/tests/test_hookcaller.py
+++ b/contrib/python/pluggy/py3/tests/test_hookcaller.py
@@ -1,272 +1,272 @@
-import pytest
-
-from pluggy import HookimplMarker, HookspecMarker, PluginValidationError
-from pluggy._hooks import HookImpl
-
-hookspec = HookspecMarker("example")
-hookimpl = HookimplMarker("example")
-
-
-@pytest.fixture
-def hc(pm):
- class Hooks:
- @hookspec
- def he_method1(self, arg):
- pass
-
- pm.add_hookspecs(Hooks)
- return pm.hook.he_method1
-
-
-@pytest.fixture
-def addmeth(hc):
- def addmeth(tryfirst=False, trylast=False, hookwrapper=False):
- def wrap(func):
- hookimpl(tryfirst=tryfirst, trylast=trylast, hookwrapper=hookwrapper)(func)
- hc._add_hookimpl(HookImpl(None, "<temp>", func, func.example_impl))
- return func
-
- return wrap
-
- return addmeth
-
-
-def funcs(hookmethods):
- return [hookmethod.function for hookmethod in hookmethods]
-
-
-def test_adding_nonwrappers(hc, addmeth):
- @addmeth()
- def he_method1():
- pass
-
- @addmeth()
- def he_method2():
- pass
-
- @addmeth()
- def he_method3():
- pass
-
- assert funcs(hc._nonwrappers) == [he_method1, he_method2, he_method3]
-
-
-def test_adding_nonwrappers_trylast(hc, addmeth):
- @addmeth()
- def he_method1_middle():
- pass
-
- @addmeth(trylast=True)
- def he_method1():
- pass
-
- @addmeth()
- def he_method1_b():
- pass
-
- assert funcs(hc._nonwrappers) == [he_method1, he_method1_middle, he_method1_b]
-
-
-def test_adding_nonwrappers_trylast3(hc, addmeth):
- @addmeth()
- def he_method1_a():
- pass
-
- @addmeth(trylast=True)
- def he_method1_b():
- pass
-
- @addmeth()
- def he_method1_c():
- pass
-
- @addmeth(trylast=True)
- def he_method1_d():
- pass
-
- assert funcs(hc._nonwrappers) == [
- he_method1_d,
- he_method1_b,
- he_method1_a,
- he_method1_c,
- ]
-
-
-def test_adding_nonwrappers_trylast2(hc, addmeth):
- @addmeth()
- def he_method1_middle():
- pass
-
- @addmeth()
- def he_method1_b():
- pass
-
- @addmeth(trylast=True)
- def he_method1():
- pass
-
- assert funcs(hc._nonwrappers) == [he_method1, he_method1_middle, he_method1_b]
-
-
-def test_adding_nonwrappers_tryfirst(hc, addmeth):
- @addmeth(tryfirst=True)
- def he_method1():
- pass
-
- @addmeth()
- def he_method1_middle():
- pass
-
- @addmeth()
- def he_method1_b():
- pass
-
- assert funcs(hc._nonwrappers) == [he_method1_middle, he_method1_b, he_method1]
-
-
-def test_adding_wrappers_ordering(hc, addmeth):
- @addmeth(hookwrapper=True)
- def he_method1():
- pass
-
- @addmeth()
- def he_method1_middle():
- pass
-
- @addmeth(hookwrapper=True)
- def he_method3():
- pass
-
- assert funcs(hc._nonwrappers) == [he_method1_middle]
- assert funcs(hc._wrappers) == [he_method1, he_method3]
-
-
-def test_adding_wrappers_ordering_tryfirst(hc, addmeth):
- @addmeth(hookwrapper=True, tryfirst=True)
- def he_method1():
- pass
-
- @addmeth(hookwrapper=True)
- def he_method2():
- pass
-
- assert hc._nonwrappers == []
- assert funcs(hc._wrappers) == [he_method2, he_method1]
-
-
-def test_hookspec(pm):
- class HookSpec:
- @hookspec()
- def he_myhook1(arg1):
- pass
-
- @hookspec(firstresult=True)
- def he_myhook2(arg1):
- pass
-
- @hookspec(firstresult=False)
- def he_myhook3(arg1):
- pass
-
- pm.add_hookspecs(HookSpec)
- assert not pm.hook.he_myhook1.spec.opts["firstresult"]
- assert pm.hook.he_myhook2.spec.opts["firstresult"]
- assert not pm.hook.he_myhook3.spec.opts["firstresult"]
-
-
-@pytest.mark.parametrize("name", ["hookwrapper", "optionalhook", "tryfirst", "trylast"])
-@pytest.mark.parametrize("val", [True, False])
-def test_hookimpl(name, val):
- @hookimpl(**{name: val})
- def he_myhook1(arg1):
- pass
-
- if val:
- assert he_myhook1.example_impl.get(name)
- else:
- assert not hasattr(he_myhook1, name)
-
-
-def test_hookrelay_registry(pm):
- """Verify hook caller instances are registered by name onto the relay
- and can be likewise unregistered."""
-
- class Api:
- @hookspec
- def hello(self, arg):
- "api hook 1"
-
- pm.add_hookspecs(Api)
- hook = pm.hook
- assert hasattr(hook, "hello")
- assert repr(hook.hello).find("hello") != -1
-
- class Plugin:
- @hookimpl
- def hello(self, arg):
- return arg + 1
-
- plugin = Plugin()
- pm.register(plugin)
- out = hook.hello(arg=3)
- assert out == [4]
- assert not hasattr(hook, "world")
- pm.unregister(plugin)
- assert hook.hello(arg=3) == []
-
-
-def test_hookrelay_registration_by_specname(pm):
- """Verify hook caller instances may also be registered by specifying a
- specname option to the hookimpl"""
-
- class Api:
- @hookspec
- def hello(self, arg):
- "api hook 1"
-
- pm.add_hookspecs(Api)
- hook = pm.hook
- assert hasattr(hook, "hello")
- assert len(pm.hook.hello.get_hookimpls()) == 0
-
- class Plugin:
- @hookimpl(specname="hello")
- def foo(self, arg):
- return arg + 1
-
- plugin = Plugin()
- pm.register(plugin)
- out = hook.hello(arg=3)
- assert out == [4]
-
-
-def test_hookrelay_registration_by_specname_raises(pm):
- """Verify using specname still raises the types of errors during registration as it
- would have without using specname."""
-
- class Api:
- @hookspec
- def hello(self, arg):
- "api hook 1"
-
- pm.add_hookspecs(Api)
-
- # make sure a bad signature still raises an error when using specname
- class Plugin:
- @hookimpl(specname="hello")
- def foo(self, arg, too, many, args):
- return arg + 1
-
- with pytest.raises(PluginValidationError):
- pm.register(Plugin())
-
- # make sure check_pending still fails if specname doesn't have a
- # corresponding spec. EVEN if the function name matches one.
- class Plugin2:
- @hookimpl(specname="bar")
- def hello(self, arg):
- return arg + 1
-
- pm.register(Plugin2())
- with pytest.raises(PluginValidationError):
- pm.check_pending()
+import pytest
+
+from pluggy import HookimplMarker, HookspecMarker, PluginValidationError
+from pluggy._hooks import HookImpl
+
+hookspec = HookspecMarker("example")
+hookimpl = HookimplMarker("example")
+
+
+@pytest.fixture
+def hc(pm):
+ class Hooks:
+ @hookspec
+ def he_method1(self, arg):
+ pass
+
+ pm.add_hookspecs(Hooks)
+ return pm.hook.he_method1
+
+
+@pytest.fixture
+def addmeth(hc):
+ def addmeth(tryfirst=False, trylast=False, hookwrapper=False):
+ def wrap(func):
+ hookimpl(tryfirst=tryfirst, trylast=trylast, hookwrapper=hookwrapper)(func)
+ hc._add_hookimpl(HookImpl(None, "<temp>", func, func.example_impl))
+ return func
+
+ return wrap
+
+ return addmeth
+
+
+def funcs(hookmethods):
+ return [hookmethod.function for hookmethod in hookmethods]
+
+
+def test_adding_nonwrappers(hc, addmeth):
+ @addmeth()
+ def he_method1():
+ pass
+
+ @addmeth()
+ def he_method2():
+ pass
+
+ @addmeth()
+ def he_method3():
+ pass
+
+ assert funcs(hc._nonwrappers) == [he_method1, he_method2, he_method3]
+
+
+def test_adding_nonwrappers_trylast(hc, addmeth):
+ @addmeth()
+ def he_method1_middle():
+ pass
+
+ @addmeth(trylast=True)
+ def he_method1():
+ pass
+
+ @addmeth()
+ def he_method1_b():
+ pass
+
+ assert funcs(hc._nonwrappers) == [he_method1, he_method1_middle, he_method1_b]
+
+
+def test_adding_nonwrappers_trylast3(hc, addmeth):
+ @addmeth()
+ def he_method1_a():
+ pass
+
+ @addmeth(trylast=True)
+ def he_method1_b():
+ pass
+
+ @addmeth()
+ def he_method1_c():
+ pass
+
+ @addmeth(trylast=True)
+ def he_method1_d():
+ pass
+
+ assert funcs(hc._nonwrappers) == [
+ he_method1_d,
+ he_method1_b,
+ he_method1_a,
+ he_method1_c,
+ ]
+
+
+def test_adding_nonwrappers_trylast2(hc, addmeth):
+ @addmeth()
+ def he_method1_middle():
+ pass
+
+ @addmeth()
+ def he_method1_b():
+ pass
+
+ @addmeth(trylast=True)
+ def he_method1():
+ pass
+
+ assert funcs(hc._nonwrappers) == [he_method1, he_method1_middle, he_method1_b]
+
+
+def test_adding_nonwrappers_tryfirst(hc, addmeth):
+ @addmeth(tryfirst=True)
+ def he_method1():
+ pass
+
+ @addmeth()
+ def he_method1_middle():
+ pass
+
+ @addmeth()
+ def he_method1_b():
+ pass
+
+ assert funcs(hc._nonwrappers) == [he_method1_middle, he_method1_b, he_method1]
+
+
+def test_adding_wrappers_ordering(hc, addmeth):
+ @addmeth(hookwrapper=True)
+ def he_method1():
+ pass
+
+ @addmeth()
+ def he_method1_middle():
+ pass
+
+ @addmeth(hookwrapper=True)
+ def he_method3():
+ pass
+
+ assert funcs(hc._nonwrappers) == [he_method1_middle]
+ assert funcs(hc._wrappers) == [he_method1, he_method3]
+
+
+def test_adding_wrappers_ordering_tryfirst(hc, addmeth):
+ @addmeth(hookwrapper=True, tryfirst=True)
+ def he_method1():
+ pass
+
+ @addmeth(hookwrapper=True)
+ def he_method2():
+ pass
+
+ assert hc._nonwrappers == []
+ assert funcs(hc._wrappers) == [he_method2, he_method1]
+
+
+def test_hookspec(pm):
+ class HookSpec:
+ @hookspec()
+ def he_myhook1(arg1):
+ pass
+
+ @hookspec(firstresult=True)
+ def he_myhook2(arg1):
+ pass
+
+ @hookspec(firstresult=False)
+ def he_myhook3(arg1):
+ pass
+
+ pm.add_hookspecs(HookSpec)
+ assert not pm.hook.he_myhook1.spec.opts["firstresult"]
+ assert pm.hook.he_myhook2.spec.opts["firstresult"]
+ assert not pm.hook.he_myhook3.spec.opts["firstresult"]
+
+
+@pytest.mark.parametrize("name", ["hookwrapper", "optionalhook", "tryfirst", "trylast"])
+@pytest.mark.parametrize("val", [True, False])
+def test_hookimpl(name, val):
+ @hookimpl(**{name: val})
+ def he_myhook1(arg1):
+ pass
+
+ if val:
+ assert he_myhook1.example_impl.get(name)
+ else:
+ assert not hasattr(he_myhook1, name)
+
+
+def test_hookrelay_registry(pm):
+ """Verify hook caller instances are registered by name onto the relay
+ and can be likewise unregistered."""
+
+ class Api:
+ @hookspec
+ def hello(self, arg):
+ "api hook 1"
+
+ pm.add_hookspecs(Api)
+ hook = pm.hook
+ assert hasattr(hook, "hello")
+ assert repr(hook.hello).find("hello") != -1
+
+ class Plugin:
+ @hookimpl
+ def hello(self, arg):
+ return arg + 1
+
+ plugin = Plugin()
+ pm.register(plugin)
+ out = hook.hello(arg=3)
+ assert out == [4]
+ assert not hasattr(hook, "world")
+ pm.unregister(plugin)
+ assert hook.hello(arg=3) == []
+
+
+def test_hookrelay_registration_by_specname(pm):
+ """Verify hook caller instances may also be registered by specifying a
+ specname option to the hookimpl"""
+
+ class Api:
+ @hookspec
+ def hello(self, arg):
+ "api hook 1"
+
+ pm.add_hookspecs(Api)
+ hook = pm.hook
+ assert hasattr(hook, "hello")
+ assert len(pm.hook.hello.get_hookimpls()) == 0
+
+ class Plugin:
+ @hookimpl(specname="hello")
+ def foo(self, arg):
+ return arg + 1
+
+ plugin = Plugin()
+ pm.register(plugin)
+ out = hook.hello(arg=3)
+ assert out == [4]
+
+
+def test_hookrelay_registration_by_specname_raises(pm):
+ """Verify using specname still raises the types of errors during registration as it
+ would have without using specname."""
+
+ class Api:
+ @hookspec
+ def hello(self, arg):
+ "api hook 1"
+
+ pm.add_hookspecs(Api)
+
+ # make sure a bad signature still raises an error when using specname
+ class Plugin:
+ @hookimpl(specname="hello")
+ def foo(self, arg, too, many, args):
+ return arg + 1
+
+ with pytest.raises(PluginValidationError):
+ pm.register(Plugin())
+
+ # make sure check_pending still fails if specname doesn't have a
+ # corresponding spec. EVEN if the function name matches one.
+ class Plugin2:
+ @hookimpl(specname="bar")
+ def hello(self, arg):
+ return arg + 1
+
+ pm.register(Plugin2())
+ with pytest.raises(PluginValidationError):
+ pm.check_pending()
diff --git a/contrib/python/pluggy/py3/tests/test_invocations.py b/contrib/python/pluggy/py3/tests/test_invocations.py
index 323b9b21e8..e254849adc 100644
--- a/contrib/python/pluggy/py3/tests/test_invocations.py
+++ b/contrib/python/pluggy/py3/tests/test_invocations.py
@@ -1,215 +1,215 @@
-import pytest
-from pluggy import PluginValidationError, HookimplMarker, HookspecMarker
-
-
-hookspec = HookspecMarker("example")
-hookimpl = HookimplMarker("example")
-
-
-def test_argmismatch(pm):
- class Api:
- @hookspec
- def hello(self, arg):
- "api hook 1"
-
- pm.add_hookspecs(Api)
-
- class Plugin:
- @hookimpl
- def hello(self, argwrong):
- pass
-
- with pytest.raises(PluginValidationError) as exc:
- pm.register(Plugin())
-
- assert "argwrong" in str(exc.value)
-
-
-def test_only_kwargs(pm):
- class Api:
- @hookspec
- def hello(self, arg):
- "api hook 1"
-
- pm.add_hookspecs(Api)
- with pytest.raises(TypeError) as exc:
- pm.hook.hello(3)
-
- comprehensible = "hook calling supports only keyword arguments"
- assert comprehensible in str(exc.value)
-
-
-def test_opt_in_args(pm):
- """Verfiy that two hookimpls with mutex args can serve
- under the same spec.
- """
-
- class Api:
- @hookspec
- def hello(self, arg1, arg2, common_arg):
- "api hook 1"
-
- class Plugin1:
- @hookimpl
- def hello(self, arg1, common_arg):
- return arg1 + common_arg
-
- class Plugin2:
- @hookimpl
- def hello(self, arg2, common_arg):
- return arg2 + common_arg
-
- pm.add_hookspecs(Api)
- pm.register(Plugin1())
- pm.register(Plugin2())
-
- results = pm.hook.hello(arg1=1, arg2=2, common_arg=0)
- assert results == [2, 1]
-
-
-def test_call_order(pm):
- class Api:
- @hookspec
- def hello(self, arg):
- "api hook 1"
-
- pm.add_hookspecs(Api)
-
- class Plugin1:
- @hookimpl
- def hello(self, arg):
- return 1
-
- class Plugin2:
- @hookimpl
- def hello(self, arg):
- return 2
-
- class Plugin3:
- @hookimpl
- def hello(self, arg):
- return 3
-
- class Plugin4:
- @hookimpl(hookwrapper=True)
- def hello(self, arg):
- assert arg == 0
- outcome = yield
- assert outcome.get_result() == [3, 2, 1]
-
- pm.register(Plugin1())
- pm.register(Plugin2())
- pm.register(Plugin3())
- pm.register(Plugin4()) # hookwrapper should get same list result
- res = pm.hook.hello(arg=0)
- assert res == [3, 2, 1]
-
-
-def test_firstresult_definition(pm):
- class Api:
- @hookspec(firstresult=True)
- def hello(self, arg):
- "api hook 1"
-
- pm.add_hookspecs(Api)
-
- class Plugin1:
- @hookimpl
- def hello(self, arg):
- return arg + 1
-
- class Plugin2:
- @hookimpl
- def hello(self, arg):
- return arg - 1
-
- class Plugin3:
- @hookimpl
- def hello(self, arg):
- return None
-
- class Plugin4:
- @hookimpl(hookwrapper=True)
- def hello(self, arg):
- assert arg == 3
- outcome = yield
- assert outcome.get_result() == 2
-
- pm.register(Plugin1()) # discarded - not the last registered plugin
- pm.register(Plugin2()) # used as result
- pm.register(Plugin3()) # None result is ignored
- pm.register(Plugin4()) # hookwrapper should get same non-list result
- res = pm.hook.hello(arg=3)
- assert res == 2
-
-
-def test_firstresult_force_result(pm):
- """Verify forcing a result in a wrapper."""
-
- class Api:
- @hookspec(firstresult=True)
- def hello(self, arg):
- "api hook 1"
-
- pm.add_hookspecs(Api)
-
- class Plugin1:
- @hookimpl
- def hello(self, arg):
- return arg + 1
-
- class Plugin2:
- @hookimpl(hookwrapper=True)
- def hello(self, arg):
- assert arg == 3
- outcome = yield
- assert outcome.get_result() == 4
- outcome.force_result(0)
-
- class Plugin3:
- @hookimpl
- def hello(self, arg):
- return None
-
- pm.register(Plugin1())
- pm.register(Plugin2()) # wrapper
- pm.register(Plugin3()) # ignored since returns None
- res = pm.hook.hello(arg=3)
- assert res == 0 # this result is forced and not a list
-
-
-def test_firstresult_returns_none(pm):
- """If None results are returned by underlying implementations ensure
- the multi-call loop returns a None value.
- """
-
- class Api:
- @hookspec(firstresult=True)
- def hello(self, arg):
- "api hook 1"
-
- pm.add_hookspecs(Api)
-
- class Plugin1:
- @hookimpl
- def hello(self, arg):
- return None
-
- pm.register(Plugin1())
- res = pm.hook.hello(arg=3)
- assert res is None
-
-
-def test_firstresult_no_plugin(pm):
- """If no implementations/plugins have been registered for a firstresult
- hook the multi-call loop should return a None value.
- """
-
- class Api:
- @hookspec(firstresult=True)
- def hello(self, arg):
- "api hook 1"
-
- pm.add_hookspecs(Api)
- res = pm.hook.hello(arg=3)
- assert res is None
+import pytest
+from pluggy import PluginValidationError, HookimplMarker, HookspecMarker
+
+
+hookspec = HookspecMarker("example")
+hookimpl = HookimplMarker("example")
+
+
+def test_argmismatch(pm):
+ class Api:
+ @hookspec
+ def hello(self, arg):
+ "api hook 1"
+
+ pm.add_hookspecs(Api)
+
+ class Plugin:
+ @hookimpl
+ def hello(self, argwrong):
+ pass
+
+ with pytest.raises(PluginValidationError) as exc:
+ pm.register(Plugin())
+
+ assert "argwrong" in str(exc.value)
+
+
+def test_only_kwargs(pm):
+ class Api:
+ @hookspec
+ def hello(self, arg):
+ "api hook 1"
+
+ pm.add_hookspecs(Api)
+ with pytest.raises(TypeError) as exc:
+ pm.hook.hello(3)
+
+ comprehensible = "hook calling supports only keyword arguments"
+ assert comprehensible in str(exc.value)
+
+
+def test_opt_in_args(pm):
+ """Verfiy that two hookimpls with mutex args can serve
+ under the same spec.
+ """
+
+ class Api:
+ @hookspec
+ def hello(self, arg1, arg2, common_arg):
+ "api hook 1"
+
+ class Plugin1:
+ @hookimpl
+ def hello(self, arg1, common_arg):
+ return arg1 + common_arg
+
+ class Plugin2:
+ @hookimpl
+ def hello(self, arg2, common_arg):
+ return arg2 + common_arg
+
+ pm.add_hookspecs(Api)
+ pm.register(Plugin1())
+ pm.register(Plugin2())
+
+ results = pm.hook.hello(arg1=1, arg2=2, common_arg=0)
+ assert results == [2, 1]
+
+
+def test_call_order(pm):
+ class Api:
+ @hookspec
+ def hello(self, arg):
+ "api hook 1"
+
+ pm.add_hookspecs(Api)
+
+ class Plugin1:
+ @hookimpl
+ def hello(self, arg):
+ return 1
+
+ class Plugin2:
+ @hookimpl
+ def hello(self, arg):
+ return 2
+
+ class Plugin3:
+ @hookimpl
+ def hello(self, arg):
+ return 3
+
+ class Plugin4:
+ @hookimpl(hookwrapper=True)
+ def hello(self, arg):
+ assert arg == 0
+ outcome = yield
+ assert outcome.get_result() == [3, 2, 1]
+
+ pm.register(Plugin1())
+ pm.register(Plugin2())
+ pm.register(Plugin3())
+ pm.register(Plugin4()) # hookwrapper should get same list result
+ res = pm.hook.hello(arg=0)
+ assert res == [3, 2, 1]
+
+
+def test_firstresult_definition(pm):
+ class Api:
+ @hookspec(firstresult=True)
+ def hello(self, arg):
+ "api hook 1"
+
+ pm.add_hookspecs(Api)
+
+ class Plugin1:
+ @hookimpl
+ def hello(self, arg):
+ return arg + 1
+
+ class Plugin2:
+ @hookimpl
+ def hello(self, arg):
+ return arg - 1
+
+ class Plugin3:
+ @hookimpl
+ def hello(self, arg):
+ return None
+
+ class Plugin4:
+ @hookimpl(hookwrapper=True)
+ def hello(self, arg):
+ assert arg == 3
+ outcome = yield
+ assert outcome.get_result() == 2
+
+ pm.register(Plugin1()) # discarded - not the last registered plugin
+ pm.register(Plugin2()) # used as result
+ pm.register(Plugin3()) # None result is ignored
+ pm.register(Plugin4()) # hookwrapper should get same non-list result
+ res = pm.hook.hello(arg=3)
+ assert res == 2
+
+
+def test_firstresult_force_result(pm):
+ """Verify forcing a result in a wrapper."""
+
+ class Api:
+ @hookspec(firstresult=True)
+ def hello(self, arg):
+ "api hook 1"
+
+ pm.add_hookspecs(Api)
+
+ class Plugin1:
+ @hookimpl
+ def hello(self, arg):
+ return arg + 1
+
+ class Plugin2:
+ @hookimpl(hookwrapper=True)
+ def hello(self, arg):
+ assert arg == 3
+ outcome = yield
+ assert outcome.get_result() == 4
+ outcome.force_result(0)
+
+ class Plugin3:
+ @hookimpl
+ def hello(self, arg):
+ return None
+
+ pm.register(Plugin1())
+ pm.register(Plugin2()) # wrapper
+ pm.register(Plugin3()) # ignored since returns None
+ res = pm.hook.hello(arg=3)
+ assert res == 0 # this result is forced and not a list
+
+
+def test_firstresult_returns_none(pm):
+ """If None results are returned by underlying implementations ensure
+ the multi-call loop returns a None value.
+ """
+
+ class Api:
+ @hookspec(firstresult=True)
+ def hello(self, arg):
+ "api hook 1"
+
+ pm.add_hookspecs(Api)
+
+ class Plugin1:
+ @hookimpl
+ def hello(self, arg):
+ return None
+
+ pm.register(Plugin1())
+ res = pm.hook.hello(arg=3)
+ assert res is None
+
+
+def test_firstresult_no_plugin(pm):
+ """If no implementations/plugins have been registered for a firstresult
+ hook the multi-call loop should return a None value.
+ """
+
+ class Api:
+ @hookspec(firstresult=True)
+ def hello(self, arg):
+ "api hook 1"
+
+ pm.add_hookspecs(Api)
+ res = pm.hook.hello(arg=3)
+ assert res is None
diff --git a/contrib/python/pluggy/py3/tests/test_multicall.py b/contrib/python/pluggy/py3/tests/test_multicall.py
index 8ffb452f69..ca65f32f7a 100644
--- a/contrib/python/pluggy/py3/tests/test_multicall.py
+++ b/contrib/python/pluggy/py3/tests/test_multicall.py
@@ -1,147 +1,147 @@
-import pytest
-from pluggy import HookCallError, HookspecMarker, HookimplMarker
-from pluggy._hooks import HookImpl
-from pluggy._callers import _multicall
-
-
-hookspec = HookspecMarker("example")
-hookimpl = HookimplMarker("example")
-
-
-def MC(methods, kwargs, firstresult=False):
- caller = _multicall
- hookfuncs = []
- for method in methods:
- f = HookImpl(None, "<temp>", method, method.example_impl)
- hookfuncs.append(f)
- return caller("foo", hookfuncs, kwargs, firstresult)
-
-
-def test_keyword_args():
- @hookimpl
- def f(x):
- return x + 1
-
- class A:
- @hookimpl
- def f(self, x, y):
- return x + y
-
- reslist = MC([f, A().f], dict(x=23, y=24))
- assert reslist == [24 + 23, 24]
-
-
-def test_keyword_args_with_defaultargs():
- @hookimpl
- def f(x, z=1):
- return x + z
-
- reslist = MC([f], dict(x=23, y=24))
- assert reslist == [24]
-
-
-def test_tags_call_error():
- @hookimpl
- def f(x):
- return x
-
- with pytest.raises(HookCallError):
- MC([f], {})
-
-
-def test_call_none_is_no_result():
- @hookimpl
- def m1():
- return 1
-
- @hookimpl
- def m2():
- return None
-
- res = MC([m1, m2], {}, firstresult=True)
- assert res == 1
- res = MC([m1, m2], {}, {})
- assert res == [1]
-
-
-def test_hookwrapper():
- out = []
-
- @hookimpl(hookwrapper=True)
- def m1():
- out.append("m1 init")
- yield None
- out.append("m1 finish")
-
- @hookimpl
- def m2():
- out.append("m2")
- return 2
-
- res = MC([m2, m1], {})
- assert res == [2]
- assert out == ["m1 init", "m2", "m1 finish"]
- out[:] = []
- res = MC([m2, m1], {}, firstresult=True)
- assert res == 2
- assert out == ["m1 init", "m2", "m1 finish"]
-
-
-def test_hookwrapper_order():
- out = []
-
- @hookimpl(hookwrapper=True)
- def m1():
- out.append("m1 init")
- yield 1
- out.append("m1 finish")
-
- @hookimpl(hookwrapper=True)
- def m2():
- out.append("m2 init")
- yield 2
- out.append("m2 finish")
-
- res = MC([m2, m1], {})
- assert res == []
- assert out == ["m1 init", "m2 init", "m2 finish", "m1 finish"]
-
-
-def test_hookwrapper_not_yield():
- @hookimpl(hookwrapper=True)
- def m1():
- pass
-
- with pytest.raises(TypeError):
- MC([m1], {})
-
-
-def test_hookwrapper_too_many_yield():
- @hookimpl(hookwrapper=True)
- def m1():
- yield 1
- yield 2
-
- with pytest.raises(RuntimeError) as ex:
- MC([m1], {})
- assert "m1" in str(ex.value)
- assert (__file__ + ":") in str(ex.value)
-
-
-@pytest.mark.parametrize("exc", [ValueError, SystemExit])
-def test_hookwrapper_exception(exc):
- out = []
-
- @hookimpl(hookwrapper=True)
- def m1():
- out.append("m1 init")
- yield None
- out.append("m1 finish")
-
- @hookimpl
- def m2():
- raise exc
-
- with pytest.raises(exc):
- MC([m2, m1], {})
- assert out == ["m1 init", "m1 finish"]
+import pytest
+from pluggy import HookCallError, HookspecMarker, HookimplMarker
+from pluggy._hooks import HookImpl
+from pluggy._callers import _multicall
+
+
+hookspec = HookspecMarker("example")
+hookimpl = HookimplMarker("example")
+
+
+def MC(methods, kwargs, firstresult=False):
+ caller = _multicall
+ hookfuncs = []
+ for method in methods:
+ f = HookImpl(None, "<temp>", method, method.example_impl)
+ hookfuncs.append(f)
+ return caller("foo", hookfuncs, kwargs, firstresult)
+
+
+def test_keyword_args():
+ @hookimpl
+ def f(x):
+ return x + 1
+
+ class A:
+ @hookimpl
+ def f(self, x, y):
+ return x + y
+
+ reslist = MC([f, A().f], dict(x=23, y=24))
+ assert reslist == [24 + 23, 24]
+
+
+def test_keyword_args_with_defaultargs():
+ @hookimpl
+ def f(x, z=1):
+ return x + z
+
+ reslist = MC([f], dict(x=23, y=24))
+ assert reslist == [24]
+
+
+def test_tags_call_error():
+ @hookimpl
+ def f(x):
+ return x
+
+ with pytest.raises(HookCallError):
+ MC([f], {})
+
+
+def test_call_none_is_no_result():
+ @hookimpl
+ def m1():
+ return 1
+
+ @hookimpl
+ def m2():
+ return None
+
+ res = MC([m1, m2], {}, firstresult=True)
+ assert res == 1
+ res = MC([m1, m2], {}, {})
+ assert res == [1]
+
+
+def test_hookwrapper():
+ out = []
+
+ @hookimpl(hookwrapper=True)
+ def m1():
+ out.append("m1 init")
+ yield None
+ out.append("m1 finish")
+
+ @hookimpl
+ def m2():
+ out.append("m2")
+ return 2
+
+ res = MC([m2, m1], {})
+ assert res == [2]
+ assert out == ["m1 init", "m2", "m1 finish"]
+ out[:] = []
+ res = MC([m2, m1], {}, firstresult=True)
+ assert res == 2
+ assert out == ["m1 init", "m2", "m1 finish"]
+
+
+def test_hookwrapper_order():
+ out = []
+
+ @hookimpl(hookwrapper=True)
+ def m1():
+ out.append("m1 init")
+ yield 1
+ out.append("m1 finish")
+
+ @hookimpl(hookwrapper=True)
+ def m2():
+ out.append("m2 init")
+ yield 2
+ out.append("m2 finish")
+
+ res = MC([m2, m1], {})
+ assert res == []
+ assert out == ["m1 init", "m2 init", "m2 finish", "m1 finish"]
+
+
+def test_hookwrapper_not_yield():
+ @hookimpl(hookwrapper=True)
+ def m1():
+ pass
+
+ with pytest.raises(TypeError):
+ MC([m1], {})
+
+
+def test_hookwrapper_too_many_yield():
+ @hookimpl(hookwrapper=True)
+ def m1():
+ yield 1
+ yield 2
+
+ with pytest.raises(RuntimeError) as ex:
+ MC([m1], {})
+ assert "m1" in str(ex.value)
+ assert (__file__ + ":") in str(ex.value)
+
+
+@pytest.mark.parametrize("exc", [ValueError, SystemExit])
+def test_hookwrapper_exception(exc):
+ out = []
+
+ @hookimpl(hookwrapper=True)
+ def m1():
+ out.append("m1 init")
+ yield None
+ out.append("m1 finish")
+
+ @hookimpl
+ def m2():
+ raise exc
+
+ with pytest.raises(exc):
+ MC([m2, m1], {})
+ assert out == ["m1 init", "m1 finish"]
diff --git a/contrib/python/pluggy/py3/tests/test_pluginmanager.py b/contrib/python/pluggy/py3/tests/test_pluginmanager.py
index 304a007a58..56a1e0756c 100644
--- a/contrib/python/pluggy/py3/tests/test_pluginmanager.py
+++ b/contrib/python/pluggy/py3/tests/test_pluginmanager.py
@@ -1,544 +1,544 @@
-"""
-``PluginManager`` unit and public API testing.
-"""
-import pytest
-
-from pluggy import (
- PluginValidationError,
- HookCallError,
- HookimplMarker,
- HookspecMarker,
-)
-from pluggy._manager import importlib_metadata
-
-
-hookspec = HookspecMarker("example")
-hookimpl = HookimplMarker("example")
-
-
-def test_plugin_double_register(pm):
- """Registering the same plugin more then once isn't allowed"""
- pm.register(42, name="abc")
- with pytest.raises(ValueError):
- pm.register(42, name="abc")
- with pytest.raises(ValueError):
- pm.register(42, name="def")
-
-
-def test_pm(pm):
- """Basic registration with objects"""
-
- class A:
- pass
-
- a1, a2 = A(), A()
- pm.register(a1)
- assert pm.is_registered(a1)
- pm.register(a2, "hello")
- assert pm.is_registered(a2)
- out = pm.get_plugins()
- assert a1 in out
- assert a2 in out
- assert pm.get_plugin("hello") == a2
- assert pm.unregister(a1) == a1
- assert not pm.is_registered(a1)
-
- out = pm.list_name_plugin()
- assert len(out) == 1
- assert out == [("hello", a2)]
-
-
-def test_has_plugin(pm):
- class A:
- pass
-
- a1 = A()
- pm.register(a1, "hello")
- assert pm.is_registered(a1)
- assert pm.has_plugin("hello")
-
-
-def test_register_dynamic_attr(he_pm):
- class A:
- def __getattr__(self, name):
- if name[0] != "_":
- return 42
- raise AttributeError()
-
- a = A()
- he_pm.register(a)
- assert not he_pm.get_hookcallers(a)
-
-
-def test_pm_name(pm):
- class A:
- pass
-
- a1 = A()
- name = pm.register(a1, name="hello")
- assert name == "hello"
- pm.unregister(a1)
- assert pm.get_plugin(a1) is None
- assert not pm.is_registered(a1)
- assert not pm.get_plugins()
- name2 = pm.register(a1, name="hello")
- assert name2 == name
- pm.unregister(name="hello")
- assert pm.get_plugin(a1) is None
- assert not pm.is_registered(a1)
- assert not pm.get_plugins()
-
-
-def test_set_blocked(pm):
- class A:
- pass
-
- a1 = A()
- name = pm.register(a1)
- assert pm.is_registered(a1)
- assert not pm.is_blocked(name)
- pm.set_blocked(name)
- assert pm.is_blocked(name)
- assert not pm.is_registered(a1)
-
- pm.set_blocked("somename")
- assert pm.is_blocked("somename")
- assert not pm.register(A(), "somename")
- pm.unregister(name="somename")
- assert pm.is_blocked("somename")
-
-
-def test_register_mismatch_method(he_pm):
- class hello:
- @hookimpl
- def he_method_notexists(self):
- pass
-
- plugin = hello()
-
- he_pm.register(plugin)
- with pytest.raises(PluginValidationError) as excinfo:
- he_pm.check_pending()
- assert excinfo.value.plugin is plugin
-
-
-def test_register_mismatch_arg(he_pm):
- class hello:
- @hookimpl
- def he_method1(self, qlwkje):
- pass
-
- plugin = hello()
-
- with pytest.raises(PluginValidationError) as excinfo:
- he_pm.register(plugin)
- assert excinfo.value.plugin is plugin
-
-
-def test_register_hookwrapper_not_a_generator_function(he_pm):
- class hello:
- @hookimpl(hookwrapper=True)
- def he_method1(self):
- pass # pragma: no cover
-
- plugin = hello()
-
- with pytest.raises(PluginValidationError, match="generator function") as excinfo:
- he_pm.register(plugin)
- assert excinfo.value.plugin is plugin
-
-
-def test_register(pm):
- class MyPlugin:
- pass
-
- my = MyPlugin()
- pm.register(my)
- assert my in pm.get_plugins()
- my2 = MyPlugin()
- pm.register(my2)
- assert {my, my2}.issubset(pm.get_plugins())
-
- assert pm.is_registered(my)
- assert pm.is_registered(my2)
- pm.unregister(my)
- assert not pm.is_registered(my)
- assert my not in pm.get_plugins()
-
-
-def test_register_unknown_hooks(pm):
- class Plugin1:
- @hookimpl
- def he_method1(self, arg):
- return arg + 1
-
- pname = pm.register(Plugin1())
-
- class Hooks:
- @hookspec
- def he_method1(self, arg):
- pass
-
- pm.add_hookspecs(Hooks)
- # assert not pm._unverified_hooks
- assert pm.hook.he_method1(arg=1) == [2]
- assert len(pm.get_hookcallers(pm.get_plugin(pname))) == 1
-
-
-def test_register_historic(pm):
- class Hooks:
- @hookspec(historic=True)
- def he_method1(self, arg):
- pass
-
- pm.add_hookspecs(Hooks)
-
- pm.hook.he_method1.call_historic(kwargs=dict(arg=1))
- out = []
-
- class Plugin:
- @hookimpl
- def he_method1(self, arg):
- out.append(arg)
-
- pm.register(Plugin())
- assert out == [1]
-
- class Plugin2:
- @hookimpl
- def he_method1(self, arg):
- out.append(arg * 10)
-
- pm.register(Plugin2())
- assert out == [1, 10]
- pm.hook.he_method1.call_historic(kwargs=dict(arg=12))
- assert out == [1, 10, 120, 12]
-
-
-@pytest.mark.parametrize("result_callback", [True, False])
-def test_with_result_memorized(pm, result_callback):
- """Verify that ``_HookCaller._maybe_apply_history()`
- correctly applies the ``result_callback`` function, when provided,
- to the result from calling each newly registered hook.
- """
- out = []
- if result_callback:
-
- def callback(res):
- out.append(res)
-
- else:
- callback = None
-
- class Hooks:
- @hookspec(historic=True)
- def he_method1(self, arg):
- pass
-
- pm.add_hookspecs(Hooks)
-
- class Plugin1:
- @hookimpl
- def he_method1(self, arg):
- return arg * 10
-
- pm.register(Plugin1())
-
- he_method1 = pm.hook.he_method1
- he_method1.call_historic(result_callback=callback, kwargs=dict(arg=1))
-
- class Plugin2:
- @hookimpl
- def he_method1(self, arg):
- return arg * 10
-
- pm.register(Plugin2())
- if result_callback:
- assert out == [10, 10]
- else:
- assert out == []
-
-
-def test_with_callbacks_immediately_executed(pm):
- class Hooks:
- @hookspec(historic=True)
- def he_method1(self, arg):
- pass
-
- pm.add_hookspecs(Hooks)
-
- class Plugin1:
- @hookimpl
- def he_method1(self, arg):
- return arg * 10
-
- class Plugin2:
- @hookimpl
- def he_method1(self, arg):
- return arg * 20
-
- class Plugin3:
- @hookimpl
- def he_method1(self, arg):
- return arg * 30
-
- out = []
- pm.register(Plugin1())
- pm.register(Plugin2())
-
- he_method1 = pm.hook.he_method1
- he_method1.call_historic(lambda res: out.append(res), dict(arg=1))
- assert out == [20, 10]
- pm.register(Plugin3())
- assert out == [20, 10, 30]
-
-
-def test_register_historic_incompat_hookwrapper(pm):
- class Hooks:
- @hookspec(historic=True)
- def he_method1(self, arg):
- pass
-
- pm.add_hookspecs(Hooks)
-
- out = []
-
- class Plugin:
- @hookimpl(hookwrapper=True)
- def he_method1(self, arg):
- out.append(arg)
-
- with pytest.raises(PluginValidationError):
- pm.register(Plugin())
-
-
-def test_call_extra(pm):
- class Hooks:
- @hookspec
- def he_method1(self, arg):
- pass
-
- pm.add_hookspecs(Hooks)
-
- def he_method1(arg):
- return arg * 10
-
- out = pm.hook.he_method1.call_extra([he_method1], dict(arg=1))
- assert out == [10]
-
-
-def test_call_with_too_few_args(pm):
- class Hooks:
- @hookspec
- def he_method1(self, arg):
- pass
-
- pm.add_hookspecs(Hooks)
-
- class Plugin1:
- @hookimpl
- def he_method1(self, arg):
- 0 / 0
-
- pm.register(Plugin1())
- with pytest.raises(HookCallError):
- with pytest.warns(UserWarning):
- pm.hook.he_method1()
-
-
-def test_subset_hook_caller(pm):
- class Hooks:
- @hookspec
- def he_method1(self, arg):
- pass
-
- pm.add_hookspecs(Hooks)
-
- out = []
-
- class Plugin1:
- @hookimpl
- def he_method1(self, arg):
- out.append(arg)
-
- class Plugin2:
- @hookimpl
- def he_method1(self, arg):
- out.append(arg * 10)
-
- class PluginNo:
- pass
-
- plugin1, plugin2, plugin3 = Plugin1(), Plugin2(), PluginNo()
- pm.register(plugin1)
- pm.register(plugin2)
- pm.register(plugin3)
- pm.hook.he_method1(arg=1)
- assert out == [10, 1]
- out[:] = []
-
- hc = pm.subset_hook_caller("he_method1", [plugin1])
- hc(arg=2)
- assert out == [20]
- out[:] = []
-
- hc = pm.subset_hook_caller("he_method1", [plugin2])
- hc(arg=2)
- assert out == [2]
- out[:] = []
-
- pm.unregister(plugin1)
- hc(arg=2)
- assert out == []
- out[:] = []
-
- pm.hook.he_method1(arg=1)
- assert out == [10]
-
-
-def test_get_hookimpls(pm):
- class Hooks:
- @hookspec
- def he_method1(self, arg):
- pass
-
- pm.add_hookspecs(Hooks)
- assert pm.hook.he_method1.get_hookimpls() == []
-
- class Plugin1:
- @hookimpl
- def he_method1(self, arg):
- pass
-
- class Plugin2:
- @hookimpl
- def he_method1(self, arg):
- pass
-
- class PluginNo:
- pass
-
- plugin1, plugin2, plugin3 = Plugin1(), Plugin2(), PluginNo()
- pm.register(plugin1)
- pm.register(plugin2)
- pm.register(plugin3)
-
- hookimpls = pm.hook.he_method1.get_hookimpls()
- hook_plugins = [item.plugin for item in hookimpls]
- assert hook_plugins == [plugin1, plugin2]
-
-
-def test_add_hookspecs_nohooks(pm):
- with pytest.raises(ValueError):
- pm.add_hookspecs(10)
-
-
-def test_load_setuptools_instantiation(monkeypatch, pm):
- class EntryPoint:
- name = "myname"
- group = "hello"
- value = "myname:foo"
-
- def load(self):
- class PseudoPlugin:
- x = 42
-
- return PseudoPlugin()
-
- class Distribution:
- entry_points = (EntryPoint(),)
-
- dist = Distribution()
-
- def my_distributions():
- return (dist,)
-
- monkeypatch.setattr(importlib_metadata, "distributions", my_distributions)
- num = pm.load_setuptools_entrypoints("hello")
- assert num == 1
- plugin = pm.get_plugin("myname")
- assert plugin.x == 42
- ret = pm.list_plugin_distinfo()
- # poor man's `assert ret == [(plugin, mock.ANY)]`
- assert len(ret) == 1
- assert len(ret[0]) == 2
- assert ret[0][0] == plugin
- assert ret[0][1]._dist == dist
- num = pm.load_setuptools_entrypoints("hello")
- assert num == 0 # no plugin loaded by this call
-
-
-def test_add_tracefuncs(he_pm):
- out = []
-
- class api1:
- @hookimpl
- def he_method1(self):
- out.append("he_method1-api1")
-
- class api2:
- @hookimpl
- def he_method1(self):
- out.append("he_method1-api2")
-
- he_pm.register(api1())
- he_pm.register(api2())
-
- def before(hook_name, hook_impls, kwargs):
- out.append((hook_name, list(hook_impls), kwargs))
-
- def after(outcome, hook_name, hook_impls, kwargs):
- out.append((outcome, hook_name, list(hook_impls), kwargs))
-
- undo = he_pm.add_hookcall_monitoring(before, after)
-
- he_pm.hook.he_method1(arg=1)
- assert len(out) == 4
- assert out[0][0] == "he_method1"
- assert len(out[0][1]) == 2
- assert isinstance(out[0][2], dict)
- assert out[1] == "he_method1-api2"
- assert out[2] == "he_method1-api1"
- assert len(out[3]) == 4
- assert out[3][1] == out[0][0]
-
- undo()
- he_pm.hook.he_method1(arg=1)
- assert len(out) == 4 + 2
-
-
-def test_hook_tracing(he_pm):
- saveindent = []
-
- class api1:
- @hookimpl
- def he_method1(self):
- saveindent.append(he_pm.trace.root.indent)
-
- class api2:
- @hookimpl
- def he_method1(self):
- saveindent.append(he_pm.trace.root.indent)
- raise ValueError()
-
- he_pm.register(api1())
- out = []
- he_pm.trace.root.setwriter(out.append)
- undo = he_pm.enable_tracing()
- try:
- indent = he_pm.trace.root.indent
- he_pm.hook.he_method1(arg=1)
- assert indent == he_pm.trace.root.indent
- assert len(out) == 2
- assert "he_method1" in out[0]
- assert "finish" in out[1]
-
- out[:] = []
- he_pm.register(api2())
-
- with pytest.raises(ValueError):
- he_pm.hook.he_method1(arg=1)
- assert he_pm.trace.root.indent == indent
- assert saveindent[0] > indent
- finally:
- undo()
+"""
+``PluginManager`` unit and public API testing.
+"""
+import pytest
+
+from pluggy import (
+ PluginValidationError,
+ HookCallError,
+ HookimplMarker,
+ HookspecMarker,
+)
+from pluggy._manager import importlib_metadata
+
+
+hookspec = HookspecMarker("example")
+hookimpl = HookimplMarker("example")
+
+
+def test_plugin_double_register(pm):
+ """Registering the same plugin more then once isn't allowed"""
+ pm.register(42, name="abc")
+ with pytest.raises(ValueError):
+ pm.register(42, name="abc")
+ with pytest.raises(ValueError):
+ pm.register(42, name="def")
+
+
+def test_pm(pm):
+ """Basic registration with objects"""
+
+ class A:
+ pass
+
+ a1, a2 = A(), A()
+ pm.register(a1)
+ assert pm.is_registered(a1)
+ pm.register(a2, "hello")
+ assert pm.is_registered(a2)
+ out = pm.get_plugins()
+ assert a1 in out
+ assert a2 in out
+ assert pm.get_plugin("hello") == a2
+ assert pm.unregister(a1) == a1
+ assert not pm.is_registered(a1)
+
+ out = pm.list_name_plugin()
+ assert len(out) == 1
+ assert out == [("hello", a2)]
+
+
+def test_has_plugin(pm):
+ class A:
+ pass
+
+ a1 = A()
+ pm.register(a1, "hello")
+ assert pm.is_registered(a1)
+ assert pm.has_plugin("hello")
+
+
+def test_register_dynamic_attr(he_pm):
+ class A:
+ def __getattr__(self, name):
+ if name[0] != "_":
+ return 42
+ raise AttributeError()
+
+ a = A()
+ he_pm.register(a)
+ assert not he_pm.get_hookcallers(a)
+
+
+def test_pm_name(pm):
+ class A:
+ pass
+
+ a1 = A()
+ name = pm.register(a1, name="hello")
+ assert name == "hello"
+ pm.unregister(a1)
+ assert pm.get_plugin(a1) is None
+ assert not pm.is_registered(a1)
+ assert not pm.get_plugins()
+ name2 = pm.register(a1, name="hello")
+ assert name2 == name
+ pm.unregister(name="hello")
+ assert pm.get_plugin(a1) is None
+ assert not pm.is_registered(a1)
+ assert not pm.get_plugins()
+
+
+def test_set_blocked(pm):
+ class A:
+ pass
+
+ a1 = A()
+ name = pm.register(a1)
+ assert pm.is_registered(a1)
+ assert not pm.is_blocked(name)
+ pm.set_blocked(name)
+ assert pm.is_blocked(name)
+ assert not pm.is_registered(a1)
+
+ pm.set_blocked("somename")
+ assert pm.is_blocked("somename")
+ assert not pm.register(A(), "somename")
+ pm.unregister(name="somename")
+ assert pm.is_blocked("somename")
+
+
+def test_register_mismatch_method(he_pm):
+ class hello:
+ @hookimpl
+ def he_method_notexists(self):
+ pass
+
+ plugin = hello()
+
+ he_pm.register(plugin)
+ with pytest.raises(PluginValidationError) as excinfo:
+ he_pm.check_pending()
+ assert excinfo.value.plugin is plugin
+
+
+def test_register_mismatch_arg(he_pm):
+ class hello:
+ @hookimpl
+ def he_method1(self, qlwkje):
+ pass
+
+ plugin = hello()
+
+ with pytest.raises(PluginValidationError) as excinfo:
+ he_pm.register(plugin)
+ assert excinfo.value.plugin is plugin
+
+
+def test_register_hookwrapper_not_a_generator_function(he_pm):
+ class hello:
+ @hookimpl(hookwrapper=True)
+ def he_method1(self):
+ pass # pragma: no cover
+
+ plugin = hello()
+
+ with pytest.raises(PluginValidationError, match="generator function") as excinfo:
+ he_pm.register(plugin)
+ assert excinfo.value.plugin is plugin
+
+
+def test_register(pm):
+ class MyPlugin:
+ pass
+
+ my = MyPlugin()
+ pm.register(my)
+ assert my in pm.get_plugins()
+ my2 = MyPlugin()
+ pm.register(my2)
+ assert {my, my2}.issubset(pm.get_plugins())
+
+ assert pm.is_registered(my)
+ assert pm.is_registered(my2)
+ pm.unregister(my)
+ assert not pm.is_registered(my)
+ assert my not in pm.get_plugins()
+
+
+def test_register_unknown_hooks(pm):
+ class Plugin1:
+ @hookimpl
+ def he_method1(self, arg):
+ return arg + 1
+
+ pname = pm.register(Plugin1())
+
+ class Hooks:
+ @hookspec
+ def he_method1(self, arg):
+ pass
+
+ pm.add_hookspecs(Hooks)
+ # assert not pm._unverified_hooks
+ assert pm.hook.he_method1(arg=1) == [2]
+ assert len(pm.get_hookcallers(pm.get_plugin(pname))) == 1
+
+
+def test_register_historic(pm):
+ class Hooks:
+ @hookspec(historic=True)
+ def he_method1(self, arg):
+ pass
+
+ pm.add_hookspecs(Hooks)
+
+ pm.hook.he_method1.call_historic(kwargs=dict(arg=1))
+ out = []
+
+ class Plugin:
+ @hookimpl
+ def he_method1(self, arg):
+ out.append(arg)
+
+ pm.register(Plugin())
+ assert out == [1]
+
+ class Plugin2:
+ @hookimpl
+ def he_method1(self, arg):
+ out.append(arg * 10)
+
+ pm.register(Plugin2())
+ assert out == [1, 10]
+ pm.hook.he_method1.call_historic(kwargs=dict(arg=12))
+ assert out == [1, 10, 120, 12]
+
+
+@pytest.mark.parametrize("result_callback", [True, False])
+def test_with_result_memorized(pm, result_callback):
+ """Verify that ``_HookCaller._maybe_apply_history()`
+ correctly applies the ``result_callback`` function, when provided,
+ to the result from calling each newly registered hook.
+ """
+ out = []
+ if result_callback:
+
+ def callback(res):
+ out.append(res)
+
+ else:
+ callback = None
+
+ class Hooks:
+ @hookspec(historic=True)
+ def he_method1(self, arg):
+ pass
+
+ pm.add_hookspecs(Hooks)
+
+ class Plugin1:
+ @hookimpl
+ def he_method1(self, arg):
+ return arg * 10
+
+ pm.register(Plugin1())
+
+ he_method1 = pm.hook.he_method1
+ he_method1.call_historic(result_callback=callback, kwargs=dict(arg=1))
+
+ class Plugin2:
+ @hookimpl
+ def he_method1(self, arg):
+ return arg * 10
+
+ pm.register(Plugin2())
+ if result_callback:
+ assert out == [10, 10]
+ else:
+ assert out == []
+
+
+def test_with_callbacks_immediately_executed(pm):
+ class Hooks:
+ @hookspec(historic=True)
+ def he_method1(self, arg):
+ pass
+
+ pm.add_hookspecs(Hooks)
+
+ class Plugin1:
+ @hookimpl
+ def he_method1(self, arg):
+ return arg * 10
+
+ class Plugin2:
+ @hookimpl
+ def he_method1(self, arg):
+ return arg * 20
+
+ class Plugin3:
+ @hookimpl
+ def he_method1(self, arg):
+ return arg * 30
+
+ out = []
+ pm.register(Plugin1())
+ pm.register(Plugin2())
+
+ he_method1 = pm.hook.he_method1
+ he_method1.call_historic(lambda res: out.append(res), dict(arg=1))
+ assert out == [20, 10]
+ pm.register(Plugin3())
+ assert out == [20, 10, 30]
+
+
+def test_register_historic_incompat_hookwrapper(pm):
+ class Hooks:
+ @hookspec(historic=True)
+ def he_method1(self, arg):
+ pass
+
+ pm.add_hookspecs(Hooks)
+
+ out = []
+
+ class Plugin:
+ @hookimpl(hookwrapper=True)
+ def he_method1(self, arg):
+ out.append(arg)
+
+ with pytest.raises(PluginValidationError):
+ pm.register(Plugin())
+
+
+def test_call_extra(pm):
+ class Hooks:
+ @hookspec
+ def he_method1(self, arg):
+ pass
+
+ pm.add_hookspecs(Hooks)
+
+ def he_method1(arg):
+ return arg * 10
+
+ out = pm.hook.he_method1.call_extra([he_method1], dict(arg=1))
+ assert out == [10]
+
+
+def test_call_with_too_few_args(pm):
+ class Hooks:
+ @hookspec
+ def he_method1(self, arg):
+ pass
+
+ pm.add_hookspecs(Hooks)
+
+ class Plugin1:
+ @hookimpl
+ def he_method1(self, arg):
+ 0 / 0
+
+ pm.register(Plugin1())
+ with pytest.raises(HookCallError):
+ with pytest.warns(UserWarning):
+ pm.hook.he_method1()
+
+
+def test_subset_hook_caller(pm):
+ class Hooks:
+ @hookspec
+ def he_method1(self, arg):
+ pass
+
+ pm.add_hookspecs(Hooks)
+
+ out = []
+
+ class Plugin1:
+ @hookimpl
+ def he_method1(self, arg):
+ out.append(arg)
+
+ class Plugin2:
+ @hookimpl
+ def he_method1(self, arg):
+ out.append(arg * 10)
+
+ class PluginNo:
+ pass
+
+ plugin1, plugin2, plugin3 = Plugin1(), Plugin2(), PluginNo()
+ pm.register(plugin1)
+ pm.register(plugin2)
+ pm.register(plugin3)
+ pm.hook.he_method1(arg=1)
+ assert out == [10, 1]
+ out[:] = []
+
+ hc = pm.subset_hook_caller("he_method1", [plugin1])
+ hc(arg=2)
+ assert out == [20]
+ out[:] = []
+
+ hc = pm.subset_hook_caller("he_method1", [plugin2])
+ hc(arg=2)
+ assert out == [2]
+ out[:] = []
+
+ pm.unregister(plugin1)
+ hc(arg=2)
+ assert out == []
+ out[:] = []
+
+ pm.hook.he_method1(arg=1)
+ assert out == [10]
+
+
+def test_get_hookimpls(pm):
+ class Hooks:
+ @hookspec
+ def he_method1(self, arg):
+ pass
+
+ pm.add_hookspecs(Hooks)
+ assert pm.hook.he_method1.get_hookimpls() == []
+
+ class Plugin1:
+ @hookimpl
+ def he_method1(self, arg):
+ pass
+
+ class Plugin2:
+ @hookimpl
+ def he_method1(self, arg):
+ pass
+
+ class PluginNo:
+ pass
+
+ plugin1, plugin2, plugin3 = Plugin1(), Plugin2(), PluginNo()
+ pm.register(plugin1)
+ pm.register(plugin2)
+ pm.register(plugin3)
+
+ hookimpls = pm.hook.he_method1.get_hookimpls()
+ hook_plugins = [item.plugin for item in hookimpls]
+ assert hook_plugins == [plugin1, plugin2]
+
+
+def test_add_hookspecs_nohooks(pm):
+ with pytest.raises(ValueError):
+ pm.add_hookspecs(10)
+
+
+def test_load_setuptools_instantiation(monkeypatch, pm):
+ class EntryPoint:
+ name = "myname"
+ group = "hello"
+ value = "myname:foo"
+
+ def load(self):
+ class PseudoPlugin:
+ x = 42
+
+ return PseudoPlugin()
+
+ class Distribution:
+ entry_points = (EntryPoint(),)
+
+ dist = Distribution()
+
+ def my_distributions():
+ return (dist,)
+
+ monkeypatch.setattr(importlib_metadata, "distributions", my_distributions)
+ num = pm.load_setuptools_entrypoints("hello")
+ assert num == 1
+ plugin = pm.get_plugin("myname")
+ assert plugin.x == 42
+ ret = pm.list_plugin_distinfo()
+ # poor man's `assert ret == [(plugin, mock.ANY)]`
+ assert len(ret) == 1
+ assert len(ret[0]) == 2
+ assert ret[0][0] == plugin
+ assert ret[0][1]._dist == dist
+ num = pm.load_setuptools_entrypoints("hello")
+ assert num == 0 # no plugin loaded by this call
+
+
+def test_add_tracefuncs(he_pm):
+ out = []
+
+ class api1:
+ @hookimpl
+ def he_method1(self):
+ out.append("he_method1-api1")
+
+ class api2:
+ @hookimpl
+ def he_method1(self):
+ out.append("he_method1-api2")
+
+ he_pm.register(api1())
+ he_pm.register(api2())
+
+ def before(hook_name, hook_impls, kwargs):
+ out.append((hook_name, list(hook_impls), kwargs))
+
+ def after(outcome, hook_name, hook_impls, kwargs):
+ out.append((outcome, hook_name, list(hook_impls), kwargs))
+
+ undo = he_pm.add_hookcall_monitoring(before, after)
+
+ he_pm.hook.he_method1(arg=1)
+ assert len(out) == 4
+ assert out[0][0] == "he_method1"
+ assert len(out[0][1]) == 2
+ assert isinstance(out[0][2], dict)
+ assert out[1] == "he_method1-api2"
+ assert out[2] == "he_method1-api1"
+ assert len(out[3]) == 4
+ assert out[3][1] == out[0][0]
+
+ undo()
+ he_pm.hook.he_method1(arg=1)
+ assert len(out) == 4 + 2
+
+
+def test_hook_tracing(he_pm):
+ saveindent = []
+
+ class api1:
+ @hookimpl
+ def he_method1(self):
+ saveindent.append(he_pm.trace.root.indent)
+
+ class api2:
+ @hookimpl
+ def he_method1(self):
+ saveindent.append(he_pm.trace.root.indent)
+ raise ValueError()
+
+ he_pm.register(api1())
+ out = []
+ he_pm.trace.root.setwriter(out.append)
+ undo = he_pm.enable_tracing()
+ try:
+ indent = he_pm.trace.root.indent
+ he_pm.hook.he_method1(arg=1)
+ assert indent == he_pm.trace.root.indent
+ assert len(out) == 2
+ assert "he_method1" in out[0]
+ assert "finish" in out[1]
+
+ out[:] = []
+ he_pm.register(api2())
+
+ with pytest.raises(ValueError):
+ he_pm.hook.he_method1(arg=1)
+ assert he_pm.trace.root.indent == indent
+ assert saveindent[0] > indent
+ finally:
+ undo()
diff --git a/contrib/python/pluggy/py3/tests/test_tracer.py b/contrib/python/pluggy/py3/tests/test_tracer.py
index 992ec67914..7915f0ee75 100644
--- a/contrib/python/pluggy/py3/tests/test_tracer.py
+++ b/contrib/python/pluggy/py3/tests/test_tracer.py
@@ -1,78 +1,78 @@
-from pluggy._tracing import TagTracer
-
-import pytest
-
-
-@pytest.fixture
-def rootlogger():
- return TagTracer()
-
-
-def test_simple(rootlogger):
- log = rootlogger.get("pytest")
- log("hello")
- out = []
- rootlogger.setwriter(out.append)
- log("world")
- assert len(out) == 1
- assert out[0] == "world [pytest]\n"
- sublog = log.get("collection")
- sublog("hello")
- assert out[1] == "hello [pytest:collection]\n"
-
-
-def test_indent(rootlogger):
- log = rootlogger.get("1")
- out = []
- log.root.setwriter(lambda arg: out.append(arg))
- log("hello")
- log.root.indent += 1
- log("line1")
- log("line2")
- log.root.indent += 1
- log("line3")
- log("line4")
- log.root.indent -= 1
- log("line5")
- log.root.indent -= 1
- log("last")
- assert len(out) == 7
- names = [x[: x.rfind(" [")] for x in out]
- assert names == [
- "hello",
- " line1",
- " line2",
- " line3",
- " line4",
- " line5",
- "last",
- ]
-
-
-def test_readable_output_dictargs(rootlogger):
-
- out = rootlogger._format_message(["test"], [1])
- assert out == "1 [test]\n"
-
- out2 = rootlogger._format_message(["test"], ["test", {"a": 1}])
- assert out2 == "test [test]\n a: 1\n"
-
-
-def test_setprocessor(rootlogger):
- log = rootlogger.get("1")
- log2 = log.get("2")
- assert log2.tags == tuple("12")
- out = []
- rootlogger.setprocessor(tuple("12"), lambda *args: out.append(args))
- log("not seen")
- log2("seen")
- assert len(out) == 1
- tags, args = out[0]
- assert "1" in tags
- assert "2" in tags
- assert args == ("seen",)
- l2 = []
- rootlogger.setprocessor("1:2", lambda *args: l2.append(args))
- log2("seen")
- tags, args = l2[0]
- assert args == ("seen",)
+from pluggy._tracing import TagTracer
+
+import pytest
+
+
+@pytest.fixture
+def rootlogger():
+ return TagTracer()
+
+
+def test_simple(rootlogger):
+ log = rootlogger.get("pytest")
+ log("hello")
+ out = []
+ rootlogger.setwriter(out.append)
+ log("world")
+ assert len(out) == 1
+ assert out[0] == "world [pytest]\n"
+ sublog = log.get("collection")
+ sublog("hello")
+ assert out[1] == "hello [pytest:collection]\n"
+
+
+def test_indent(rootlogger):
+ log = rootlogger.get("1")
+ out = []
+ log.root.setwriter(lambda arg: out.append(arg))
+ log("hello")
+ log.root.indent += 1
+ log("line1")
+ log("line2")
+ log.root.indent += 1
+ log("line3")
+ log("line4")
+ log.root.indent -= 1
+ log("line5")
+ log.root.indent -= 1
+ log("last")
+ assert len(out) == 7
+ names = [x[: x.rfind(" [")] for x in out]
+ assert names == [
+ "hello",
+ " line1",
+ " line2",
+ " line3",
+ " line4",
+ " line5",
+ "last",
+ ]
+
+
+def test_readable_output_dictargs(rootlogger):
+
+ out = rootlogger._format_message(["test"], [1])
+ assert out == "1 [test]\n"
+
+ out2 = rootlogger._format_message(["test"], ["test", {"a": 1}])
+ assert out2 == "test [test]\n a: 1\n"
+
+
+def test_setprocessor(rootlogger):
+ log = rootlogger.get("1")
+ log2 = log.get("2")
+ assert log2.tags == tuple("12")
+ out = []
+ rootlogger.setprocessor(tuple("12"), lambda *args: out.append(args))
+ log("not seen")
+ log2("seen")
+ assert len(out) == 1
+ tags, args = out[0]
+ assert "1" in tags
+ assert "2" in tags
+ assert args == ("seen",)
+ l2 = []
+ rootlogger.setprocessor("1:2", lambda *args: l2.append(args))
+ log2("seen")
+ tags, args = l2[0]
+ assert args == ("seen",)
diff --git a/contrib/python/pluggy/py3/tests/ya.make b/contrib/python/pluggy/py3/tests/ya.make
index 565cbdd42b..b35f70ca36 100644
--- a/contrib/python/pluggy/py3/tests/ya.make
+++ b/contrib/python/pluggy/py3/tests/ya.make
@@ -1,22 +1,22 @@
-PY3TEST()
-
-OWNER(g:python-contrib)
-
-PEERDIR (
- contrib/python/pluggy
-)
-
-TEST_SRCS(
- conftest.py
- test_details.py
- test_helpers.py
- test_hookcaller.py
- test_invocations.py
- test_multicall.py
- test_pluginmanager.py
- test_tracer.py
-)
-
-NO_LINT()
-
-END()
+PY3TEST()
+
+OWNER(g:python-contrib)
+
+PEERDIR (
+ contrib/python/pluggy
+)
+
+TEST_SRCS(
+ conftest.py
+ test_details.py
+ test_helpers.py
+ test_hookcaller.py
+ test_invocations.py
+ test_multicall.py
+ test_pluginmanager.py
+ test_tracer.py
+)
+
+NO_LINT()
+
+END()
diff --git a/contrib/python/pluggy/py3/ya.make b/contrib/python/pluggy/py3/ya.make
index ba9c269fc8..99b618d436 100644
--- a/contrib/python/pluggy/py3/ya.make
+++ b/contrib/python/pluggy/py3/ya.make
@@ -1,32 +1,32 @@
-PY3_LIBRARY()
-
-OWNER(g:python-contrib)
-
-VERSION(1.0.0)
-
-LICENSE(MIT)
-
-NO_LINT()
-
-PY_SRCS(
- TOP_LEVEL
- pluggy/__init__.py
- pluggy/_callers.py
- pluggy/_hooks.py
- pluggy/_manager.py
- pluggy/_result.py
- pluggy/_tracing.py
- pluggy/_version.py
-)
-
-RESOURCE_FILES(
- PREFIX contrib/python/pluggy/py3/
- .dist-info/METADATA
- .dist-info/top_level.txt
-)
-
-END()
-
-RECURSE_FOR_TESTS(
- tests
-)
+PY3_LIBRARY()
+
+OWNER(g:python-contrib)
+
+VERSION(1.0.0)
+
+LICENSE(MIT)
+
+NO_LINT()
+
+PY_SRCS(
+ TOP_LEVEL
+ pluggy/__init__.py
+ pluggy/_callers.py
+ pluggy/_hooks.py
+ pluggy/_manager.py
+ pluggy/_result.py
+ pluggy/_tracing.py
+ pluggy/_version.py
+)
+
+RESOURCE_FILES(
+ PREFIX contrib/python/pluggy/py3/
+ .dist-info/METADATA
+ .dist-info/top_level.txt
+)
+
+END()
+
+RECURSE_FOR_TESTS(
+ tests
+)
diff --git a/contrib/python/pluggy/ya.make b/contrib/python/pluggy/ya.make
index 43e4c7b2ab..54babff7fc 100644
--- a/contrib/python/pluggy/ya.make
+++ b/contrib/python/pluggy/ya.make
@@ -4,17 +4,17 @@ LICENSE(Service-Py23-Proxy)
OWNER(g:python-contrib)
-IF (PYTHON2)
- PEERDIR(contrib/python/pluggy/py2)
-ELSE()
- PEERDIR(contrib/python/pluggy/py3)
-ENDIF()
-
-NO_LINT()
+IF (PYTHON2)
+ PEERDIR(contrib/python/pluggy/py2)
+ELSE()
+ PEERDIR(contrib/python/pluggy/py3)
+ENDIF()
+
+NO_LINT()
END()
-
-RECURSE(
- py2
- py3
-)
+
+RECURSE(
+ py2
+ py3
+)