Projects
openEuler:Mainline
python-pluggy
Sign Up
Log In
Username
Password
We truncated the diff of some files because they were too big. If you want to see the full diff for every file,
click here
.
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
Expand all
Collapse all
Changes of Revision 2
View file
_service:tar_scm:python-pluggy.spec
Changed
@@ -1,12 +1,12 @@ %global _empty_manifest_terminate_build 0 %bcond_with test Name: python-pluggy -Version: 0.13.1 -Release: 2 +Version: 1.0.0 +Release: 1 Summary: plugin and hook calling mechanisms for python License: MIT URL: https://github.com/pytest-dev/pluggy -Source0: https://files.pythonhosted.org/packages/f8/04/7a8542bed4b16a65c2714bf76cf5a0b026157da7f75e87cc88774aa10b14/pluggy-0.13.1.tar.gz +Source0: https://files.pythonhosted.org/packages/a1/16/db2d7de3474b6e37cbb9c008965ee63835bba517e22cdb8c35b5116b5ce1/pluggy-1.0.0.tar.gz BuildArch: noarch Requires: python3-importlib-metadata @@ -40,7 +40,7 @@ %{_description} %prep -%autosetup -n pluggy-0.13.1 +%autosetup -n pluggy-%{version} %build %py3_build @@ -85,6 +85,9 @@ %{_pkgdocdir} %changelog +* Wed Feb 15 2023 wubijie <wubijie@kylinos.cn> - 1.0.0-1 +- Update package to version 1.0.0 + * Sat Nov 27 2021 shixuantong <shixuantong@huawei> - 0.13.1-2 - disable %check
View file
_service
Changed
@@ -2,7 +2,7 @@ <service name="tar_scm"> <param name="scm">git</param> <param name="url">git@gitee.com:src-openeuler/python-pluggy.git</param> - <param name="revision">871a4ce302d60b617843c2b6d4368fcc52fbd3fe</param> + <param name="revision">master</param> <param name="exclude">*</param> <param name="extract">*</param> </service>
View file
_service:tar_scm:pluggy-0.13.1.tar.gz/.travis.yml
Deleted
@@ -1,81 +0,0 @@ -dist: xenial -language: python - -stages: -- name: test - if: tag IS NOT present -- name: deploy - if: repo = pytest-dev/pluggy AND tag IS present - -jobs: - include: - - python: '3.6' - env: TOXENV=linting - cache: - directories: - - $HOME/.cache/pre-commit - - python: '3.6' - env: TOXENV=docs - - python: '2.7' - env: TOXENV=py27-coverage - - python: '3.4' - env: TOXENV=py34-coverage - - python: '3.5' - env: TOXENV=py35-coverage - - python: '3.6' - env: TOXENV=py36-coverage - - python: 'pypy2.7-6.0' - env: TOXENV=pypy-coverage - - python: 'pypy3.5-6.0' - env: TOXENV=pypy3-coverage - - python: '3.7' - env: TOXENV=py37-coverage - - python: '3.8-dev' - env: TOXENV=py38-coverage - - python: '3.6' - env: TOXENV=py36-pytestmaster-coverage - - python: '3.6' - env: TOXENV=py36-pytestfeatures-coverage - - python: '3.6' - env: TOXENV=benchmark - - python: '3.7' - env: TOXENV=py37-pytestmaster-coverage - - python: '3.7' - env: TOXENV=py37-pytestfeatures-coverage - - - stage: deploy - python: '3.6' - env: - install: pip install -U setuptools setuptools_scm - script: skip - deploy: - provider: pypi - user: nicoddemus - distributions: sdist bdist_wheel - skip_upload_docs: true - password: - secure: PDvQCKfXrF1V/tdwEOfeDEjDs6vJ9gKWo4yrMUmBx1JL5plHZaqfHLftlGMoHekaQTHcfyYYbxignFw5IGsn97/nFKKWDPNBfZA+3RJJmeJfz2NQunYkSnoqtBtfEtWHzFPdkCm0w/CN9C8IpRjhvLnFTUQzil6iMy6wZG276gU= - on: - tags: true - repo: pytest-dev/pluggy - -install: - - pip install -U pip - - pip install -U --force-reinstall setuptools tox - -script: - - tox - -after_script: - - | - if "${TOXENV%-coverage}" != "$TOXENV" ; then - bash <(curl -s https://codecov.io/bash) -Z -X gcov -X coveragepy -X search -X xcode -X gcovout -X fix -f coverage.xml -n $TOXENV - fi - -notifications: - irc: - channels: - - "chat.freenode.net#pytest" - on_success: change - on_failure: change - skip_join: true
View file
_service:tar_scm:pluggy-0.13.1.tar.gz/HOWTORELEASE.rst
Deleted
@@ -1,23 +0,0 @@ -Release Procedure ------------------ - -#. From a clean work tree, execute:: - - tox -e release -- VERSION - - This will create the branch ready to be pushed. - -#. Open a PR targeting ``master``. - -#. All tests must pass and the PR must be approved by at least another maintainer. - -#. Publish to PyPI by pushing a tag:: - - git tag X.Y.Z release-X.Y.Z - git push git@github.com:pytest-dev/pluggy.git X.Y.Z - - The tag will trigger a new build, which will deploy to PyPI. - -#. Make sure it is `available on PyPI <https://pypi.org/project/pluggy>`_. - -#. Merge the PR into ``master``, either manually or using GitHub's web interface.
View file
_service:tar_scm:pluggy-0.13.1.tar.gz/appveyor.yml
Deleted
@@ -1,38 +0,0 @@ -environment: - matrix: - # note: please use "tox --listenvs" to populate the build matrix below - - TOXENV: "linting" - - TOXENV: "docs" - - TOXENV: "py27" - - TOXENV: "py34" - - TOXENV: "py35" - - TOXENV: "py36" - - TOXENV: "py37" - - TOXENV: "pypy" - - TOXENV: "py36-pytestmaster" - - TOXENV: "py36-pytestfeatures" - -install: - - echo Installed Pythons - - dir c:\Python* - - # install pypy using choco (redirect to a file and write to console in case - # choco install returns non-zero, because choco install python.pypy is too - # noisy) - # pypy is disabled until #1963 gets fixed - - choco install python.pypy > pypy-inst.log 2>&1 || (type pypy-inst.log & exit /b 1) - - set PATH=C:\tools\pypy\pypy;%PATH% # so tox can find pypy - - echo PyPy installed - - pypy --version - - - C:\Python35\python -m pip install -U pip - - C:\Python35\python -m pip install -U --force-reinstall setuptools tox - -build: false # Not a C# project, build stuff at the test step instead. - -test_script: - - C:\Python35\Scripts\tox - -# We don't deploy anything on tags with AppVeyor, we use Travis instead, so we -# might as well save resources -skip_tags: true
View file
_service:tar_scm:pluggy-0.13.1.tar.gz/src/pluggy/callers.py
Deleted
@@ -1,208 +0,0 @@ -""" -Call loop machinery -""" -import sys -import warnings - -_py3 = sys.version_info > (3, 0) - - -if not _py3: - exec( - """ -def _reraise(cls, val, tb): - raise cls, val, tb -""" - ) - - -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(object): - def __init__(self, result, excinfo): - self._result = result - self._excinfo = excinfo - - @property - def excinfo(self): - return self._excinfo - - @property - def result(self): - """Get the result(s) for this hook call (DEPRECATED in favor of ``get_result()``).""" - msg = "Use get_result() which forces correct exception handling" - warnings.warn(DeprecationWarning(msg), stacklevel=2) - return self._result - - @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 - if _py3: - raise ex1.with_traceback(ex2) - _reraise(*ex) # noqa - - -def _wrapped_call(wrap_controller, func): - """ Wrap calling to a function with a generator which needs to yield - exactly once. The yield point will trigger calling the wrapped function - and return its ``_Result`` to the yield point. The generator then needs - to finish (raise StopIteration) in order for the wrapped call to complete. - """ - try: - next(wrap_controller) # first yield - except StopIteration: - _raise_wrapfail(wrap_controller, "did not yield") - call_outcome = _Result.from_call(func) - try: - wrap_controller.send(call_outcome) - _raise_wrapfail(wrap_controller, "has second yield") - except StopIteration: - pass - return call_outcome.get_result() - - -class _LegacyMultiCall(object): - """ execute a call into multiple python functions/methods. """ - - # XXX note that the __multicall__ argument is supported only - # for pytest compatibility reasons. It was never officially - # supported there and is explicitely deprecated since 2.8 - # so we can remove it soon, allowing to avoid the below recursion - # in execute() and simplify/speed up the execute loop. - - def __init__(self, hook_impls, kwargs, firstresult=False): - self.hook_impls = hook_impls - self.caller_kwargs = kwargs # come from _HookCaller.__call__() - self.caller_kwargs"__multicall__" = self - self.firstresult = firstresult - - def execute(self): - caller_kwargs = self.caller_kwargs - self.results = results = - firstresult = self.firstresult - - while self.hook_impls: - hook_impl = self.hook_impls.pop() - try: - args = caller_kwargsargname for argname in hook_impl.argnames - except KeyError: - for argname in hook_impl.argnames: - if argname not in caller_kwargs: - raise HookCallError( - "hook call must provide argument %r" % (argname,) - ) - if hook_impl.hookwrapper: - return _wrapped_call(hook_impl.function(*args), self.execute) - res = hook_impl.function(*args) - if res is not None: - if firstresult: - return res - results.append(res) - - if not firstresult: - return results - - def __repr__(self): - status = "%d meths" % (len(self.hook_impls),) - if hasattr(self, "results"): - status = ("%d results, " % len(self.results)) + status - return "<_MultiCall %s, kwargs=%r>" % (status, self.caller_kwargs) - - -def _legacymulticall(hook_impls, caller_kwargs, firstresult=False): - return _LegacyMultiCall( - hook_impls, caller_kwargs, firstresult=firstresult - ).execute() - - -def _multicall(hook_impls, caller_kwargs, firstresult=False): - """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_kwargsargname for argname in hook_impl.argnames - except KeyError: - for argname in hook_impl.argnames: - if argname not in caller_kwargs: - raise HookCallError( - "hook call must provide argument %r" % (argname,) - ) - - 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(results0 if results else None, excinfo) - else: - outcome = _Result(results, excinfo) -
View file
_service:tar_scm:pluggy-0.13.1.tar.gz/src/pluggy/hooks.py
Deleted
@@ -1,359 +0,0 @@ -""" -Internal hook annotation, representation and calling machinery. -""" -import inspect -import sys -import warnings -from .callers import _legacymulticall, _multicall - - -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. - """ - - 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(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. - """ - - def __init__(self, project_name): - self.project_name = project_name - - def __call__( - self, - function=None, - hookwrapper=False, - optionalhook=False, - tryfirst=False, - trylast=False, - ): - - """ 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). - - """ - - def setattr_hookimpl_opts(func): - setattr( - func, - self.project_name + "_impl", - dict( - hookwrapper=hookwrapper, - optionalhook=optionalhook, - tryfirst=tryfirst, - trylast=trylast, - ), - ) - 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) - - -if hasattr(inspect, "getfullargspec"): - - def _getargspec(func): - return inspect.getfullargspec(func) - - -else: - - def _getargspec(func): - return inspect.getargspec(func) - - -_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. - - In case of a class, its ``__init__`` method is considered. - For methods the ``self`` parameter is not included. - """ - cache = getattr(func, "__dict__", {}) - try: - return cache"_varnames" - except KeyError: - pass - - 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 = _getargspec(func) - except TypeError: - return (), () - - args, defaults = tuple(spec.args), spec.defaults - if defaults: - index = -len(defaults) - args, kwargs = args:index, tuple(argsindex:) - else: - 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") - if args: - if inspect.ismethod(func) or ( - "." in getattr(func, "__qualname__", ()) and args0 in implicit_names - ): - args = args1: - - try: - cache"_varnames" = args, kwargs - except TypeError: - pass - return args, kwargs - - -class _HookRelay(object): - """ hook holder object for performing 1:N hook calls where N is the number - of registered plugins. - - """ -
View file
_service:tar_scm:pluggy-0.13.1.tar.gz/src/pluggy/manager.py
Deleted
@@ -1,394 +0,0 @@ -import inspect -import sys -from . import _tracing -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 - - -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(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 - 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, implprefix=None): - """If ``implprefix`` is given implementation functions - 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() - 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, - ) - self._implprefix = implprefix - self._inner_hookexec = lambda hook, methods, kwargs: hook.multicall( - methods, - kwargs, - firstresult=hook.spec.opts.get("firstresult") if hook.spec else False, - ) - - def _hookexec(self, hook, methods, kwargs): - # called from all hookcaller instances. - # enable_tracing will set its own wrapping function at self._inner_hookexec - 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. """ - 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._name2pluginplugin_name = plugin - - # register matching hook implementations of the plugin - self._plugin2hookcallersplugin = 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) - 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 - # TODO: remove when we drop implprefix in 1.0 - elif res is None and self._implprefix and name.startswith(self._implprefix): - _warn_for_function( - DeprecationWarning( - "The `implprefix` system is deprecated please decorate " - "this function using an instance of HookimplMarker." - ), - method, - ) - res = {} - 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._name2pluginname == None registration was blocked: ignore - if self._name2plugin.get(name): - del self._name2pluginname - - 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._name2pluginname = None - - def is_blocked(self, name): - """ return ``True`` if the given plugin name is blocked. """ - return name in self._name2plugin and self._name2pluginname 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)
View file
_service:tar_scm:pluggy-0.13.1.tar.gz/testing/test_deprecations.py
Deleted
@@ -1,54 +0,0 @@ -""" -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())
View file
_service:tar_scm:pluggy-1.0.0.tar.gz/.github
Added
+(directory)
View file
_service:tar_scm:pluggy-1.0.0.tar.gz/.github/workflows
Added
+(directory)
View file
_service:tar_scm:pluggy-1.0.0.tar.gz/.github/workflows/main.yml
Added
@@ -0,0 +1,148 @@ +name: main + +on: + push: + branches: + - main + tags: + - "*" + + pull_request: + branches: + - main + +jobs: + build: + runs-on: ${{ matrix.os }} + + strategy: + fail-fast: false + matrix: + name: + "windows-py36", + "windows-py39", + "windows-pypy3", + + "ubuntu-py36", + "ubuntu-py36-pytestmain", + "ubuntu-py37", + "ubuntu-py38", + "ubuntu-py39", + "ubuntu-pypy3", + "ubuntu-benchmark", + + "linting", + "docs", + + + include: + - name: "windows-py36" + python: "3.6" + os: windows-latest + tox_env: "py36" + - name: "windows-py39" + python: "3.9" + os: windows-latest + tox_env: "py39" + - name: "windows-pypy3" + python: "pypy3" + os: windows-latest + tox_env: "pypy3" + - name: "ubuntu-py36" + python: "3.6" + os: ubuntu-latest + tox_env: "py36" + use_coverage: true + - name: "ubuntu-py36-pytestmain" + python: "3.6" + os: ubuntu-latest + tox_env: "py36-pytestmain" + use_coverage: true + - name: "ubuntu-py37" + python: "3.7" + os: ubuntu-latest + tox_env: "py37" + use_coverage: true + - name: "ubuntu-py38" + python: "3.8" + os: ubuntu-latest + tox_env: "py38" + use_coverage: true + - name: "ubuntu-py39" + python: "3.9" + os: ubuntu-latest + tox_env: "py39" + use_coverage: true + - name: "ubuntu-pypy3" + python: "pypy3" + os: ubuntu-latest + tox_env: "pypy3" + use_coverage: true + - name: "ubuntu-benchmark" + python: "3.8" + os: ubuntu-latest + tox_env: "benchmark" + - name: "linting" + python: "3.8" + os: ubuntu-latest + tox_env: "linting" + - name: "docs" + python: "3.8" + os: ubuntu-latest + tox_env: "docs" + + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: Set up Python ${{ matrix.python }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python }} + + - name: Install dependencies + run: | + python -m pip install --upgrade pip setuptools + python -m pip install tox coverage + + - name: Test without coverage + if: "! matrix.use_coverage" + run: "tox -e ${{ matrix.tox_env }}" + + - name: Test with coverage + if: "matrix.use_coverage" + run: "tox -e ${{ matrix.tox_env }}-coverage" + + - name: Upload coverage + if: matrix.use_coverage && github.repository == 'pytest-dev/pluggy' + env: + CODECOV_NAME: ${{ matrix.name }} + run: bash scripts/upload-coverage.sh -F GHA,${{ runner.os }} + + deploy: + if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags') && github.repository == 'pytest-dev/pluggy' + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - uses: actions/setup-python@v2 + with: + python-version: "3.8" + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install --upgrade wheel setuptools setuptools_scm + + - name: Build package + run: python setup.py sdist bdist_wheel + + - name: Publish package + uses: pypa/gh-action-pypi-publish@v1.4.1 + with: + user: __token__ + password: ${{ secrets.pypi_token }}
View file
_service:tar_scm:pluggy-0.13.1.tar.gz/.pre-commit-config.yaml -> _service:tar_scm:pluggy-1.0.0.tar.gz/.pre-commit-config.yaml
Changed
@@ -1,16 +1,14 @@ repos: - repo: https://github.com/ambv/black - rev: 19.3b0 + rev: 21.7b0 hooks: - id: black args: --safe, --quiet - language_version: python3.6 - repo: https://github.com/asottile/blacken-docs - rev: v0.5.0 + rev: v1.10.0 hooks: - id: blacken-docs - additional_dependencies: black==19.3b0 - language_version: python3.6 + additional_dependencies: black==21.7b0 - repo: https://github.com/pre-commit/pre-commit-hooks rev: v2.1.0 hooks: @@ -25,8 +23,12 @@ files: ^(CHANGELOG.rst|HOWTORELEASE.rst|README.rst|changelog/.*)$ language: python additional_dependencies: pygments, restructuredtext_lint - language_version: python3.6 - repo: https://github.com/pre-commit/pygrep-hooks - rev: v1.3.0 + rev: v1.9.0 hooks: - id: rst-backticks +- repo: https://github.com/asottile/pyupgrade + rev: v2.23.3 + hooks: + - id: pyupgrade + args: --py36-plus
View file
_service:tar_scm:pluggy-0.13.1.tar.gz/CHANGELOG.rst -> _service:tar_scm:pluggy-1.0.0.tar.gz/CHANGELOG.rst
Changed
@@ -4,6 +4,82 @@ .. towncrier release notes start +pluggy 1.0.0 (2021-08-25) +========================= + +Deprecations and Removals +------------------------- + +- `#116 <https://github.com/pytest-dev/pluggy/issues/116>`_: Remove deprecated ``implprefix`` support. + Decorate hook implementations using an instance of HookimplMarker instead. + The deprecation was announced in release ``0.7.0``. + + +- `#120 <https://github.com/pytest-dev/pluggy/issues/120>`_: Remove the deprecated ``proc`` argument to ``call_historic``. + Use ``result_callback`` instead, which has the same behavior. + The deprecation was announced in release ``0.7.0``. + + +- `#265 <https://github.com/pytest-dev/pluggy/issues/265>`_: Remove the ``_Result.result`` property. Use ``_Result.get_result()`` instead. + Note that unlike ``result``, ``get_result()`` raises the exception if the hook raised. + The deprecation was announced in release ``0.6.0``. + + +- `#267 <https://github.com/pytest-dev/pluggy/issues/267>`_: Remove official support for Python 3.4. + + +- `#272 <https://github.com/pytest-dev/pluggy/issues/272>`_: Dropped support for Python 2. + Continue to use pluggy 0.13.x for Python 2 support. + + +- `#308 <https://github.com/pytest-dev/pluggy/issues/308>`_: Remove official support for Python 3.5. + + +- `#313 <https://github.com/pytest-dev/pluggy/issues/313>`_: The internal ``pluggy.callers``, ``pluggy.manager`` and ``pluggy.hooks`` are now explicitly marked private by a ``_`` prefix (e.g. ``pluggy._callers``). + Only API exported by the top-level ``pluggy`` module is considered public. + + +- `#59 <https://github.com/pytest-dev/pluggy/issues/59>`_: Remove legacy ``__multicall__`` recursive hook calling system. + The deprecation was announced in release ``0.5.0``. + + + +Features +-------- + +- `#282 <https://github.com/pytest-dev/pluggy/issues/282>`_: When registering a hookimpl which is declared as ``hookwrapper=True`` but whose + function is not a generator function, a ``PluggyValidationError`` exception is + now raised. + + Previously this problem would cause an error only later, when calling the hook. + + In the unlikely case that you have a hookwrapper that *returns* a generator + instead of yielding directly, for example: + + .. code-block:: python + + def my_hook_real_implementation(arg): + print("before") + yield + print("after") + + + @hookimpl(hookwrapper=True) + def my_hook(arg): + return my_hook_implementation(arg) + + change it to use ``yield from`` instead: + + .. code-block:: python + + @hookimpl(hookwrapper=True) + def my_hook(arg): + yield from my_hook_implementation(arg) + + +- `#309 <https://github.com/pytest-dev/pluggy/issues/309>`_: Add official support for Python 3.9. + + pluggy 0.13.1 (2019-11-21) ==========================
View file
_service:tar_scm:pluggy-0.13.1.tar.gz/PKG-INFO -> _service:tar_scm:pluggy-1.0.0.tar.gz/PKG-INFO
Changed
@@ -1,460 +1,16 @@ Metadata-Version: 2.1 Name: pluggy -Version: 0.13.1 +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 license -Description: ==================================================== - 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. -
View file
_service:tar_scm:pluggy-0.13.1.tar.gz/README.rst -> _service:tar_scm:pluggy-1.0.0.tar.gz/README.rst
Changed
@@ -2,7 +2,7 @@ pluggy - A minimalist production ready plugin system ==================================================== -|pypi| |conda-forge| |versions| |travis| |appveyor| |gitter| |black| |codecov| +|pypi| |conda-forge| |versions| |github-actions| |gitter| |black| |codecov| This is the core framework used by the `pytest`_, `tox`_, and `devpi`_ projects. @@ -18,19 +18,16 @@ hookimpl = pluggy.HookimplMarker("myproject") - class MySpec(object): - """A hook specification namespace. - """ + class MySpec: + """A hook specification namespace.""" @hookspec def myhook(self, arg1, arg2): - """My special little hook that you can customize. - """ + """My special little hook that you can customize.""" - class Plugin_1(object): - """A hook implementation namespace. - """ + class Plugin_1: + """A hook implementation namespace.""" @hookimpl def myhook(self, arg1, arg2): @@ -38,9 +35,8 @@ return arg1 + arg2 - class Plugin_2(object): - """A 2nd hook implementation namespace. - """ + class Plugin_2: + """A 2nd hook implementation namespace.""" @hookimpl def myhook(self, arg1, arg2): @@ -77,11 +73,8 @@ .. |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 +.. |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
View file
_service:tar_scm:pluggy-1.0.0.tar.gz/RELEASING.rst
Added
@@ -0,0 +1,23 @@ +Release Procedure +----------------- + +#. From a clean work tree, execute:: + + tox -e release -- VERSION + + This will create the branch ready to be pushed. + +#. Open a PR targeting ``main``. + +#. All tests must pass and the PR must be approved by at least another maintainer. + +#. Publish to PyPI by pushing a tag:: + + git tag X.Y.Z release-X.Y.Z + git push git@github.com:pytest-dev/pluggy.git X.Y.Z + + The tag will trigger a new build, which will deploy to PyPI. + +#. Make sure it is `available on PyPI <https://pypi.org/project/pluggy>`_. + +#. Merge the PR into ``main``, either manually or using GitHub's web interface.
View file
_service:tar_scm:pluggy-1.0.0.tar.gz/codecov.yml
Added
@@ -0,0 +1,7 @@ +coverage: + status: + project: true + patch: true + changes: true + +comment: off
View file
_service:tar_scm:pluggy-0.13.1.tar.gz/docs/api_reference.rst -> _service:tar_scm:pluggy-1.0.0.tar.gz/docs/api_reference.rst
Changed
@@ -8,12 +8,12 @@ :undoc-members: :show-inheritance: -.. autoclass:: pluggy.callers._Result -.. automethod:: pluggy.callers._Result.get_result -.. automethod:: pluggy.callers._Result.force_result +.. autoclass:: pluggy._callers._Result +.. automethod:: pluggy._callers._Result.get_result +.. automethod:: pluggy._callers._Result.force_result -.. autoclass:: pluggy.hooks._HookCaller -.. automethod:: pluggy.hooks._HookCaller.call_extra -.. automethod:: pluggy.hooks._HookCaller.call_historic +.. autoclass:: pluggy._hooks._HookCaller +.. automethod:: pluggy._hooks._HookCaller.call_extra +.. automethod:: pluggy._hooks._HookCaller.call_historic -.. autoclass:: pluggy.hooks._HookRelay +.. autoclass:: pluggy._hooks._HookRelay
View file
_service:tar_scm:pluggy-0.13.1.tar.gz/docs/conf.py -> _service:tar_scm:pluggy-1.0.0.tar.gz/docs/conf.py
Changed
@@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import sys if sys.version_info >= (3, 8): @@ -26,12 +25,12 @@ # General information about the project. project = "pluggy" -copyright = u"2016, Holger Krekel" +copyright = "2016, Holger Krekel" author = "Holger Krekel" release = metadata.version(project) # The short X.Y version. -version = u".".join(release.split("."):2) +version = ".".join(release.split("."):2) language = None @@ -47,7 +46,6 @@ "github_button": "true", "github_banner": "true", "github_type": "star", - "travis_button": "true", "badge_branch": "master", "page_width": "1080px", "fixed_sidebar": "false", @@ -59,7 +57,7 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). -man_pages = (master_doc, "pluggy", u"pluggy Documentation", author, 1) +man_pages = (master_doc, "pluggy", "pluggy Documentation", author, 1) # -- Options for Texinfo output ------------------------------------------- @@ -71,7 +69,7 @@ ( master_doc, "pluggy", - u"pluggy Documentation", + "pluggy Documentation", author, "pluggy", "One line description of project.",
View file
_service:tar_scm:pluggy-0.13.1.tar.gz/docs/examples/eggsample-spam/eggsample_spam.py -> _service:tar_scm:pluggy-1.0.0.tar.gz/docs/examples/eggsample-spam/eggsample_spam.py
Changed
@@ -19,4 +19,4 @@ except KeyError: pass condiments"spam sauce" = 42 - return f"Now this is what I call a condiments tray!" + return "Now this is what I call a condiments tray!"
View file
_service:tar_scm:pluggy-0.13.1.tar.gz/docs/examples/toy-example.py -> _service:tar_scm:pluggy-1.0.0.tar.gz/docs/examples/toy-example.py
Changed
@@ -4,7 +4,7 @@ hookimpl = pluggy.HookimplMarker("myproject") -class MySpec(object): +class MySpec: """A hook specification namespace.""" @hookspec @@ -12,7 +12,7 @@ """My special little hook that you can customize.""" -class Plugin_1(object): +class Plugin_1: """A hook implementation namespace.""" @hookimpl @@ -21,7 +21,7 @@ return arg1 + arg2 -class Plugin_2(object): +class Plugin_2: """A 2nd hook implementation namespace.""" @hookimpl
View file
_service:tar_scm:pluggy-0.13.1.tar.gz/docs/index.rst -> _service:tar_scm:pluggy-1.0.0.tar.gz/docs/index.rst
Changed
@@ -25,7 +25,7 @@ `method overriding <https://en.wikipedia.org/wiki/Method_overriding>`_ (e.g. Jinja2) or `monkey patching <https://en.wikipedia.org/wiki/Monkey_patch>`_ (e.g. gevent -or for :std:doc:`writing tests <pytest:monkeypatch>`). +or for :std:doc:`writing tests <pytest:how-to/monkeypatch>`). These strategies become problematic though when several parties want to participate in the modification of the same program. Therefore ``pluggy`` does not rely on these mechanisms to enable a more structured approach and @@ -268,6 +268,40 @@ return config +.. _specname: + +Hookspec name matching +^^^^^^^^^^^^^^^^^^^^^^ + +During plugin :ref:`registration <registration>`, pluggy attempts to match each +hook implementation declared by the *plugin* to a hook +:ref:`specification <specs>` in the *host* program with the **same name** as +the function being decorated by ``@hookimpl`` (e.g. ``setup_project`` in the +example above). Note: there is *no* strict requirement that each *hookimpl* +has a corresponding *hookspec* (see +:ref:`enforcing spec validation <enforcing>`). + +*new in version 0.13.2:* + +To override the default behavior, a *hookimpl* may also be matched to a +*hookspec* in the *host* program with a non-matching function name by using +the ``specname`` option. Continuing the example above, the *hookimpl* function +does not need to be named ``setup_project``, but if the argument +``specname="setup_project"`` is provided to the ``hookimpl`` decorator, it will +be matched and checked against the ``setup_project`` hookspec: + +.. code-block:: python + + @hookimpl(specname="setup_project") + def any_plugin_function(config, args): + """This hook is used to process the initial config + and possibly input arguments. + """ + if args: + config.process_args(args) + + return config + Call time order ^^^^^^^^^^^^^^^ By default hooks are :ref:`called <calling>` in LIFO registered order, however, @@ -285,17 +319,15 @@ @hookimpl(trylast=True) def setup_project(config, args): - """Default implementation. - """ + """Default implementation.""" if args: config.process_args(args) return config - class SomeOtherPlugin(object): - """Some other plugin defining the same hook. - """ + class SomeOtherPlugin: + """Some other plugin defining the same hook.""" @hookimpl(tryfirst=True) def setup_project(self, config, args): @@ -315,7 +347,7 @@ # load a plugin defined on a class pm.register(SomeOtherPlugin()) -For another example see the `hook function ordering`_ section of the +For another example see the :ref:`pytest:plugin-hookorder` section of the ``pytest`` docs. .. note:: @@ -361,17 +393,17 @@ if config.use_defaults: outcome.force_result(defaults) -The generator is :py:meth:`sent <python:generator.send>` a :py:class:`pluggy.callers._Result` object which can +The generator is :py:meth:`sent <python:generator.send>` a :py:class:`pluggy._callers._Result` object which can be assigned in the ``yield`` expression and used to override or inspect the final result(s) returned back to the caller using the -:py:meth:`~pluggy.callers._Result.force_result` or -:py:meth:`~pluggy.callers._Result.get_result` methods. +:py:meth:`~pluggy._callers._Result.force_result` or +:py:meth:`~pluggy._callers._Result.get_result` methods. .. note:: Hook wrappers can **not** return results (as per generator function semantics); they can only modify them using the ``_Result`` API. -Also see the `hookwrapper`_ section in the ``pytest`` docs. +Also see the :ref:`pytest:hookwrapper` section in the ``pytest`` docs. .. _specs: @@ -423,6 +455,8 @@ which defines ``hookspec`` decorated functions as in the case of ``pytest``'s `hookspec module`_ +.. _enforcing: + Enforcing spec validation ^^^^^^^^^^^^^^^^^^^^^^^^^ By default there is no strict requirement that each *hookimpl* has @@ -487,7 +521,7 @@ This can be useful for optimizing a call loop for which you are only interested in a single core *hookimpl*. An example is the -`pytest_cmdline_main`_ central routine of ``pytest``. +:func:`~_pytest.hookspec.pytest_cmdline_main` central routine of ``pytest``. Note that all ``hookwrappers`` are still invoked with the first result. Also see the :ref:`pytest:firstresult` section in the ``pytest`` docs. @@ -497,7 +531,7 @@ Historic hooks ^^^^^^^^^^^^^^ You can mark a *hookspec* as being *historic* meaning that the hook -can be called with :py:meth:`~pluggy.hooks._HookCaller.call_historic()` **before** +can be called with :py:meth:`~pluggy._hooks._HookCaller.call_historic()` **before** having been registered: .. code-block:: python @@ -553,6 +587,7 @@ This allows for multiple plugin managers from multiple projects to define hooks alongside each other. +.. _registration: Registration ------------ @@ -574,7 +609,7 @@ Loading ``setuptools`` entry points ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ You can automatically load plugins registered through -:ref:`setuptools entry points <setuptools:entry points>` +:ref:`setuptools entry points <setuptools:entry_points>` with the :py:meth:`~pluggy.PluginManager.load_setuptools_entrypoints()` method. @@ -618,13 +653,13 @@ to override function calls made at certain points throughout a program. A particular *hook* is invoked by calling an instance of -a :py:class:`pluggy.hooks._HookCaller` which in turn *loops* through the +a :py:class:`pluggy._hooks._HookCaller` which in turn *loops* through the ``1:N`` registered *hookimpls* and calls them in sequence. Every :py:class:`~pluggy.PluginManager` has a ``hook`` attribute -which is an instance of this :py:class:`pluggy.hooks._HookRelay`. -The :py:class:`~pluggy.hooks._HookRelay` itself contains references -(by hook name) to each registered *hookimpl*'s :py:class:`~pluggy.hooks._HookCaller` instance. +which is an instance of this :py:class:`pluggy._hooks._HookRelay`. +The :py:class:`~pluggy._hooks._HookRelay` itself contains references +(by hook name) to each registered *hookimpl*'s :py:class:`~pluggy._hooks._HookCaller` instance. More practically you call a *hook* like so: @@ -656,27 +691,24 @@ hookimpl = HookimplMarker("myproject") - class Plugin1(object): + class Plugin1: @hookimpl def myhook(self, args): - """Default implementation. - """ + """Default implementation.""" return 1 - class Plugin2(object): + class Plugin2: @hookimpl def myhook(self, args): - """Default implementation. - """ + """Default implementation.""" return 2 - class Plugin3(object): + class Plugin3: @hookimpl def myhook(self, args): - """Default implementation. - """ + """Default implementation.""" return 3 @@ -714,19 +746,19 @@ hookimpl = HookimplMarker("myproject")
View file
_service:tar_scm:pluggy-0.13.1.tar.gz/pyproject.toml -> _service:tar_scm:pluggy-1.0.0.tar.gz/pyproject.toml
Changed
@@ -5,6 +5,9 @@ "wheel", +tool.setuptools_scm +write_to = "src/pluggy/_version.py" + tool.towncrier package = "pluggy" package_dir = "src/pluggy"
View file
_service:tar_scm:pluggy-0.13.1.tar.gz/scripts/release.py -> _service:tar_scm:pluggy-1.0.0.tar.gz/scripts/release.py
Changed
@@ -10,16 +10,16 @@ def create_branch(version): - """Create a fresh branch from upstream/master""" + """Create a fresh branch from upstream/main""" repo = Repo.init(".") if repo.is_dirty(untracked_files=True): - raise RuntimeError(f"Repository is dirty, please commit/stash your changes.") + raise RuntimeError("Repository is dirty, please commit/stash your changes.") branch_name = f"release-{version}" - print(f"{Fore.CYAN}Create {branch_name} branch from upstream master") + print(f"{Fore.CYAN}Create {branch_name} branch from upstream main") upstream = get_upstream(repo) upstream.fetch() - release_branch = repo.create_head(branch_name, upstream.refs.master, force=True) + release_branch = repo.create_head(branch_name, upstream.refs.main, force=True) release_branch.checkout() return repo
View file
_service:tar_scm:pluggy-1.0.0.tar.gz/scripts/upload-coverage.sh
Added
@@ -0,0 +1,16 @@ +#!/usr/bin/env bash + +set -e +set -x + +if -z "$TOXENV" ; then + python -m pip install coverage +else + # Add last TOXENV to $PATH. + PATH="$PWD/.tox/${TOXENV##*,}/bin:$PATH" +fi + +python -m coverage xml +# Set --connect-timeout to work around https://github.com/curl/curl/issues/4461 +curl -S -L --connect-timeout 5 --retry 6 -s https://codecov.io/bash -o codecov-upload.sh +bash codecov-upload.sh -Z -X fix -f coverage.xml "$@"
View file
_service:tar_scm:pluggy-0.13.1.tar.gz/setup.cfg -> _service:tar_scm:pluggy-1.0.0.tar.gz/setup.cfg
Changed
@@ -2,7 +2,52 @@ universal = 1 metadata -license_file = LICENSE +name = pluggy +description = plugin and hook calling mechanisms for python +long_description = file: README.rst +long_description_content_type = text/x-rst +license = MIT +platforms = unix, linux, osx, win32 +author = Holger Krekel +author_email = holger@merlinux.eu +url = https://github.com/pytest-dev/pluggy +classifiers = + Development Status :: 6 - Mature + Intended Audience :: Developers + License :: OSI Approved :: MIT License + Operating System :: POSIX + Operating System :: Microsoft :: Windows + Operating System :: MacOS :: MacOS X + Topic :: Software Development :: Testing + Topic :: Software Development :: Libraries + Topic :: Utilities + Programming Language :: Python :: Implementation :: CPython + Programming Language :: Python :: Implementation :: PyPy + Programming Language :: Python :: 3 + Programming Language :: Python :: 3 :: Only + Programming Language :: Python :: 3.6 + Programming Language :: Python :: 3.7 + Programming Language :: Python :: 3.8 + Programming Language :: Python :: 3.9 + +options +packages = + pluggy +install_requires = + importlib-metadata>=0.12;python_version<"3.8" +python_requires = >=3.6 +package_dir = + =src +setup_requires = + setuptools-scm + +options.extras_require +dev = + pre-commit + tox +testing = + pytest + pytest-benchmark devpi:upload formats = sdist.tgz,bdist_wheel
View file
_service:tar_scm:pluggy-0.13.1.tar.gz/setup.py -> _service:tar_scm:pluggy-1.0.0.tar.gz/setup.py
Changed
@@ -1,49 +1,5 @@ from setuptools import setup -classifiers = - "Development Status :: 4 - Beta", - "Intended Audience :: Developers", - "License :: OSI Approved :: MIT License", - "Operating System :: POSIX", - "Operating System :: Microsoft :: Windows", - "Operating System :: MacOS :: MacOS X", - "Topic :: Software Development :: Testing", - "Topic :: Software Development :: Libraries", - "Topic :: Utilities", - "Programming Language :: Python :: Implementation :: CPython", - "Programming Language :: Python :: Implementation :: PyPy", - + - ("Programming Language :: Python :: %s" % x) - for x in "2 2.7 3 3.4 3.5 3.6 3.7 3.8".split() - - -with open("README.rst", "rb") as fd: - long_description = fd.read().decode("utf-8") - -with open("CHANGELOG.rst", "rb") as fd: - long_description += "\n\n" + fd.read().decode("utf-8") - - -def main(): - setup( - name="pluggy", - description="plugin and hook calling mechanisms for python", - long_description=long_description, - use_scm_version={"write_to": "src/pluggy/_version.py"}, - setup_requires="setuptools-scm", - license="MIT license", - platforms="unix", "linux", "osx", "win32", - author="Holger Krekel", - author_email="holger@merlinux.eu", - url="https://github.com/pytest-dev/pluggy", - python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*", - install_requires='importlib-metadata>=0.12;python_version<"3.8"', - extras_require={"dev": "pre-commit", "tox"}, - classifiers=classifiers, - packages="pluggy", - package_dir={"": "src"}, - ) - if __name__ == "__main__": - main() + setup(use_scm_version={"write_to": "src/pluggy/_version.py"})
View file
_service:tar_scm:pluggy-0.13.1.tar.gz/src/pluggy.egg-info/PKG-INFO -> _service:tar_scm:pluggy-1.0.0.tar.gz/src/pluggy.egg-info/PKG-INFO
Changed
@@ -1,460 +1,16 @@ Metadata-Version: 2.1 Name: pluggy -Version: 0.13.1 +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 license -Description: ==================================================== - 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. -
View file
_service:tar_scm:pluggy-0.13.1.tar.gz/src/pluggy.egg-info/SOURCES.txt -> _service:tar_scm:pluggy-1.0.0.tar.gz/src/pluggy.egg-info/SOURCES.txt
Changed
@@ -1,17 +1,17 @@ .coveragerc .gitignore .pre-commit-config.yaml -.travis.yml CHANGELOG.rst -HOWTORELEASE.rst LICENSE MANIFEST.in README.rst -appveyor.yml +RELEASING.rst +codecov.yml pyproject.toml setup.cfg setup.py tox.ini +.github/workflows/main.yml changelog/README.rst changelog/_template.rst docs/api_reference.rst @@ -28,12 +28,14 @@ docs/examples/eggsample/eggsample/host.py docs/examples/eggsample/eggsample/lib.py scripts/release.py +scripts/upload-coverage.sh src/pluggy/__init__.py +src/pluggy/_callers.py +src/pluggy/_hooks.py +src/pluggy/_manager.py +src/pluggy/_result.py src/pluggy/_tracing.py src/pluggy/_version.py -src/pluggy/callers.py -src/pluggy/hooks.py -src/pluggy/manager.py src/pluggy.egg-info/PKG-INFO src/pluggy.egg-info/SOURCES.txt src/pluggy.egg-info/dependency_links.txt @@ -41,7 +43,6 @@ src/pluggy.egg-info/top_level.txt testing/benchmark.py testing/conftest.py -testing/test_deprecations.py testing/test_details.py testing/test_helpers.py testing/test_hookcaller.py
View file
_service:tar_scm:pluggy-0.13.1.tar.gz/src/pluggy.egg-info/requires.txt -> _service:tar_scm:pluggy-1.0.0.tar.gz/src/pluggy.egg-info/requires.txt
Changed
@@ -5,3 +5,7 @@ dev pre-commit tox + +testing +pytest +pytest-benchmark
View file
_service:tar_scm:pluggy-0.13.1.tar.gz/src/pluggy/__init__.py -> _service:tar_scm:pluggy-1.0.0.tar.gz/src/pluggy/__init__.py
Changed
@@ -13,6 +13,6 @@ "HookimplMarker", -from .manager import PluginManager, PluginValidationError -from .callers import HookCallError -from .hooks import HookspecMarker, HookimplMarker +from ._manager import PluginManager, PluginValidationError +from ._callers import HookCallError +from ._hooks import HookspecMarker, HookimplMarker
View file
_service:tar_scm:pluggy-1.0.0.tar.gz/src/pluggy/_callers.py
Added
@@ -0,0 +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_kwargsargname 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(results0 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()
View file
_service:tar_scm:pluggy-1.0.0.tar.gz/src/pluggy/_hooks.py
Added
@@ -0,0 +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(argsindex:) + 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 args0 in implicit_names + ): + args = args1: + + 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):
View file
_service:tar_scm:pluggy-1.0.0.tar.gz/src/pluggy/_manager.py
Added
@@ -0,0 +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._name2pluginplugin_name = plugin + + # register matching hook implementations of the plugin + self._plugin2hookcallersplugin = 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._name2pluginname == None registration was blocked: ignore + if self._name2plugin.get(name): + del self._name2pluginname + + 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._name2pluginname = None + + def is_blocked(self, name): + """return ``True`` if the given plugin name is blocked.""" + return name in self._name2plugin and self._name2pluginname 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>`.
View file
_service:tar_scm:pluggy-1.0.0.tar.gz/src/pluggy/_result.py
Added
@@ -0,0 +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 ex1.with_traceback(ex2)
View file
_service:tar_scm:pluggy-0.13.1.tar.gz/src/pluggy/_tracing.py -> _service:tar_scm:pluggy-1.0.0.tar.gz/src/pluggy/_tracing.py
Changed
@@ -3,7 +3,7 @@ """ -class TagTracer(object): +class TagTracer: def __init__(self): self._tags2proc = {} self._writer = None @@ -22,10 +22,10 @@ content = " ".join(map(str, args)) indent = " " * self.indent - lines = "%s%s %s\n" % (indent, content, ":".join(tags)) + lines = "{}{} {}\n".format(indent, content, ":".join(tags)) for name, value in extra.items(): - lines.append("%s %s: %s\n" % (indent, name, value)) + lines.append(f"{indent} {name}: {value}\n") return "".join(lines) @@ -50,7 +50,7 @@ self._tags2proctags = processor -class TagTracerSub(object): +class TagTracerSub: def __init__(self, root, tags): self.root = root self.tags = tags
View file
_service:tar_scm:pluggy-0.13.1.tar.gz/src/pluggy/_version.py -> _service:tar_scm:pluggy-1.0.0.tar.gz/src/pluggy/_version.py
Changed
@@ -1,4 +1,5 @@ # coding: utf-8 # file generated by setuptools_scm # don't change, don't track in version control -version = '0.13.1' +version = '1.0.0' +version_tuple = (1, 0, 0)
View file
_service:tar_scm:pluggy-0.13.1.tar.gz/testing/benchmark.py -> _service:tar_scm:pluggy-1.0.0.tar.gz/testing/benchmark.py
Changed
@@ -2,22 +2,15 @@ Benchmarking and performance tests. """ import pytest -from pluggy import HookspecMarker, HookimplMarker -from pluggy.hooks import HookImpl -from pluggy.callers import _multicall, _legacymulticall +from pluggy import HookspecMarker, HookimplMarker, PluginManager +from pluggy._hooks import HookImpl +from pluggy._callers import _multicall + 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 @@ -38,14 +31,72 @@ 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) +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)
View file
_service:tar_scm:pluggy-0.13.1.tar.gz/testing/conftest.py -> _service:tar_scm:pluggy-1.0.0.tar.gz/testing/conftest.py
Changed
@@ -10,7 +10,7 @@ hookspec = HookspecMarker("example") - class Hooks(object): + class Hooks: @hookspec def he_method1(self, arg): return arg + 1
View file
_service:tar_scm:pluggy-0.13.1.tar.gz/testing/test_details.py -> _service:tar_scm:pluggy-1.0.0.tar.gz/testing/test_details.py
Changed
@@ -15,15 +15,15 @@ opts = {} return opts - class Plugin(object): + class Plugin: def x1meth(self): pass @hookimpl(hookwrapper=True, tryfirst=True) def x1meth2(self): - pass + yield # pragma: no cover - class Spec(object): + class Spec: @hookspec def x1meth(self): pass @@ -47,12 +47,12 @@ def test_warn_when_deprecated_specified(recwarn): warning = DeprecationWarning("foo is deprecated") - class Spec(object): + class Spec: @hookspec(warn_on_impl=warning) def foo(self): pass - class Plugin(object): + class Plugin: @hookimpl def foo(self): pass @@ -73,11 +73,11 @@ when getattr() gets called (#11). """ - class DontTouchMe(object): + class DontTouchMe: def __getattr__(self, x): raise Exception("cant touch me") - class Module(object): + class Module: pass module = Module() @@ -123,7 +123,7 @@ def test_repr(): class Plugin: @hookimpl - def myhook(): + def myhook(self): raise NotImplementedError() pm = PluginManager(hookspec.project_name) @@ -131,5 +131,5 @@ plugin = Plugin() pname = pm.register(plugin) assert repr(pm.hook.myhook._nonwrappers0) == ( - "<HookImpl plugin_name=%r, plugin=%r>" % (pname, plugin) + f"<HookImpl plugin_name={pname!r}, plugin={plugin!r}>" )
View file
_service:tar_scm:pluggy-0.13.1.tar.gz/testing/test_helpers.py -> _service:tar_scm:pluggy-1.0.0.tar.gz/testing/test_helpers.py
Changed
@@ -1,19 +1,16 @@ -from pluggy.hooks import varnames -from pluggy.manager import _formatdef - -import sys -import pytest +from pluggy._hooks import varnames +from pluggy._manager import _formatdef def test_varnames(): def f(x): i = 3 # noqa - class A(object): + class A: def f(self, y): pass - class B(object): + class B: def __call__(self, z): pass @@ -30,18 +27,18 @@ def test_varnames_class(): - class C(object): + class C: def __init__(self, x): pass - class D(object): + class D: pass - class E(object): + class E: def __init__(self, x): pass - class F(object): + class F: pass assert varnames(C) == (("x",), ()) @@ -50,22 +47,19 @@ 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 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():
View file
_service:tar_scm:pluggy-0.13.1.tar.gz/testing/test_hookcaller.py -> _service:tar_scm:pluggy-1.0.0.tar.gz/testing/test_hookcaller.py
Changed
@@ -1,7 +1,7 @@ import pytest -from pluggy import HookimplMarker, HookspecMarker -from pluggy.hooks import HookImpl +from pluggy import HookimplMarker, HookspecMarker, PluginValidationError +from pluggy._hooks import HookImpl hookspec = HookspecMarker("example") hookimpl = HookimplMarker("example") @@ -9,7 +9,7 @@ @pytest.fixture def hc(pm): - class Hooks(object): + class Hooks: @hookspec def he_method1(self, arg): pass @@ -155,7 +155,7 @@ def test_hookspec(pm): - class HookSpec(object): + class HookSpec: @hookspec() def he_myhook1(arg1): pass @@ -191,7 +191,7 @@ """Verify hook caller instances are registered by name onto the relay and can be likewise unregistered.""" - class Api(object): + class Api: @hookspec def hello(self, arg): "api hook 1" @@ -201,7 +201,7 @@ assert hasattr(hook, "hello") assert repr(hook.hello).find("hello") != -1 - class Plugin(object): + class Plugin: @hookimpl def hello(self, arg): return arg + 1 @@ -213,3 +213,60 @@ 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()
View file
_service:tar_scm:pluggy-0.13.1.tar.gz/testing/test_invocations.py -> _service:tar_scm:pluggy-1.0.0.tar.gz/testing/test_invocations.py
Changed
@@ -7,14 +7,14 @@ def test_argmismatch(pm): - class Api(object): + class Api: @hookspec def hello(self, arg): "api hook 1" pm.add_hookspecs(Api) - class Plugin(object): + class Plugin: @hookimpl def hello(self, argwrong): pass @@ -26,7 +26,7 @@ def test_only_kwargs(pm): - class Api(object): + class Api: @hookspec def hello(self, arg): "api hook 1" @@ -44,17 +44,17 @@ under the same spec. """ - class Api(object): + class Api: @hookspec def hello(self, arg1, arg2, common_arg): "api hook 1" - class Plugin1(object): + class Plugin1: @hookimpl def hello(self, arg1, common_arg): return arg1 + common_arg - class Plugin2(object): + class Plugin2: @hookimpl def hello(self, arg2, common_arg): return arg2 + common_arg @@ -68,29 +68,29 @@ def test_call_order(pm): - class Api(object): + class Api: @hookspec def hello(self, arg): "api hook 1" pm.add_hookspecs(Api) - class Plugin1(object): + class Plugin1: @hookimpl def hello(self, arg): return 1 - class Plugin2(object): + class Plugin2: @hookimpl def hello(self, arg): return 2 - class Plugin3(object): + class Plugin3: @hookimpl def hello(self, arg): return 3 - class Plugin4(object): + class Plugin4: @hookimpl(hookwrapper=True) def hello(self, arg): assert arg == 0 @@ -106,29 +106,29 @@ def test_firstresult_definition(pm): - class Api(object): + class Api: @hookspec(firstresult=True) def hello(self, arg): "api hook 1" pm.add_hookspecs(Api) - class Plugin1(object): + class Plugin1: @hookimpl def hello(self, arg): return arg + 1 - class Plugin2(object): + class Plugin2: @hookimpl def hello(self, arg): return arg - 1 - class Plugin3(object): + class Plugin3: @hookimpl def hello(self, arg): return None - class Plugin4(object): + class Plugin4: @hookimpl(hookwrapper=True) def hello(self, arg): assert arg == 3 @@ -144,22 +144,21 @@ def test_firstresult_force_result(pm): - """Verify forcing a result in a wrapper. - """ + """Verify forcing a result in a wrapper.""" - class Api(object): + class Api: @hookspec(firstresult=True) def hello(self, arg): "api hook 1" pm.add_hookspecs(Api) - class Plugin1(object): + class Plugin1: @hookimpl def hello(self, arg): return arg + 1 - class Plugin2(object): + class Plugin2: @hookimpl(hookwrapper=True) def hello(self, arg): assert arg == 3 @@ -167,7 +166,7 @@ assert outcome.get_result() == 4 outcome.force_result(0) - class Plugin3(object): + class Plugin3: @hookimpl def hello(self, arg): return None @@ -184,14 +183,14 @@ the multi-call loop returns a None value. """ - class Api(object): + class Api: @hookspec(firstresult=True) def hello(self, arg): "api hook 1" pm.add_hookspecs(Api) - class Plugin1(object): + class Plugin1: @hookimpl def hello(self, arg): return None @@ -206,7 +205,7 @@ hook the multi-call loop should return a None value. """ - class Api(object): + class Api: @hookspec(firstresult=True) def hello(self, arg): "api hook 1"
View file
_service:tar_scm:pluggy-0.13.1.tar.gz/testing/test_multicall.py -> _service:tar_scm:pluggy-1.0.0.tar.gz/testing/test_multicall.py
Changed
@@ -1,7 +1,7 @@ import pytest from pluggy import HookCallError, HookspecMarker, HookimplMarker -from pluggy.hooks import HookImpl -from pluggy.callers import _multicall, _legacymulticall +from pluggy._hooks import HookImpl +from pluggy._callers import _multicall hookspec = HookspecMarker("example") @@ -14,32 +14,7 @@ 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 + return caller("foo", hookfuncs, kwargs, firstresult) def test_keyword_args(): @@ -47,7 +22,7 @@ def f(x): return x + 1 - class A(object): + class A: @hookimpl def f(self, x, y): return x + y @@ -74,20 +49,6 @@ 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():
View file
_service:tar_scm:pluggy-0.13.1.tar.gz/testing/test_pluginmanager.py -> _service:tar_scm:pluggy-1.0.0.tar.gz/testing/test_pluginmanager.py
Changed
@@ -2,16 +2,14 @@ ``PluginManager`` unit and public API testing. """ import pytest -import types from pluggy import ( - PluginManager, PluginValidationError, HookCallError, HookimplMarker, HookspecMarker, ) -from pluggy.manager import importlib_metadata +from pluggy._manager import importlib_metadata hookspec = HookspecMarker("example") @@ -30,7 +28,7 @@ def test_pm(pm): """Basic registration with objects""" - class A(object): + class A: pass a1, a2 = A(), A() @@ -51,7 +49,7 @@ def test_has_plugin(pm): - class A(object): + class A: pass a1 = A() @@ -61,7 +59,7 @@ def test_register_dynamic_attr(he_pm): - class A(object): + class A: def __getattr__(self, name): if name0 != "_": return 42 @@ -73,7 +71,7 @@ def test_pm_name(pm): - class A(object): + class A: pass a1 = A() @@ -92,7 +90,7 @@ def test_set_blocked(pm): - class A(object): + class A: pass a1 = A() @@ -111,7 +109,7 @@ def test_register_mismatch_method(he_pm): - class hello(object): + class hello: @hookimpl def he_method_notexists(self): pass @@ -125,7 +123,7 @@ def test_register_mismatch_arg(he_pm): - class hello(object): + class hello: @hookimpl def he_method1(self, qlwkje): pass @@ -137,8 +135,21 @@ 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(object): + class MyPlugin: pass my = MyPlugin() @@ -146,7 +157,7 @@ assert my in pm.get_plugins() my2 = MyPlugin() pm.register(my2) - assert set(my, my2).issubset(pm.get_plugins()) + assert {my, my2}.issubset(pm.get_plugins()) assert pm.is_registered(my) assert pm.is_registered(my2) @@ -156,14 +167,14 @@ def test_register_unknown_hooks(pm): - class Plugin1(object): + class Plugin1: @hookimpl def he_method1(self, arg): return arg + 1 pname = pm.register(Plugin1()) - class Hooks(object): + class Hooks: @hookspec def he_method1(self, arg): pass @@ -175,7 +186,7 @@ def test_register_historic(pm): - class Hooks(object): + class Hooks: @hookspec(historic=True) def he_method1(self, arg): pass @@ -185,7 +196,7 @@ pm.hook.he_method1.call_historic(kwargs=dict(arg=1)) out = - class Plugin(object): + class Plugin: @hookimpl def he_method1(self, arg): out.append(arg) @@ -193,7 +204,7 @@ pm.register(Plugin()) assert out == 1 - class Plugin2(object): + class Plugin2: @hookimpl def he_method1(self, arg): out.append(arg * 10) @@ -219,14 +230,14 @@ else: callback = None - class Hooks(object): + class Hooks: @hookspec(historic=True) def he_method1(self, arg): pass pm.add_hookspecs(Hooks) - class Plugin1(object): + class Plugin1: @hookimpl def he_method1(self, arg): return arg * 10 @@ -236,7 +247,7 @@ he_method1 = pm.hook.he_method1 he_method1.call_historic(result_callback=callback, kwargs=dict(arg=1)) - class Plugin2(object): + class Plugin2: @hookimpl def he_method1(self, arg): return arg * 10 @@ -249,24 +260,24 @@ def test_with_callbacks_immediately_executed(pm): - class Hooks(object): + class Hooks: @hookspec(historic=True) def he_method1(self, arg): pass pm.add_hookspecs(Hooks) - class Plugin1(object): + class Plugin1: @hookimpl def he_method1(self, arg): return arg * 10
View file
_service:tar_scm:pluggy-0.13.1.tar.gz/tox.ini -> _service:tar_scm:pluggy-1.0.0.tar.gz/tox.ini
Changed
@@ -1,5 +1,5 @@ tox -envlist=linting,docs,py{27,34,35,36,37,38,py,py3},py{36,37}-pytest{master,features} +envlist=linting,docs,py{36,37,38,39,py3},py{36,37}-pytest{main} testenv commands= @@ -9,11 +9,10 @@ setenv= _PYTEST_SETUP_SKIP_PLUGGY_DEP=1 coverage: _PLUGGY_TOX_CMD=coverage run -m pytest - pytestmaster: _PYTEST_DEP=git+https://github.com/pytest-dev/pytest.git@master - pytestfeatures: _PYTEST_DEP=git+https://github.com/pytest-dev/pytest.git@features +extras=testing deps= coverage: coverage - {env:_PYTEST_DEP:pytest} + pytestmain: git+https://github.com/pytest-dev/pytest.git@main testenv:benchmark commands=pytest {posargs:testing/benchmark.py} @@ -23,7 +22,7 @@ testenv:linting skip_install = true -basepython = python3.6 +basepython = python3 deps = pre-commit commands = pre-commit run --all-files --show-diff-on-failure
Locations
Projects
Search
Status Monitor
Help
Open Build Service
OBS Manuals
API Documentation
OBS Portal
Reporting a Bug
Contact
Mailing List
Forums
Chat (IRC)
Twitter
Open Build Service (OBS)
is an
openSUSE project
.
浙ICP备2022010568号-2