Show source path, especially when multiple files

This commit is contained in:
Ram Rachum 2019-08-09 18:09:56 +03:00
parent 7392765ada
commit 297b3cd8d7
13 changed files with 161 additions and 20 deletions

View file

@ -22,6 +22,7 @@ def main():
expected_output = '''
Source path:... Whatever
12:18:08.017782 call 17 def main():
12:18:08.018142 line 18 try:
12:18:08.018181 line 19 bar()

View file

@ -24,10 +24,12 @@ def f5():
expected_output = '''
Source path:... Whatever
21:10:42.298924 call 5 def main():
21:10:42.299158 line 6 f2()
21:10:42.299205 call 9 def f2():
21:10:42.299246 line 10 f3()
Source path:... Whatever
21:10:42.299305 call 18 def f4():
21:10:42.299348 line 19 f5()
21:10:42.299386 call 22 def f5():

View file

@ -14,8 +14,9 @@ def mul(a, b):
def main():
factorial(4)
expected_output = '''
Source path:... Whatever
Starting var:.. x = 4
20:28:17.875295 call 5 def factorial(x):
20:28:17.875509 line 6 if x <= 1:

View file

@ -16,7 +16,7 @@ from pysnooper import pycompat
from pysnooper.variables import needs_parentheses
from .utils import (assert_output, assert_sample_output, VariableEntry,
CallEntry, LineEntry, ReturnEntry, OpcodeEntry,
ReturnValueEntry, ExceptionEntry)
ReturnValueEntry, ExceptionEntry, SourcePathEntry)
from . import mini_toolbox
@ -36,6 +36,7 @@ def test_chinese():
assert_output(
output,
(
SourcePathEntry(),
CallEntry(),
LineEntry(),
VariableEntry('a'),

View file

View file

@ -0,0 +1,6 @@
# Copyright 2019 Ram Rachum and collaborators.
# This program is distributed under the MIT license.
def bar_function(y):
x = 7 * y
return x

View file

@ -0,0 +1,11 @@
# Copyright 2019 Ram Rachum and collaborators.
# This program is distributed under the MIT license.
import pysnooper
from .bar import bar_function
@pysnooper.snoop(depth=2)
def foo_function():
z = bar_function(3)
return z

View file

@ -0,0 +1,51 @@
# Copyright 2019 Ram Rachum and collaborators.
# This program is distributed under the MIT license.
import io
import textwrap
import threading
import types
import os
import sys
from pysnooper.utils import truncate
import pytest
import pysnooper
from pysnooper.variables import needs_parentheses
from ..utils import (assert_output, assert_sample_output, VariableEntry,
CallEntry, LineEntry, ReturnEntry, OpcodeEntry,
ReturnValueEntry, ExceptionEntry, SourcePathEntry)
from .. import mini_toolbox
from .multiple_files import foo
def test_multiple_files():
with mini_toolbox.OutputCapturer(stdout=False,
stderr=True) as output_capturer:
result = foo.foo_function()
assert result == 21
output = output_capturer.string_io.getvalue()
assert_output(
output,
(
SourcePathEntry(source_path_regex=r'.*foo\.py$'),
CallEntry(),
LineEntry(),
SourcePathEntry(source_path_regex=r'.*bar\.py$'),
VariableEntry(),
CallEntry(),
LineEntry(),
VariableEntry(),
LineEntry(),
ReturnEntry(),
ReturnValueEntry(),
SourcePathEntry(source_path_regex=r'.*foo\.py$'),
VariableEntry(),
LineEntry(),
ReturnEntry(),
ReturnValueEntry(),
)
)

View file

@ -15,7 +15,7 @@ import pysnooper
from pysnooper.variables import needs_parentheses
from .utils import (assert_output, assert_sample_output, VariableEntry,
CallEntry, LineEntry, ReturnEntry, OpcodeEntry,
ReturnValueEntry, ExceptionEntry)
ReturnValueEntry, ExceptionEntry, SourcePathEntry)
from . import mini_toolbox
@ -34,6 +34,7 @@ def test_string_io():
assert_output(
output,
(
SourcePathEntry(),
VariableEntry('foo', value_regex="u?'baba'"),
CallEntry('def my_function(foo):'),
LineEntry('x = 7'),
@ -63,6 +64,7 @@ def test_thread_info():
assert_output(
output,
(
SourcePathEntry(),
VariableEntry('foo', value_regex="u?'baba'"),
CallEntry('def my_function(foo):'),
LineEntry('x = 7'),
@ -105,6 +107,7 @@ def test_multi_thread_info():
assert_output(
output,
(
SourcePathEntry(),
VariableEntry('foo', value_regex="u?'baba'"),
CallEntry('def my_function(foo):',
thread_info_regex=thread_info_regex.format(
@ -174,6 +177,7 @@ def test_callable():
assert_output(
output,
(
SourcePathEntry(),
VariableEntry('foo', value_regex="u?'baba'"),
CallEntry('def my_function(foo):'),
LineEntry('x = 7'),
@ -215,6 +219,7 @@ def test_watch():
assert_output(
output,
(
SourcePathEntry(),
VariableEntry('Foo'),
VariableEntry('io.__name__', "'io'"),
CallEntry('def my_function():'),
@ -261,6 +266,7 @@ def test_watch_explode():
assert_output(
output,
(
SourcePathEntry(),
VariableEntry('Foo'),
CallEntry('def my_function():'),
LineEntry(),
@ -315,6 +321,7 @@ def test_variables_classes():
assert_output(
output,
(
SourcePathEntry(),
VariableEntry('WithSlots'),
CallEntry('def my_function():'),
LineEntry(),
@ -360,6 +367,7 @@ def test_single_watch_no_comma():
assert_output(
output,
(
SourcePathEntry(),
VariableEntry('Foo'),
CallEntry('def my_function():'),
LineEntry('foo = Foo()'),
@ -392,6 +400,7 @@ def test_long_variable():
assert_output(
output,
(
SourcePathEntry(),
CallEntry('def my_function():'),
LineEntry('foo = list(range(1000))'),
VariableEntry('foo', value_regex=regex),
@ -419,6 +428,7 @@ def test_repr_exception():
assert_output(
output,
(
SourcePathEntry(),
VariableEntry('Bad'),
CallEntry('def my_function():'),
LineEntry('bad = Bad()'),
@ -455,6 +465,7 @@ def test_depth():
assert_output(
output,
(
SourcePathEntry(),
VariableEntry(),
VariableEntry(),
CallEntry('def f1(x1):'),
@ -510,6 +521,7 @@ def test_method_and_prefix():
assert_output(
output,
(
SourcePathEntry(prefix='ZZZ'),
VariableEntry('self', prefix='ZZZ'),
VariableEntry('self.x', '2', prefix='ZZZ'),
CallEntry('def square(self):', prefix='ZZZ'),
@ -542,6 +554,7 @@ def test_file_output():
assert_output(
output,
(
SourcePathEntry(),
VariableEntry('_foo', value_regex="u?'baba'"),
CallEntry('def my_function(_foo):'),
LineEntry('x = 7'),
@ -577,6 +590,7 @@ def test_confusing_decorator_lines():
assert_output(
output,
(
SourcePathEntry(),
VariableEntry('foo', value_regex="u?'baba'"),
CallEntry('def my_function(foo):'),
LineEntry(),
@ -606,6 +620,7 @@ def test_lambda():
assert_output(
output,
(
SourcePathEntry(),
VariableEntry('x', '7'),
CallEntry(source_regex='^my_function = pysnooper.*'),
LineEntry(source_regex='^my_function = pysnooper.*'),
@ -638,6 +653,7 @@ def test_unavailable_source():
assert_output(
output,
(
SourcePathEntry(),
VariableEntry(stage='starting'),
CallEntry('SOURCE IS UNAVAILABLE'),
LineEntry('SOURCE IS UNAVAILABLE'),
@ -666,6 +682,7 @@ def test_no_overwrite_by_default():
assert_output(
shortened_output,
(
SourcePathEntry(),
VariableEntry('foo', value_regex="u?'baba'"),
CallEntry('def my_function(foo):'),
LineEntry('x = 7'),
@ -698,6 +715,7 @@ def test_overwrite():
assert_output(
output,
(
SourcePathEntry(),
VariableEntry('foo', value_regex="u?'baba'"),
CallEntry('def my_function(foo):'),
LineEntry('x = 7'),
@ -793,6 +811,7 @@ def test_with_block():
output,
(
# In first with
SourcePathEntry(),
VariableEntry('x', '2'),
VariableEntry('bar1'),
VariableEntry('bar2'),
@ -897,6 +916,7 @@ def test_with_block_depth():
assert_output(
output,
(
SourcePathEntry(),
VariableEntry(),
VariableEntry(),
VariableEntry(),
@ -948,6 +968,7 @@ def test_cellvars():
assert_output(
output,
(
SourcePathEntry(),
VariableEntry(),
VariableEntry(),
VariableEntry(),
@ -1002,6 +1023,7 @@ def test_var_order():
assert_output(
output,
(
SourcePathEntry(),
VariableEntry(),
VariableEntry(),
@ -1087,6 +1109,7 @@ def test_generator():
assert_output(
output,
(
SourcePathEntry(),
VariableEntry('x1', '0'),
VariableEntry(),
CallEntry(),
@ -1162,6 +1185,7 @@ def test_custom_repr():
assert_output(
output,
(
SourcePathEntry(),
VariableEntry('x', '10000'),
CallEntry(),
LineEntry(),
@ -1188,6 +1212,7 @@ def test_custom_repr_single():
assert_output(
output,
(
SourcePathEntry(),
VariableEntry('x', '10000'),
CallEntry(),
LineEntry(),

View file

@ -167,6 +167,31 @@ class ReturnValueEntry(_BaseValueEntry):
else:
return True
class SourcePathEntry(_BaseValueEntry):
def __init__(self, source_path=None, source_path_regex=None, prefix=''):
_BaseValueEntry.__init__(self, prefix=prefix)
if source_path is not None:
assert source_path_regex is None
self.source_path = source_path
self.source_path_regex = (None if source_path_regex is None else
re.compile(source_path_regex))
_preamble_pattern = re.compile(
r"""^Source path$"""
)
def _check_preamble(self, preamble):
return bool(self._preamble_pattern.match(preamble))
def _check_content(self, source_path):
if self.source_path is not None:
return source_path == self.source_path
elif self.source_path_regex is not None:
return self.source_path_regex.match(source_path)
else:
return True
class _BaseEventEntry(_BaseEntry):
def __init__(self, source=None, source_regex=None, thread_info=None,
@ -278,7 +303,15 @@ def assert_sample_output(module):
time_pattern = re.sub(r'\d', r'\\d', time)
def normalise(out):
return re.sub(time_pattern, time, out).strip()
out = re.sub(time_pattern, time, out).strip()
out = re.sub(
r'^( *)Source path:\.\.\. .*$',
r'\1Source path:... Whatever',
out,
flags=re.MULTILINE
)
return out
output = output_capturer.string_io.getvalue()
@ -290,3 +323,5 @@ def assert_sample_output(module):
except AssertionError:
print('\n\nActual Output:\n\n' + output) # to copy paste into expected_output
raise # show pytest diff (may need -vv flag to see in full)