Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: pytest-dev/pluggy
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 0.12.0
Choose a base ref
...
head repository: pytest-dev/pluggy
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 0.13.0
Choose a head ref

Commits on May 27, 2019

  1. Merge pull request #216 from asottile/release-0.12.0

    Preparing release 0.12.0
    asottile authored May 27, 2019
    Copy the full SHA
    57b2a6d View commit details

Commits on Jul 2, 2019

  1. Copy the full SHA
    c66077d View commit details
  2. Copy the full SHA
    763b661 View commit details
  3. Update tests matrix to reflect recent changes in pytest

    No Python 3.8 on AppVeyor yet.
    hroncok committed Jul 2, 2019
    Copy the full SHA
    1bf30d6 View commit details

Commits on Jul 3, 2019

  1. Replace importlib_metadata with importlib.metadata on Python 3.8+ (#223)

    Replace importlib_metadata with importlib.metadata on Python 3.8+
    nicoddemus authored Jul 3, 2019
    Copy the full SHA
    8aa5592 View commit details

Commits on Jul 21, 2019

  1. Copy the full SHA
    b2ffeb9 View commit details
  2. Don't confusingly reuse a variable name inside varnames()

    `defaults` is the default values e.g. `1` in `foo=1`. It's just used to
    find the offset of the kwargs, but the code reused the name which made
    me scratch my head for a minute.
    bluetech committed Jul 21, 2019
    Copy the full SHA
    92c31e1 View commit details

Commits on Jul 22, 2019

  1. Remove broken test

    The test passes invalid values to the instance (it does not takes
    functions, it takes HookImpls). The test fails, but it accidentally used
    `return` instead of `assert` so it wasn't visible.
    
    Since what is being tested is evidently unimportant and legacy, just
    remove it.
    bluetech committed Jul 22, 2019
    Copy the full SHA
    0194e63 View commit details
  2. Fix call_historic() example in index.rst

    It doesn't take `**kwargs` but an argument `kwargs`.
    bluetech committed Jul 22, 2019
    Copy the full SHA
    95dd65f View commit details

Commits on Jul 24, 2019

  1. Merge pull request #224 from bluetech/fixes1

    A few minor fixes
    goodboy authored Jul 24, 2019
    Copy the full SHA
    a1c6c35 View commit details

Commits on Jul 26, 2019

  1. Inline _TracedHookExecution

    The way _TracedHookExecution is implemented, it takes the PluginManager
    instance, which creates a cyclic dependency between the manager.py and
    _tracing.py modules. It also mutates internal variables of
    PluginManager. This makes the code harder to understand. Just inlining
    it makes things mostly straightforward.
    
    This commit also removes an assert which prevented a trace from being
    added if there is already an active one. I don't see any reason why it
    was done; seems like a legitimate thing to do and should work just fine.
    bluetech committed Jul 26, 2019
    Copy the full SHA
    69c3255 View commit details
  2. Remove TagTracerSub.setmyprocessor

    This function is undocumented and unused internally.
    
    pytest used to use it in a test case, but hasn't done so since 2010:
    pytest-dev/pytest@b3628da#diff-5fd183e022c6cb9ca47f6c1ffc09eadeL274
    
    A code search on GitHub only finds it inside copies of pluggy itself.
    bluetech committed Jul 26, 2019
    Copy the full SHA
    92e72be View commit details
  3. Mark a few things on TagTracer private

    They are only directly used internally and not documented,
    
    Makes it easier to see what is exposed.
    bluetech committed Jul 26, 2019
    Copy the full SHA
    a05bdd5 View commit details
  4. Remove unhelpful _HookRelay._trace indirection

    It was only used as a kind of namespacing, but it makes things harder to
    follow. Since TagTracerSub doesn't have any side-effects, avoid storing
    it and make its use entirely localized.
    
    pytest, devpi and tox don't use PluginManager.hook._trace.
    bluetech committed Jul 26, 2019
    Copy the full SHA
    1f8fdd8 View commit details
  5. Reduce scope of except to avoid masking exceptions

    If a KeyError is raised by the user-supplied processor function, it
    should propagate.
    bluetech committed Jul 26, 2019
    Copy the full SHA
    f0535d2 View commit details
  6. Improve name tag2proc -> tags2proc

    The key is a "path" (tuple) of tags, not a single tag.
    bluetech committed Jul 26, 2019
    Copy the full SHA
    1be7c25 View commit details

Commits on Aug 17, 2019

  1. Do the string joining already inside _format_message()

    This is a bit more readable.
    bluetech committed Aug 17, 2019
    Copy the full SHA
    8900b4c View commit details

Commits on Aug 22, 2019

  1. Copy the full SHA
    2ef0090 View commit details
  2. test_pluginmanager: use pluggy.manager.metadata

    This has the version switch in a central place only once.
    
    Ref: https://github.com/pytest-dev/pluggy/pull/223/files#r315505111
    blueyed committed Aug 22, 2019
    Copy the full SHA
    12133da View commit details
  3. Merge pull request #229 from blueyed/tox-py

    tox.ini: make "tox -e py37" etc work
    nicoddemus authored Aug 22, 2019
    Copy the full SHA
    14e9d85 View commit details

Commits on Aug 29, 2019

  1. Copy the full SHA
    4497278 View commit details

Commits on Aug 30, 2019

  1. Merge pull request #226 from bluetech/simplify-tracing

    Code-level simplifications to tracing.
    goodboy authored Aug 30, 2019
    Copy the full SHA
    e40a877 View commit details

Commits on Sep 3, 2019

  1. Copy the full SHA
    c8872e2 View commit details
  2. Fix simple typo: specfication -> specification (#233)

    Fix simple typo: specfication -> specification
    nicoddemus authored Sep 3, 2019
    Copy the full SHA
    5b7aba1 View commit details

Commits on Sep 10, 2019

  1. Preparing release 0.13.0

    asottile committed Sep 10, 2019
    Copy the full SHA
    93a6b83 View commit details
Showing with 93 additions and 106 deletions.
  1. +12 −12 .travis.yml
  2. +10 −0 CHANGELOG.rst
  3. +6 −7 appveyor.yml
  4. +7 −2 docs/conf.py
  5. +3 −1 docs/index.rst
  6. +1 −1 setup.py
  7. +14 −35 src/pluggy/_tracing.py
  8. +6 −9 src/pluggy/hooks.py
  9. +22 −4 src/pluggy/manager.py
  10. +1 −10 testing/test_multicall.py
  11. +2 −1 testing/test_pluginmanager.py
  12. +4 −19 testing/test_tracer.py
  13. +5 −5 tox.ini
24 changes: 12 additions & 12 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -17,31 +17,31 @@ jobs:
- python: '3.6'
env: TOXENV=docs
- python: '2.7'
env: TOXENV=py27-pytestrelease-coverage
env: TOXENV=py27-coverage
- python: '3.4'
env: TOXENV=py34-pytestrelease-coverage
env: TOXENV=py34-coverage
- python: '3.5'
env: TOXENV=py35-pytestrelease-coverage
env: TOXENV=py35-coverage
- python: '3.6'
env: TOXENV=py36-pytestrelease-coverage
env: TOXENV=py36-coverage
- python: 'pypy2.7-6.0'
env: TOXENV=pypy-pytestrelease-coverage
env: TOXENV=pypy-coverage
- python: 'pypy3.5-6.0'
env: TOXENV=pypy3-pytestrelease-coverage
env: TOXENV=pypy3-coverage
- python: '3.7'
env: TOXENV=py37-pytestrelease-coverage
env: TOXENV=py37-coverage
- python: '3.8-dev'
env: TOXENV=py38-pytestrelease-coverage
- python: '2.7'
env: TOXENV=py27-pytestmaster-coverage
- python: '2.7'
env: TOXENV=py27-pytestfeatures-coverage
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'
10 changes: 10 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -4,6 +4,16 @@ Changelog

.. towncrier release notes start
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)
==========================

13 changes: 6 additions & 7 deletions appveyor.yml
Original file line number Diff line number Diff line change
@@ -3,13 +3,12 @@ environment:
# note: please use "tox --listenvs" to populate the build matrix below
- TOXENV: "linting"
- TOXENV: "docs"
- TOXENV: "py27-pytestrelease"
- TOXENV: "py34-pytestrelease"
- TOXENV: "py35-pytestrelease"
- TOXENV: "py36-pytestrelease"
- TOXENV: "pypy-pytestrelease"
- TOXENV: "py27-pytestmaster"
- TOXENV: "py27-pytestfeatures"
- TOXENV: "py27"
- TOXENV: "py34"
- TOXENV: "py35"
- TOXENV: "py36"
- TOXENV: "py37"
- TOXENV: "pypy"
- TOXENV: "py36-pytestmaster"
- TOXENV: "py36-pytestfeatures"

9 changes: 7 additions & 2 deletions docs/conf.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# -*- coding: utf-8 -*-
import importlib_metadata
import sys

if sys.version_info >= (3, 8):
from importlib import metadata
else:
import importlib_metadata as metadata


extensions = [
@@ -24,7 +29,7 @@
copyright = u"2016, Holger Krekel"
author = "Holger Krekel"

release = importlib_metadata.version(project)
release = metadata.version(project)
# The short X.Y version.
version = u".".join(release.split(".")[:2])

4 changes: 3 additions & 1 deletion docs/index.rst
Original file line number Diff line number Diff line change
@@ -777,7 +777,9 @@ using the :py:meth:`pluggy._HookCaller.call_historic()` method:
# call with history; no results returned
pm.hook.myhook.call_historic(config=config, args=sys.argv, result_callback=callback)
pm.hook.myhook.call_historic(
kwargs={"config": config, "args": sys.argv}, result_callback=callback
)
# ... more of our program ...
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
@@ -37,7 +37,7 @@ def main():
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"],
install_requires=['importlib-metadata>=0.12;python_version<"3.8"'],
extras_require={"dev": ["pre-commit", "tox"]},
classifiers=classifiers,
packages=["pluggy"],
49 changes: 14 additions & 35 deletions src/pluggy/_tracing.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
"""
Tracing utils
"""
from .callers import _Result


class TagTracer(object):
def __init__(self):
self._tag2proc = {}
self.writer = None
self._tags2proc = {}
self._writer = None
self.indent = 0

def get(self, name):
return TagTracerSub(self, (name,))

def format_message(self, tags, args):
def _format_message(self, tags, args):
if isinstance(args[-1], dict):
extra = args[-1]
args = args[:-1]
@@ -27,26 +26,28 @@ def format_message(self, tags, args):

for name, value in extra.items():
lines.append("%s %s: %s\n" % (indent, name, value))
return lines

def processmessage(self, tags, args):
if self.writer is not None and args:
lines = self.format_message(tags, args)
self.writer("".join(lines))
return "".join(lines)

def _processmessage(self, tags, args):
if self._writer is not None and args:
self._writer(self._format_message(tags, args))
try:
self._tag2proc[tags](tags, args)
processor = self._tags2proc[tags]
except KeyError:
pass
else:
processor(tags, args)

def setwriter(self, writer):
self.writer = writer
self._writer = writer

def setprocessor(self, tags, processor):
if isinstance(tags, str):
tags = tuple(tags.split(":"))
else:
assert isinstance(tags, tuple)
self._tag2proc[tags] = processor
self._tags2proc[tags] = processor


class TagTracerSub(object):
@@ -55,29 +56,7 @@ def __init__(self, root, tags):
self.tags = tags

def __call__(self, *args):
self.root.processmessage(self.tags, args)

def setmyprocessor(self, processor):
self.root.setprocessor(self.tags, processor)
self.root._processmessage(self.tags, args)

def get(self, name):
return self.__class__(self.root, self.tags + (name,))


class _TracedHookExecution(object):
def __init__(self, pluginmanager, before, after):
self.pluginmanager = pluginmanager
self.before = before
self.after = after
self.oldcall = pluginmanager._inner_hookexec
assert not isinstance(self.oldcall, _TracedHookExecution)
self.pluginmanager._inner_hookexec = self

def __call__(self, hook, hook_impls, kwargs):
self.before(hook.name, hook_impls, kwargs)
outcome = _Result.from_call(lambda: self.oldcall(hook, hook_impls, kwargs))
self.after(outcome, hook.name, hook_impls, kwargs)
return outcome.get_result()

def undo(self):
self.pluginmanager._inner_hookexec = self.oldcall
15 changes: 6 additions & 9 deletions src/pluggy/hooks.py
Original file line number Diff line number Diff line change
@@ -84,7 +84,7 @@ def __call__(
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 specfication.
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.
@@ -161,7 +161,7 @@ def varnames(func):
try:
func = getattr(func, "__call__", func)
except Exception:
return ()
return (), ()

try: # func MUST be a function or method here or we won't parse any args
spec = _getargspec(func)
@@ -171,9 +171,9 @@ def varnames(func):
args, defaults = tuple(spec.args), spec.defaults
if defaults:
index = -len(defaults)
args, defaults = args[:index], tuple(args[index:])
args, kwargs = args[:index], tuple(args[index:])
else:
defaults = ()
kwargs = ()

# strip any implicit instance arg
# pypy3 uses "obj" instead of "self" for default dunder methods
@@ -185,10 +185,10 @@ def varnames(func):
args = args[1:]

try:
cache["_varnames"] = args, defaults
cache["_varnames"] = args, kwargs
except TypeError:
pass
return args, defaults
return args, kwargs


class _HookRelay(object):
@@ -197,9 +197,6 @@ class _HookRelay(object):
"""

def __init__(self, trace):
self._trace = trace


class _HookCaller(object):
def __init__(self, name, hook_execute, specmodule_or_class=None, spec_opts=None):
26 changes: 22 additions & 4 deletions src/pluggy/manager.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import inspect
import sys
from . import _tracing
from .callers import _Result
from .hooks import HookImpl, _HookRelay, _HookCaller, normalize_hookimpl_opts
import warnings

import importlib_metadata
if sys.version_info >= (3, 8):
from importlib import metadata as importlib_metadata
else:
import importlib_metadata


def _warn_for_function(warning, function):
@@ -66,7 +71,7 @@ def __init__(self, project_name, implprefix=None):
self._plugin2hookcallers = {}
self._plugin_distinfo = []
self.trace = _tracing.TagTracer().get("pluginmanage")
self.hook = _HookRelay(self.trace.root.get("hook"))
self.hook = _HookRelay()
if implprefix is not None:
warnings.warn(
"Support for the `implprefix` arg is now deprecated and will "
@@ -321,11 +326,24 @@ def add_hookcall_monitoring(self, before, after):
same arguments as ``before`` but also a :py:class:`_Result`` object
which represents the result of the overall hook call.
"""
return _tracing._TracedHookExecution(self, before, after).undo
oldcall = self._inner_hookexec

def traced_hookexec(hook, hook_impls, kwargs):
before(hook.name, hook_impls, kwargs)
outcome = _Result.from_call(lambda: oldcall(hook, hook_impls, kwargs))
after(outcome, hook.name, hook_impls, kwargs)
return outcome.get_result()

self._inner_hookexec = traced_hookexec

def undo():
self._inner_hookexec = oldcall

return undo

def enable_tracing(self):
""" enable tracing of hook calls and return an undo function. """
hooktrace = self.hook._trace
hooktrace = self.trace.root.get("hook")

def before(hook_name, methods, kwargs):
hooktrace.root.indent += 1
11 changes: 1 addition & 10 deletions testing/test_multicall.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,13 @@
import pytest
from pluggy import HookCallError, HookspecMarker, HookimplMarker
from pluggy.hooks import HookImpl
from pluggy.callers import _multicall, _legacymulticall, _LegacyMultiCall
from pluggy.callers import _multicall, _legacymulticall


hookspec = HookspecMarker("example")
hookimpl = HookimplMarker("example")


def test_uses_copy_of_methods():
out = [lambda: 42]
mc = _LegacyMultiCall(out, {})
repr(mc)
out[:] = []
res = mc.execute()
return res == 42


def MC(methods, kwargs, firstresult=False):
caller = _multicall
hookfuncs = []
3 changes: 2 additions & 1 deletion testing/test_pluginmanager.py
Original file line number Diff line number Diff line change
@@ -3,14 +3,15 @@
"""
import pytest
import types
import importlib_metadata

from pluggy import (
PluginManager,
PluginValidationError,
HookCallError,
HookimplMarker,
HookspecMarker,
)
from pluggy.manager import importlib_metadata


hookspec = HookspecMarker("example")
23 changes: 4 additions & 19 deletions testing/test_tracer.py
Original file line number Diff line number Diff line change
@@ -51,11 +51,11 @@ def test_indent(rootlogger):

def test_readable_output_dictargs(rootlogger):

out = rootlogger.format_message(["test"], [1])
assert out == ["1 [test]\n"]
out = rootlogger._format_message(["test"], [1])
assert out == "1 [test]\n"

out2 = rootlogger.format_message(["test"], ["test", {"a": 1}])
assert out2 == ["test [test]\n", " a: 1\n"]
out2 = rootlogger._format_message(["test"], ["test", {"a": 1}])
assert out2 == "test [test]\n a: 1\n"


def test_setprocessor(rootlogger):
@@ -76,18 +76,3 @@ def test_setprocessor(rootlogger):
log2("seen")
tags, args = l2[0]
assert args == ("seen",)


def test_setmyprocessor(rootlogger):
log = rootlogger.get("1")
log2 = log.get("2")
out = []
log2.setmyprocessor(lambda *args: out.append(args))
log("not seen")
assert not out
log2(42)
assert len(out) == 1
tags, args = out[0]
assert "1" in tags
assert "2" in tags
assert args == (42,)
Loading