Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Code-level simplifications to tracing #226

Merged
merged 8 commits into from
Aug 30, 2019
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 15 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 = {}
nicoddemus marked this conversation as resolved.
Show resolved Hide resolved
self._writer = None
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as far as i can tell the "writer" is a "print" style function we ight be able to simplify

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]
Expand All @@ -27,26 +26,29 @@ 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):
nicoddemus marked this conversation as resolved.
Show resolved Hide resolved
if self._writer is not None and args:
message = self._format_message(tags, args)
bluetech marked this conversation as resolved.
Show resolved Hide resolved
self._writer(message)
try:
self._tag2proc[tags](tags, args)
processor = self._tags2proc[tags]
goodboy marked this conversation as resolved.
Show resolved Hide resolved
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):
Expand All @@ -55,29 +57,7 @@ def __init__(self, root, tags):
self.tags = tags

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

def setmyprocessor(self, processor):
nicoddemus marked this conversation as resolved.
Show resolved Hide resolved
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):
bluetech marked this conversation as resolved.
Show resolved Hide resolved
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
3 changes: 0 additions & 3 deletions src/pluggy/hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
20 changes: 17 additions & 3 deletions src/pluggy/manager.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import inspect
import sys
from . import _tracing
from .callers import _Result
from .hooks import HookImpl, _HookRelay, _HookCaller, normalize_hookimpl_opts
import warnings

Expand Down Expand Up @@ -70,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 "
Expand Down Expand Up @@ -325,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
nicoddemus marked this conversation as resolved.
Show resolved Hide resolved

def traced_hookexec(hook, hook_impls, kwargs):
bluetech marked this conversation as resolved.
Show resolved Hide resolved
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
Expand Down
23 changes: 4 additions & 19 deletions testing/test_tracer.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand All @@ -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,)