mirror of https://github.com/cool-RR/PySnooper.git
Support generators
This commit is contained in:
parent
669863a65f
commit
5698d6c3e2
|
@ -149,6 +149,9 @@ On multi-threaded apps identify which thread are snooped in output::
|
||||||
@pysnooper.snoop(thread_info=True)
|
@pysnooper.snoop(thread_info=True)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
PySnooper supports decorating generators.
|
||||||
|
|
||||||
|
|
||||||
# Installation #
|
# Installation #
|
||||||
|
|
||||||
You can install **PySnooper** by:
|
You can install **PySnooper** by:
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
import abc
|
import abc
|
||||||
import os
|
import os
|
||||||
|
import inspect
|
||||||
|
|
||||||
if hasattr(abc, 'ABC'):
|
if hasattr(abc, 'ABC'):
|
||||||
ABC = abc.ABC
|
ABC = abc.ABC
|
||||||
|
@ -35,3 +36,9 @@ else:
|
||||||
(hasattr(subclass, 'open') and
|
(hasattr(subclass, 'open') and
|
||||||
'path' in subclass.__name__.lower())
|
'path' in subclass.__name__.lower())
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
iscoroutinefunction = inspect.iscoroutinefunction
|
||||||
|
except AttributeError:
|
||||||
|
iscoroutinefunction = lambda whatever: False # Lolz
|
||||||
|
|
|
@ -209,11 +209,32 @@ class Tracer:
|
||||||
self.target_codes.add(function.__code__)
|
self.target_codes.add(function.__code__)
|
||||||
|
|
||||||
@functools.wraps(function)
|
@functools.wraps(function)
|
||||||
def inner(*args, **kwargs):
|
def simple_wrapper(*args, **kwargs):
|
||||||
with self:
|
with self:
|
||||||
return function(*args, **kwargs)
|
return function(*args, **kwargs)
|
||||||
|
|
||||||
return inner
|
@functools.wraps(function)
|
||||||
|
def generator_wrapper(*args, **kwargs):
|
||||||
|
gen = function(*args, **kwargs)
|
||||||
|
method, incoming = gen.send, None
|
||||||
|
while True:
|
||||||
|
with self:
|
||||||
|
try:
|
||||||
|
outgoing = method(incoming)
|
||||||
|
except StopIteration:
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
method, incoming = gen.send, (yield outgoing)
|
||||||
|
except Exception as e:
|
||||||
|
method, incoming = gen.throw, e
|
||||||
|
|
||||||
|
if pycompat.iscoroutinefunction(function):
|
||||||
|
# return decorate(function, coroutine_wrapper)
|
||||||
|
raise NotImplementedError
|
||||||
|
elif inspect.isgeneratorfunction(function):
|
||||||
|
return generator_wrapper
|
||||||
|
else:
|
||||||
|
return simple_wrapper
|
||||||
|
|
||||||
def write(self, s):
|
def write(self, s):
|
||||||
if self.overwrite and not self._did_overwrite:
|
if self.overwrite and not self._did_overwrite:
|
||||||
|
|
|
@ -5,6 +5,7 @@ import io
|
||||||
import textwrap
|
import textwrap
|
||||||
import threading
|
import threading
|
||||||
import types
|
import types
|
||||||
|
import sys
|
||||||
|
|
||||||
from pysnooper.utils import truncate
|
from pysnooper.utils import truncate
|
||||||
from python_toolbox import sys_tools, temp_file_tools
|
from python_toolbox import sys_tools, temp_file_tools
|
||||||
|
@ -1047,3 +1048,86 @@ def test_indentation():
|
||||||
def test_exception():
|
def test_exception():
|
||||||
from .samples import exception
|
from .samples import exception
|
||||||
assert_sample_output(exception)
|
assert_sample_output(exception)
|
||||||
|
|
||||||
|
|
||||||
|
def test_generator():
|
||||||
|
string_io = io.StringIO()
|
||||||
|
original_tracer = sys.gettrace()
|
||||||
|
original_tracer_active = lambda: (sys.gettrace() is original_tracer)
|
||||||
|
|
||||||
|
|
||||||
|
@pysnooper.snoop(string_io)
|
||||||
|
def f(x1):
|
||||||
|
assert not original_tracer_active()
|
||||||
|
x2 = (yield x1)
|
||||||
|
assert not original_tracer_active()
|
||||||
|
x3 = 'foo'
|
||||||
|
assert not original_tracer_active()
|
||||||
|
x4 = (yield 2)
|
||||||
|
assert not original_tracer_active()
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
assert original_tracer_active()
|
||||||
|
generator = f(0)
|
||||||
|
assert original_tracer_active()
|
||||||
|
first_item = next(generator)
|
||||||
|
assert original_tracer_active()
|
||||||
|
assert first_item == 0
|
||||||
|
second_item = generator.send('blabla')
|
||||||
|
assert original_tracer_active()
|
||||||
|
assert second_item == 2
|
||||||
|
with pytest.raises(StopIteration) as exc_info:
|
||||||
|
generator.send('looloo')
|
||||||
|
assert original_tracer_active()
|
||||||
|
|
||||||
|
output = string_io.getvalue()
|
||||||
|
assert_output(
|
||||||
|
output,
|
||||||
|
(
|
||||||
|
VariableEntry('x1', '0'),
|
||||||
|
VariableEntry(),
|
||||||
|
CallEntry(),
|
||||||
|
LineEntry(),
|
||||||
|
VariableEntry(),
|
||||||
|
VariableEntry(),
|
||||||
|
LineEntry(),
|
||||||
|
ReturnEntry(),
|
||||||
|
ReturnValueEntry('0'),
|
||||||
|
|
||||||
|
# Pause and resume:
|
||||||
|
|
||||||
|
VariableEntry('x1', '0'),
|
||||||
|
VariableEntry(),
|
||||||
|
VariableEntry(),
|
||||||
|
VariableEntry(),
|
||||||
|
CallEntry(),
|
||||||
|
VariableEntry('x2', "'blabla'"),
|
||||||
|
LineEntry(),
|
||||||
|
LineEntry(),
|
||||||
|
VariableEntry('x3', "'foo'"),
|
||||||
|
LineEntry(),
|
||||||
|
LineEntry(),
|
||||||
|
ReturnEntry(),
|
||||||
|
ReturnValueEntry('2'),
|
||||||
|
|
||||||
|
# Pause and resume:
|
||||||
|
|
||||||
|
VariableEntry('x1', '0'),
|
||||||
|
VariableEntry(),
|
||||||
|
VariableEntry(),
|
||||||
|
VariableEntry(),
|
||||||
|
VariableEntry(),
|
||||||
|
VariableEntry(),
|
||||||
|
CallEntry(),
|
||||||
|
VariableEntry('x4', "'looloo'"),
|
||||||
|
LineEntry(),
|
||||||
|
LineEntry(),
|
||||||
|
ReturnEntry(),
|
||||||
|
ReturnValueEntry(None),
|
||||||
|
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue