mirror of
https://github.com/cool-RR/PySnooper.git
synced 2026-01-23 18:24:44 +00:00
Add normalize flag to remove machine-specific data
This allows for diffing between multiple PySnooper outputs.
This commit is contained in:
parent
c0bf4bd006
commit
0c5834196a
6 changed files with 301 additions and 112 deletions
|
|
@ -144,6 +144,12 @@ Start all snoop lines with a prefix, to grep for them easily:
|
|||
@pysnooper.snoop(prefix='ZZZ ')
|
||||
```
|
||||
|
||||
Remove all machine-related data (paths, timestamps, memory addresses) to compare with other traces easily:
|
||||
|
||||
```python
|
||||
@pysnooper.snoop(normalize=True)
|
||||
```
|
||||
|
||||
On multi-threaded apps identify which thread are snooped in output:
|
||||
|
||||
```python
|
||||
|
|
|
|||
|
|
@ -22,19 +22,19 @@ if pycompat.PY2:
|
|||
ipython_filename_pattern = re.compile('^<ipython-input-([0-9]+)-.*>$')
|
||||
|
||||
|
||||
def get_local_reprs(frame, watch=(), custom_repr=(), max_length=None):
|
||||
def get_local_reprs(frame, watch=(), custom_repr=(), max_length=None, normalize=False):
|
||||
code = frame.f_code
|
||||
vars_order = (code.co_varnames + code.co_cellvars + code.co_freevars +
|
||||
tuple(frame.f_locals.keys()))
|
||||
|
||||
result_items = [(key, utils.get_shortish_repr(value, custom_repr,
|
||||
max_length))
|
||||
max_length, normalize))
|
||||
for key, value in frame.f_locals.items()]
|
||||
result_items.sort(key=lambda key_value: vars_order.index(key_value[0]))
|
||||
result = collections.OrderedDict(result_items)
|
||||
|
||||
for variable in watch:
|
||||
result.update(sorted(variable.items(frame)))
|
||||
result.update(sorted(variable.items(frame, normalize)))
|
||||
return result
|
||||
|
||||
|
||||
|
|
@ -201,7 +201,7 @@ class Tracer:
|
|||
'''
|
||||
def __init__(self, output=None, watch=(), watch_explode=(), depth=1,
|
||||
prefix='', overwrite=False, thread_info=False, custom_repr=(),
|
||||
max_variable_length=100):
|
||||
max_variable_length=100, normalize=False):
|
||||
self._write = get_write_function(output, overwrite)
|
||||
|
||||
self.watch = [
|
||||
|
|
@ -226,6 +226,7 @@ class Tracer:
|
|||
self.custom_repr = custom_repr
|
||||
self.last_source_path = None
|
||||
self.max_variable_length = max_variable_length
|
||||
self.normalize = normalize
|
||||
|
||||
def __call__(self, function_or_class):
|
||||
if DISABLED:
|
||||
|
|
@ -351,9 +352,10 @@ class Tracer:
|
|||
### Finished checking whether we should trace this line. ##############
|
||||
|
||||
now = datetime_module.datetime.now().time()
|
||||
now_string = pycompat.time_isoformat(now, timespec='microseconds')
|
||||
now_string = pycompat.time_isoformat(now, timespec='microseconds') if not self.normalize else ' ' * 15
|
||||
line_no = frame.f_lineno
|
||||
source_path, source = get_path_and_source_from_frame(frame)
|
||||
source_path = source_path if not self.normalize else os.path.basename(source_path)
|
||||
if self.last_source_path != source_path:
|
||||
self.write(u'{indent}Source path:... {source_path}'.
|
||||
format(**locals()))
|
||||
|
|
@ -361,6 +363,9 @@ class Tracer:
|
|||
source_line = source[line_no - 1]
|
||||
thread_info = ""
|
||||
if self.thread_info:
|
||||
if self.normalize:
|
||||
raise NotImplementedError("normalize is not supported with "
|
||||
"thread_info")
|
||||
current_thread = threading.current_thread()
|
||||
thread_info = "{ident}-{name} ".format(
|
||||
ident=current_thread.ident, name=current_thread.getName())
|
||||
|
|
@ -372,7 +377,9 @@ class Tracer:
|
|||
self.frame_to_local_reprs[frame] = local_reprs = \
|
||||
get_local_reprs(frame,
|
||||
watch=self.watch, custom_repr=self.custom_repr,
|
||||
max_length=self.max_variable_length)
|
||||
max_length=self.max_variable_length,
|
||||
normalize=self.normalize,
|
||||
)
|
||||
|
||||
newish_string = ('Starting var:.. ' if event == 'call' else
|
||||
'New var:....... ')
|
||||
|
|
@ -437,7 +444,9 @@ class Tracer:
|
|||
if not ended_by_exception:
|
||||
return_value_repr = utils.get_shortish_repr(arg,
|
||||
custom_repr=self.custom_repr,
|
||||
max_length=self.max_variable_length)
|
||||
max_length=self.max_variable_length,
|
||||
normalize=self.normalize,
|
||||
)
|
||||
self.write('{indent}Return value:.. {return_value_repr}'.
|
||||
format(**locals()))
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
# This program is distributed under the MIT license.
|
||||
|
||||
import abc
|
||||
import re
|
||||
|
||||
import sys
|
||||
from .pycompat import ABC, string_types, collections_abc
|
||||
|
|
@ -55,13 +56,23 @@ def get_repr_function(item, custom_repr):
|
|||
return repr
|
||||
|
||||
|
||||
def get_shortish_repr(item, custom_repr=(), max_length=None):
|
||||
DEFAULT_REPR_RE = re.compile(r' at 0x[a-f0-9A-F]{4,}')
|
||||
|
||||
|
||||
def normalize_repr(item_repr):
|
||||
"""Remove memory address (0x...) from a default python repr"""
|
||||
return DEFAULT_REPR_RE.sub('', item_repr)
|
||||
|
||||
|
||||
def get_shortish_repr(item, custom_repr=(), max_length=None, normalize=False):
|
||||
repr_function = get_repr_function(item, custom_repr)
|
||||
try:
|
||||
r = repr_function(item)
|
||||
except Exception:
|
||||
r = 'REPR FAILED'
|
||||
r = r.replace('\r', '').replace('\n', '')
|
||||
if normalize:
|
||||
r = normalize_repr(r)
|
||||
if max_length:
|
||||
r = truncate(r, max_length)
|
||||
return r
|
||||
|
|
|
|||
|
|
@ -27,15 +27,15 @@ class BaseVariable(pycompat.ABC):
|
|||
else:
|
||||
self.unambiguous_source = source
|
||||
|
||||
def items(self, frame):
|
||||
def items(self, frame, normalize=False):
|
||||
try:
|
||||
main_value = eval(self.code, frame.f_globals or {}, frame.f_locals)
|
||||
except Exception:
|
||||
return ()
|
||||
return self._items(main_value)
|
||||
return self._items(main_value, normalize)
|
||||
|
||||
@abc.abstractmethod
|
||||
def _items(self, key):
|
||||
def _items(self, key, normalize=False):
|
||||
raise NotImplementedError
|
||||
|
||||
@property
|
||||
|
|
@ -51,8 +51,8 @@ class BaseVariable(pycompat.ABC):
|
|||
|
||||
|
||||
class CommonVariable(BaseVariable):
|
||||
def _items(self, main_value):
|
||||
result = [(self.source, utils.get_shortish_repr(main_value))]
|
||||
def _items(self, main_value, normalize=False):
|
||||
result = [(self.source, utils.get_shortish_repr(main_value, normalize=normalize))]
|
||||
for key in self._safe_keys(main_value):
|
||||
try:
|
||||
if key in self.exclude:
|
||||
|
|
@ -122,7 +122,7 @@ class Indices(Keys):
|
|||
|
||||
|
||||
class Exploding(BaseVariable):
|
||||
def _items(self, main_value):
|
||||
def _items(self, main_value, normalize=False):
|
||||
if isinstance(main_value, Mapping):
|
||||
cls = Keys
|
||||
elif isinstance(main_value, Sequence):
|
||||
|
|
@ -130,4 +130,4 @@ class Exploding(BaseVariable):
|
|||
else:
|
||||
cls = Attrs
|
||||
|
||||
return cls(self.source, self.exclude)._items(main_value)
|
||||
return cls(self.source, self.exclude)._items(main_value, normalize)
|
||||
|
|
|
|||
|
|
@ -159,13 +159,14 @@ def test_multi_thread_info():
|
|||
)
|
||||
|
||||
|
||||
def test_callable():
|
||||
@pytest.mark.parametrize("normalize", (True, False))
|
||||
def test_callable(normalize):
|
||||
string_io = io.StringIO()
|
||||
|
||||
def write(msg):
|
||||
string_io.write(msg)
|
||||
|
||||
@pysnooper.snoop(write)
|
||||
@pysnooper.snoop(write, normalize=normalize)
|
||||
def my_function(foo):
|
||||
x = 7
|
||||
y = 8
|
||||
|
|
@ -187,13 +188,13 @@ def test_callable():
|
|||
LineEntry('return y + x'),
|
||||
ReturnEntry('return y + x'),
|
||||
ReturnValueEntry('15'),
|
||||
)
|
||||
),
|
||||
normalize=normalize,
|
||||
)
|
||||
|
||||
|
||||
|
||||
def test_watch():
|
||||
|
||||
@pytest.mark.parametrize("normalize", (True, False))
|
||||
def test_watch(normalize):
|
||||
class Foo(object):
|
||||
def __init__(self):
|
||||
self.x = 2
|
||||
|
|
@ -205,7 +206,7 @@ def test_watch():
|
|||
'foo.x',
|
||||
'io.__name__',
|
||||
'len(foo.__dict__["x"] * "abc")',
|
||||
))
|
||||
), normalize=normalize)
|
||||
def my_function():
|
||||
foo = Foo()
|
||||
for i in range(2):
|
||||
|
|
@ -240,18 +241,19 @@ def test_watch():
|
|||
LineEntry(),
|
||||
ReturnEntry(),
|
||||
ReturnValueEntry('None')
|
||||
)
|
||||
),
|
||||
normalize=normalize,
|
||||
)
|
||||
|
||||
|
||||
def test_watch_explode():
|
||||
@pytest.mark.parametrize("normalize", (True, False))
|
||||
def test_watch_explode(normalize):
|
||||
class Foo:
|
||||
def __init__(self, x, y):
|
||||
self.x = x
|
||||
self.y = y
|
||||
|
||||
|
||||
@pysnooper.snoop(watch_explode=('_d', '_point', 'lst + []'))
|
||||
@pysnooper.snoop(watch_explode=('_d', '_point', 'lst + []'), normalize=normalize)
|
||||
def my_function():
|
||||
_d = {'a': 1, 'b': 2, 'c': 'ignore'}
|
||||
_point = Foo(x=3, y=4)
|
||||
|
|
@ -290,11 +292,13 @@ def test_watch_explode():
|
|||
VariableEntry('lst + []'),
|
||||
ReturnEntry(),
|
||||
ReturnValueEntry('None')
|
||||
)
|
||||
),
|
||||
normalize=normalize,
|
||||
)
|
||||
|
||||
|
||||
def test_variables_classes():
|
||||
@pytest.mark.parametrize("normalize", (True, False))
|
||||
def test_variables_classes(normalize):
|
||||
class WithSlots(object):
|
||||
__slots__ = ('x', 'y')
|
||||
|
||||
|
|
@ -307,7 +311,7 @@ def test_variables_classes():
|
|||
pysnooper.Attrs('_d'), # doesn't have attributes
|
||||
pysnooper.Attrs('_s'),
|
||||
pysnooper.Indices('_lst')[-3:],
|
||||
))
|
||||
), normalize=normalize)
|
||||
def my_function():
|
||||
_d = {'a': 1, 'b': 2, 'c': 'ignore'}
|
||||
_s = WithSlots()
|
||||
|
|
@ -339,13 +343,13 @@ def test_variables_classes():
|
|||
VariableEntry('_lst[999]', '999'),
|
||||
ReturnEntry(),
|
||||
ReturnValueEntry('None')
|
||||
)
|
||||
),
|
||||
normalize=normalize,
|
||||
)
|
||||
|
||||
|
||||
|
||||
def test_single_watch_no_comma():
|
||||
|
||||
@pytest.mark.parametrize("normalize", (True, False))
|
||||
def test_single_watch_no_comma(normalize):
|
||||
class Foo(object):
|
||||
def __init__(self):
|
||||
self.x = 2
|
||||
|
|
@ -353,7 +357,7 @@ def test_single_watch_no_comma():
|
|||
def square(self):
|
||||
self.x **= 2
|
||||
|
||||
@pysnooper.snoop(watch='foo')
|
||||
@pysnooper.snoop(watch='foo', normalize=normalize)
|
||||
def my_function():
|
||||
foo = Foo()
|
||||
for i in range(2):
|
||||
|
|
@ -381,12 +385,14 @@ def test_single_watch_no_comma():
|
|||
LineEntry(),
|
||||
ReturnEntry(),
|
||||
ReturnValueEntry('None')
|
||||
)
|
||||
),
|
||||
normalize=normalize,
|
||||
)
|
||||
|
||||
|
||||
def test_long_variable():
|
||||
@pysnooper.snoop()
|
||||
@pytest.mark.parametrize("normalize", (True, False))
|
||||
def test_long_variable(normalize):
|
||||
@pysnooper.snoop(normalize=normalize)
|
||||
def my_function():
|
||||
foo = list(range(1000))
|
||||
return foo
|
||||
|
|
@ -407,13 +413,14 @@ def test_long_variable():
|
|||
LineEntry(),
|
||||
ReturnEntry(),
|
||||
ReturnValueEntry(value_regex=regex)
|
||||
)
|
||||
),
|
||||
normalize=normalize,
|
||||
)
|
||||
|
||||
|
||||
|
||||
def test_long_variable_with_custom_max_variable_length():
|
||||
@pysnooper.snoop(max_variable_length=200)
|
||||
@pytest.mark.parametrize("normalize", (True, False))
|
||||
def test_long_variable_with_custom_max_variable_length(normalize):
|
||||
@pysnooper.snoop(max_variable_length=200, normalize=normalize)
|
||||
def my_function():
|
||||
foo = list(range(1000))
|
||||
return foo
|
||||
|
|
@ -434,12 +441,14 @@ def test_long_variable_with_custom_max_variable_length():
|
|||
LineEntry(),
|
||||
ReturnEntry(),
|
||||
ReturnValueEntry(value_regex=regex)
|
||||
)
|
||||
),
|
||||
normalize=normalize,
|
||||
)
|
||||
|
||||
|
||||
def test_long_variable_with_infinite_max_variable_length():
|
||||
@pysnooper.snoop(max_variable_length=None)
|
||||
@pytest.mark.parametrize("normalize", (True, False))
|
||||
def test_long_variable_with_infinite_max_variable_length(normalize):
|
||||
@pysnooper.snoop(max_variable_length=None, normalize=normalize)
|
||||
def my_function():
|
||||
foo = list(range(1000))
|
||||
return foo
|
||||
|
|
@ -460,17 +469,18 @@ def test_long_variable_with_infinite_max_variable_length():
|
|||
LineEntry(),
|
||||
ReturnEntry(),
|
||||
ReturnValueEntry(value_regex=regex)
|
||||
)
|
||||
),
|
||||
normalize=normalize,
|
||||
)
|
||||
|
||||
|
||||
|
||||
def test_repr_exception():
|
||||
@pytest.mark.parametrize("normalize", (True, False))
|
||||
def test_repr_exception(normalize):
|
||||
class Bad(object):
|
||||
def __repr__(self):
|
||||
1 / 0
|
||||
|
||||
@pysnooper.snoop()
|
||||
@pysnooper.snoop(normalize=normalize)
|
||||
def my_function():
|
||||
bad = Bad()
|
||||
|
||||
|
|
@ -489,11 +499,13 @@ def test_repr_exception():
|
|||
VariableEntry('bad', value='REPR FAILED'),
|
||||
ReturnEntry(),
|
||||
ReturnValueEntry('None')
|
||||
)
|
||||
),
|
||||
normalize=normalize,
|
||||
)
|
||||
|
||||
|
||||
def test_depth():
|
||||
@pytest.mark.parametrize("normalize", (True, False))
|
||||
def test_depth(normalize):
|
||||
string_io = io.StringIO()
|
||||
|
||||
def f4(x4):
|
||||
|
|
@ -508,7 +520,7 @@ def test_depth():
|
|||
result2 = f3(x2)
|
||||
return result2
|
||||
|
||||
@pysnooper.snoop(string_io, depth=3)
|
||||
@pysnooper.snoop(string_io, depth=3, normalize=normalize)
|
||||
def f1(x1):
|
||||
result1 = f2(x1)
|
||||
return result1
|
||||
|
|
@ -549,16 +561,18 @@ def test_depth():
|
|||
LineEntry(),
|
||||
ReturnEntry(),
|
||||
ReturnValueEntry('20'),
|
||||
)
|
||||
),
|
||||
normalize=normalize,
|
||||
)
|
||||
|
||||
|
||||
def test_method_and_prefix():
|
||||
@pytest.mark.parametrize("normalize", (True, False))
|
||||
def test_method_and_prefix(normalize):
|
||||
class Baz(object):
|
||||
def __init__(self):
|
||||
self.x = 2
|
||||
|
||||
@pysnooper.snoop(watch=('self.x',), prefix='ZZZ')
|
||||
@pysnooper.snoop(watch=('self.x',), prefix='ZZZ', normalize=normalize)
|
||||
def square(self):
|
||||
foo = 7
|
||||
self.x **= 2
|
||||
|
|
@ -587,15 +601,17 @@ def test_method_and_prefix():
|
|||
ReturnEntry(prefix='ZZZ'),
|
||||
ReturnValueEntry(prefix='ZZZ'),
|
||||
),
|
||||
prefix='ZZZ'
|
||||
prefix='ZZZ',
|
||||
normalize=normalize,
|
||||
)
|
||||
|
||||
|
||||
def test_file_output():
|
||||
@pytest.mark.parametrize("normalize", (True, False))
|
||||
def test_file_output(normalize):
|
||||
with mini_toolbox.create_temp_folder(prefix='pysnooper') as folder:
|
||||
path = folder / 'foo.log'
|
||||
|
||||
@pysnooper.snoop(path)
|
||||
@pysnooper.snoop(path, normalize=normalize)
|
||||
def my_function(_foo):
|
||||
x = 7
|
||||
y = 8
|
||||
|
|
@ -618,18 +634,20 @@ def test_file_output():
|
|||
LineEntry('return y + x'),
|
||||
ReturnEntry('return y + x'),
|
||||
ReturnValueEntry('15'),
|
||||
)
|
||||
),
|
||||
normalize=normalize,
|
||||
)
|
||||
|
||||
|
||||
def test_confusing_decorator_lines():
|
||||
@pytest.mark.parametrize("normalize", (True, False))
|
||||
def test_confusing_decorator_lines(normalize):
|
||||
string_io = io.StringIO()
|
||||
|
||||
def empty_decorator(function):
|
||||
return function
|
||||
|
||||
@empty_decorator
|
||||
@pysnooper.snoop(string_io,
|
||||
@pysnooper.snoop(string_io, normalize=normalize,
|
||||
depth=2) # Multi-line decorator for extra confusion!
|
||||
@empty_decorator
|
||||
@empty_decorator
|
||||
|
|
@ -661,13 +679,15 @@ def test_confusing_decorator_lines():
|
|||
# back in my_function
|
||||
ReturnEntry(),
|
||||
ReturnValueEntry('15'),
|
||||
)
|
||||
),
|
||||
normalize=normalize,
|
||||
)
|
||||
|
||||
|
||||
def test_lambda():
|
||||
@pytest.mark.parametrize("normalize", (True, False))
|
||||
def test_lambda(normalize):
|
||||
string_io = io.StringIO()
|
||||
my_function = pysnooper.snoop(string_io)(lambda x: x ** 2)
|
||||
my_function = pysnooper.snoop(string_io, normalize=normalize)(lambda x: x ** 2)
|
||||
result = my_function(7)
|
||||
assert result == 49
|
||||
output = string_io.getvalue()
|
||||
|
|
@ -680,7 +700,8 @@ def test_lambda():
|
|||
LineEntry(source_regex='^my_function = pysnooper.*'),
|
||||
ReturnEntry(source_regex='^my_function = pysnooper.*'),
|
||||
ReturnValueEntry('49'),
|
||||
)
|
||||
),
|
||||
normalize=normalize,
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -820,9 +841,10 @@ def test_needs_parentheses():
|
|||
assert needs_parentheses('x if z else y')
|
||||
|
||||
|
||||
def test_with_block():
|
||||
@pytest.mark.parametrize("normalize", (True, False))
|
||||
def test_with_block(normalize):
|
||||
# Testing that a single Tracer can handle many mixed uses
|
||||
snoop = pysnooper.snoop()
|
||||
snoop = pysnooper.snoop(normalize=normalize)
|
||||
|
||||
def foo(x):
|
||||
if x == 0:
|
||||
|
|
@ -940,10 +962,12 @@ def test_with_block():
|
|||
ReturnEntry('qux()'),
|
||||
ReturnValueEntry('None'),
|
||||
),
|
||||
normalize=normalize,
|
||||
)
|
||||
|
||||
|
||||
def test_with_block_depth():
|
||||
@pytest.mark.parametrize("normalize", (True, False))
|
||||
def test_with_block_depth(normalize):
|
||||
string_io = io.StringIO()
|
||||
|
||||
def f4(x4):
|
||||
|
|
@ -960,7 +984,7 @@ def test_with_block_depth():
|
|||
|
||||
def f1(x1):
|
||||
str(3)
|
||||
with pysnooper.snoop(string_io, depth=3):
|
||||
with pysnooper.snoop(string_io, depth=3, normalize=normalize):
|
||||
result1 = f2(x1)
|
||||
return result1
|
||||
|
||||
|
|
@ -974,6 +998,7 @@ def test_with_block_depth():
|
|||
VariableEntry(),
|
||||
VariableEntry(),
|
||||
VariableEntry(),
|
||||
VariableEntry(),
|
||||
LineEntry('result1 = f2(x1)'),
|
||||
|
||||
VariableEntry(),
|
||||
|
|
@ -995,10 +1020,13 @@ def test_with_block_depth():
|
|||
LineEntry(),
|
||||
ReturnEntry(),
|
||||
ReturnValueEntry('20'),
|
||||
)
|
||||
),
|
||||
normalize=normalize,
|
||||
)
|
||||
|
||||
def test_cellvars():
|
||||
|
||||
@pytest.mark.parametrize("normalize", (True, False))
|
||||
def test_cellvars(normalize):
|
||||
string_io = io.StringIO()
|
||||
|
||||
def f2(a):
|
||||
|
|
@ -1012,7 +1040,7 @@ def test_cellvars():
|
|||
return f3(a)
|
||||
|
||||
def f1(a):
|
||||
with pysnooper.snoop(string_io, depth=4):
|
||||
with pysnooper.snoop(string_io, depth=4, normalize=normalize):
|
||||
result1 = f2(a)
|
||||
return result1
|
||||
|
||||
|
|
@ -1026,6 +1054,7 @@ def test_cellvars():
|
|||
VariableEntry(),
|
||||
VariableEntry(),
|
||||
VariableEntry(),
|
||||
VariableEntry(),
|
||||
LineEntry('result1 = f2(a)'),
|
||||
|
||||
VariableEntry(),
|
||||
|
|
@ -1057,10 +1086,13 @@ def test_cellvars():
|
|||
ReturnValueEntry(),
|
||||
ReturnEntry(),
|
||||
ReturnValueEntry(),
|
||||
)
|
||||
),
|
||||
normalize=normalize,
|
||||
)
|
||||
|
||||
def test_var_order():
|
||||
|
||||
@pytest.mark.parametrize("normalize", (True, False))
|
||||
def test_var_order(normalize):
|
||||
string_io = io.StringIO()
|
||||
|
||||
def f(one, two, three, four):
|
||||
|
|
@ -1070,7 +1102,7 @@ def test_var_order():
|
|||
|
||||
five, six, seven = 5, 6, 7
|
||||
|
||||
with pysnooper.snoop(string_io, depth=2):
|
||||
with pysnooper.snoop(string_io, depth=2, normalize=normalize):
|
||||
result = f(1, 2, 3, 4)
|
||||
|
||||
output = string_io.getvalue()
|
||||
|
|
@ -1080,6 +1112,7 @@ def test_var_order():
|
|||
SourcePathEntry(),
|
||||
VariableEntry(),
|
||||
VariableEntry(),
|
||||
VariableEntry(),
|
||||
|
||||
LineEntry('result = f(1, 2, 3, 4)'),
|
||||
VariableEntry("one", "1"),
|
||||
|
|
@ -1100,7 +1133,8 @@ def test_var_order():
|
|||
VariableEntry("seven", "7"),
|
||||
ReturnEntry(),
|
||||
ReturnValueEntry(),
|
||||
)
|
||||
),
|
||||
normalize=normalize,
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -1209,7 +1243,8 @@ def test_generator():
|
|||
)
|
||||
|
||||
|
||||
def test_custom_repr():
|
||||
@pytest.mark.parametrize("normalize", (True, False))
|
||||
def test_custom_repr(normalize):
|
||||
string_io = io.StringIO()
|
||||
|
||||
def large(l):
|
||||
|
|
@ -1227,7 +1262,8 @@ def test_custom_repr():
|
|||
@pysnooper.snoop(string_io, custom_repr=(
|
||||
(large, print_list_size),
|
||||
(dict, print_dict),
|
||||
(evil_condition, lambda x: 'I am evil')))
|
||||
(evil_condition, lambda x: 'I am evil')),
|
||||
normalize=normalize,)
|
||||
def sum_to_x(x):
|
||||
l = list(range(x))
|
||||
a = {'1': 1, '2': 2}
|
||||
|
|
@ -1249,13 +1285,16 @@ def test_custom_repr():
|
|||
LineEntry(),
|
||||
ReturnEntry(),
|
||||
ReturnValueEntry('49995000'),
|
||||
)
|
||||
),
|
||||
normalize=normalize,
|
||||
)
|
||||
|
||||
def test_custom_repr_single():
|
||||
|
||||
@pytest.mark.parametrize("normalize", (True, False))
|
||||
def test_custom_repr_single(normalize):
|
||||
string_io = io.StringIO()
|
||||
|
||||
@pysnooper.snoop(string_io, custom_repr=(list, lambda l: 'foofoo!'))
|
||||
@pysnooper.snoop(string_io, custom_repr=(list, lambda l: 'foofoo!'), normalize=normalize)
|
||||
def sum_to_x(x):
|
||||
l = list(range(x))
|
||||
return 7
|
||||
|
|
@ -1274,7 +1313,8 @@ def test_custom_repr_single():
|
|||
LineEntry(),
|
||||
ReturnEntry(),
|
||||
ReturnValueEntry('7'),
|
||||
)
|
||||
),
|
||||
normalize=normalize,
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -1297,10 +1337,11 @@ def test_disable():
|
|||
assert not output
|
||||
|
||||
|
||||
def test_class():
|
||||
@pytest.mark.parametrize("normalize", (True, False))
|
||||
def test_class(normalize):
|
||||
string_io = io.StringIO()
|
||||
|
||||
@pysnooper.snoop(string_io)
|
||||
@pysnooper.snoop(string_io, normalize=normalize)
|
||||
class MyClass(object):
|
||||
def __init__(self):
|
||||
self.x = 7
|
||||
|
|
@ -1317,12 +1358,12 @@ def test_class():
|
|||
output,
|
||||
(
|
||||
SourcePathEntry(),
|
||||
VariableEntry('self', value_regex="u?.+MyClass object at"),
|
||||
VariableEntry('self', value_regex="u?.+MyClass object"),
|
||||
CallEntry('def __init__(self):'),
|
||||
LineEntry('self.x = 7'),
|
||||
ReturnEntry('self.x = 7'),
|
||||
ReturnValueEntry('None'),
|
||||
VariableEntry('self', value_regex="u?.+MyClass object at"),
|
||||
VariableEntry('self', value_regex="u?.+MyClass object"),
|
||||
VariableEntry('foo', value_regex="u?'baba'"),
|
||||
CallEntry('def my_method(self, foo):'),
|
||||
LineEntry('y = 8'),
|
||||
|
|
@ -1330,10 +1371,13 @@ def test_class():
|
|||
LineEntry('return y + self.x'),
|
||||
ReturnEntry('return y + self.x'),
|
||||
ReturnValueEntry('15'),
|
||||
)
|
||||
),
|
||||
normalize=normalize,
|
||||
)
|
||||
|
||||
def test_class_with_decorated_method():
|
||||
|
||||
@pytest.mark.parametrize("normalize", (True, False))
|
||||
def test_class_with_decorated_method(normalize):
|
||||
string_io = io.StringIO()
|
||||
|
||||
def decorator(function):
|
||||
|
|
@ -1342,7 +1386,7 @@ def test_class_with_decorated_method():
|
|||
return result
|
||||
return wrapper
|
||||
|
||||
@pysnooper.snoop(string_io)
|
||||
@pysnooper.snoop(string_io, normalize=normalize)
|
||||
class MyClass(object):
|
||||
def __init__(self):
|
||||
self.x = 7
|
||||
|
|
@ -1360,25 +1404,27 @@ def test_class_with_decorated_method():
|
|||
output,
|
||||
(
|
||||
SourcePathEntry(),
|
||||
VariableEntry('self', value_regex="u?.+MyClass object at"),
|
||||
VariableEntry('self', value_regex="u?.+MyClass object"),
|
||||
CallEntry('def __init__(self):'),
|
||||
LineEntry('self.x = 7'),
|
||||
ReturnEntry('self.x = 7'),
|
||||
ReturnValueEntry('None'),
|
||||
VariableEntry('args', value_regex=r"\(<.+>, 'baba'\)"),
|
||||
VariableEntry('kwargs', value_regex=r"\{\}"),
|
||||
VariableEntry('function', value_regex="u?.+my_method at"),
|
||||
VariableEntry('function', value_regex="u?.+my_method"),
|
||||
CallEntry('def wrapper(*args, **kwargs):'),
|
||||
LineEntry('result = function(*args, **kwargs)'),
|
||||
VariableEntry('result', '15'),
|
||||
LineEntry('return result'),
|
||||
ReturnEntry('return result'),
|
||||
ReturnValueEntry('15'),
|
||||
)
|
||||
),
|
||||
normalize=normalize,
|
||||
)
|
||||
|
||||
|
||||
def test_class_with_decorated_method_and_snoop_applied_to_method():
|
||||
@pytest.mark.parametrize("normalize", (True, False))
|
||||
def test_class_with_decorated_method_and_snoop_applied_to_method(normalize):
|
||||
string_io = io.StringIO()
|
||||
|
||||
def decorator(function):
|
||||
|
|
@ -1387,13 +1433,13 @@ def test_class_with_decorated_method_and_snoop_applied_to_method():
|
|||
return result
|
||||
return wrapper
|
||||
|
||||
@pysnooper.snoop(string_io)
|
||||
@pysnooper.snoop(string_io, normalize=normalize)
|
||||
class MyClass(object):
|
||||
def __init__(self):
|
||||
self.x = 7
|
||||
|
||||
@decorator
|
||||
@pysnooper.snoop(string_io)
|
||||
@pysnooper.snoop(string_io, normalize=normalize)
|
||||
def my_method(self, foo):
|
||||
y = 8
|
||||
return y + self.x
|
||||
|
|
@ -1406,18 +1452,18 @@ def test_class_with_decorated_method_and_snoop_applied_to_method():
|
|||
output,
|
||||
(
|
||||
SourcePathEntry(),
|
||||
VariableEntry('self', value_regex="u?.*MyClass object at"),
|
||||
VariableEntry('self', value_regex="u?.*MyClass object"),
|
||||
CallEntry('def __init__(self):'),
|
||||
LineEntry('self.x = 7'),
|
||||
ReturnEntry('self.x = 7'),
|
||||
ReturnValueEntry('None'),
|
||||
VariableEntry('args', value_regex=r"u?\(<.+>, 'baba'\)"),
|
||||
VariableEntry('kwargs', value_regex=r"u?\{\}"),
|
||||
VariableEntry('function', value_regex="u?.*my_method at"),
|
||||
VariableEntry('function', value_regex="u?.*my_method"),
|
||||
CallEntry('def wrapper(*args, **kwargs):'),
|
||||
LineEntry('result = function(*args, **kwargs)'),
|
||||
SourcePathEntry(),
|
||||
VariableEntry('self', value_regex="u?.*MyClass object at"),
|
||||
VariableEntry('self', value_regex="u?.*MyClass object"),
|
||||
VariableEntry('foo', value_regex="u?'baba'"),
|
||||
CallEntry('def my_method(self, foo):'),
|
||||
LineEntry('y = 8'),
|
||||
|
|
@ -1429,14 +1475,16 @@ def test_class_with_decorated_method_and_snoop_applied_to_method():
|
|||
LineEntry('return result'),
|
||||
ReturnEntry('return result'),
|
||||
ReturnValueEntry('15'),
|
||||
)
|
||||
),
|
||||
normalize=normalize,
|
||||
)
|
||||
|
||||
|
||||
def test_class_with_property():
|
||||
@pytest.mark.parametrize("normalize", (True, False))
|
||||
def test_class_with_property(normalize):
|
||||
string_io = io.StringIO()
|
||||
|
||||
@pysnooper.snoop(string_io)
|
||||
@pysnooper.snoop(string_io, normalize=normalize)
|
||||
class MyClass(object):
|
||||
def __init__(self):
|
||||
self._x = 0
|
||||
|
|
@ -1478,37 +1526,39 @@ def test_class_with_property():
|
|||
output,
|
||||
(
|
||||
SourcePathEntry(),
|
||||
VariableEntry('self', value_regex="u?.*MyClass object at"),
|
||||
VariableEntry('self', value_regex="u?.*MyClass object"),
|
||||
CallEntry('def __init__(self):'),
|
||||
LineEntry('self._x = 0'),
|
||||
ReturnEntry('self._x = 0'),
|
||||
ReturnValueEntry('None'),
|
||||
|
||||
# Called from getter
|
||||
VariableEntry('self', value_regex="u?.*MyClass object at"),
|
||||
VariableEntry('self', value_regex="u?.*MyClass object"),
|
||||
CallEntry('def plain_method(self):'),
|
||||
LineEntry('pass'),
|
||||
ReturnEntry('pass'),
|
||||
ReturnValueEntry('None'),
|
||||
|
||||
# Called from setter
|
||||
VariableEntry('self', value_regex="u?.*MyClass object at"),
|
||||
VariableEntry('self', value_regex="u?.*MyClass object"),
|
||||
CallEntry('def plain_method(self):'),
|
||||
LineEntry('pass'),
|
||||
ReturnEntry('pass'),
|
||||
ReturnValueEntry('None'),
|
||||
|
||||
# Called from deleter
|
||||
VariableEntry('self', value_regex="u?.*MyClass object at"),
|
||||
VariableEntry('self', value_regex="u?.*MyClass object"),
|
||||
CallEntry('def plain_method(self):'),
|
||||
LineEntry('pass'),
|
||||
ReturnEntry('pass'),
|
||||
ReturnValueEntry('None'),
|
||||
)
|
||||
),
|
||||
normalize=normalize,
|
||||
)
|
||||
|
||||
|
||||
def test_snooping_on_class_does_not_cause_base_class_to_be_snooped():
|
||||
@pytest.mark.parametrize("normalize", (True, False))
|
||||
def test_snooping_on_class_does_not_cause_base_class_to_be_snooped(normalize):
|
||||
string_io = io.StringIO()
|
||||
|
||||
class UnsnoopedBaseClass(object):
|
||||
|
|
@ -1518,7 +1568,7 @@ def test_snooping_on_class_does_not_cause_base_class_to_be_snooped():
|
|||
def method_on_base_class(self):
|
||||
self.method_on_base_class_was_called = True
|
||||
|
||||
@pysnooper.snoop(string_io)
|
||||
@pysnooper.snoop(string_io, normalize=normalize)
|
||||
class MyClass(UnsnoopedBaseClass):
|
||||
def method_on_child_class(self):
|
||||
self.method_on_base_class()
|
||||
|
|
@ -1534,10 +1584,100 @@ def test_snooping_on_class_does_not_cause_base_class_to_be_snooped():
|
|||
output,
|
||||
(
|
||||
SourcePathEntry(),
|
||||
VariableEntry('self', value_regex="u?.*MyClass object at"),
|
||||
VariableEntry('self', value_regex="u?.*MyClass object"),
|
||||
CallEntry('def method_on_child_class(self):'),
|
||||
LineEntry('self.method_on_base_class()'),
|
||||
ReturnEntry('self.method_on_base_class()'),
|
||||
ReturnValueEntry('None'),
|
||||
)
|
||||
),
|
||||
normalize=normalize,
|
||||
)
|
||||
|
||||
|
||||
def test_normalize():
|
||||
string_io = io.StringIO()
|
||||
|
||||
class A:
|
||||
def __init__(self, a):
|
||||
self.a = a
|
||||
|
||||
@pysnooper.snoop(string_io, normalize=True)
|
||||
def add():
|
||||
a = A(19)
|
||||
b = A(22)
|
||||
res = a.a + b.a
|
||||
return res
|
||||
|
||||
add()
|
||||
output = string_io.getvalue()
|
||||
assert_output(
|
||||
output,
|
||||
(
|
||||
SourcePathEntry('test_pysnooper.py'),
|
||||
VariableEntry('A', value_regex=r"<class .*\.A.?>"),
|
||||
CallEntry('def add():'),
|
||||
LineEntry('a = A(19)'),
|
||||
VariableEntry('a', value_regex=r"<.*\.A (?:object|instance)>"),
|
||||
LineEntry('b = A(22)'),
|
||||
VariableEntry('b', value_regex=r"<.*\.A (?:object|instance)>"),
|
||||
LineEntry('res = a.a + b.a'),
|
||||
VariableEntry('res', value="41"),
|
||||
LineEntry('return res'),
|
||||
ReturnEntry('return res'),
|
||||
ReturnValueEntry('41'),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def test_normalize_prefix():
|
||||
string_io = io.StringIO()
|
||||
_prefix = 'ZZZZ'
|
||||
|
||||
class A:
|
||||
def __init__(self, a):
|
||||
self.a = a
|
||||
|
||||
@pysnooper.snoop(string_io, normalize=True, prefix=_prefix)
|
||||
def add():
|
||||
a = A(19)
|
||||
b = A(22)
|
||||
res = a.a + b.a
|
||||
return res
|
||||
|
||||
add()
|
||||
output = string_io.getvalue()
|
||||
assert_output(
|
||||
output,
|
||||
(
|
||||
SourcePathEntry('test_pysnooper.py', prefix=_prefix),
|
||||
VariableEntry('A', value_regex=r"<class .*\.A.?>", prefix=_prefix),
|
||||
CallEntry('def add():', prefix=_prefix),
|
||||
LineEntry('a = A(19)', prefix=_prefix),
|
||||
VariableEntry('a', value_regex=r"<.*\.A (?:object|instance)>", prefix=_prefix),
|
||||
LineEntry('b = A(22)', prefix=_prefix),
|
||||
VariableEntry('b', value_regex=r"<.*\.A (?:object|instance)>", prefix=_prefix),
|
||||
LineEntry('res = a.a + b.a', prefix=_prefix),
|
||||
VariableEntry('res', value="41", prefix=_prefix),
|
||||
LineEntry('return res', prefix=_prefix),
|
||||
ReturnEntry('return res', prefix=_prefix),
|
||||
ReturnValueEntry('41', prefix=_prefix),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def test_normalize_thread_info():
|
||||
string_io = io.StringIO()
|
||||
|
||||
class A:
|
||||
def __init__(self, a):
|
||||
self.a = a
|
||||
|
||||
@pysnooper.snoop(string_io, normalize=True, thread_info=True)
|
||||
def add():
|
||||
a = A(19)
|
||||
b = A(22)
|
||||
res = a.a + b.a
|
||||
return res
|
||||
|
||||
with pytest.raises(NotImplementedError):
|
||||
add()
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
# Copyright 2019 Ram Rachum and collaborators.
|
||||
# This program is distributed under the MIT license.
|
||||
|
||||
import os
|
||||
import re
|
||||
import abc
|
||||
import inspect
|
||||
|
||||
from pysnooper.utils import DEFAULT_REPR_RE
|
||||
|
||||
try:
|
||||
from itertools import zip_longest
|
||||
except ImportError:
|
||||
|
|
@ -202,7 +204,7 @@ class _BaseEventEntry(_BaseEntry):
|
|||
if source is not None:
|
||||
assert source_regex is None
|
||||
self.line_pattern = re.compile(
|
||||
r"""^%s(?P<indent>(?: {4})*)[0-9:.]{15} """
|
||||
r"""^%s(?P<indent>(?: {4})*)(?:(?:[0-9:.]{15})|(?: {15})) """
|
||||
r"""(?P<thread_info>[0-9]+-[0-9A-Za-z_-]+[ ]+)?"""
|
||||
r"""(?P<event_name>[a-z_]*) +(?P<line_number>[0-9]*) """
|
||||
r"""+(?P<source>.*)$""" % (re.escape(self.prefix,))
|
||||
|
|
@ -269,7 +271,25 @@ class OutputFailure(Exception):
|
|||
pass
|
||||
|
||||
|
||||
def assert_output(output, expected_entries, prefix=None):
|
||||
def verify_normalize(lines, prefix):
|
||||
time_re = re.compile(r"[0-9:.]{15}")
|
||||
src_re = re.compile(r'^(?: *)Source path:\.\.\. (.*)$')
|
||||
for line in lines:
|
||||
if DEFAULT_REPR_RE.search(line):
|
||||
msg = "normalize is active, memory address should not appear"
|
||||
raise OutputFailure(line, msg)
|
||||
no_prefix = line.replace(prefix if prefix else '', '').strip()
|
||||
if time_re.match(no_prefix):
|
||||
msg = "normalize is active, time should not appear"
|
||||
raise OutputFailure(line, msg)
|
||||
m = src_re.match(line)
|
||||
if m:
|
||||
if not os.path.basename(m.group(1)) == m.group(1):
|
||||
msg = "normalize is active, path should be only basename"
|
||||
raise OutputFailure(line, msg)
|
||||
|
||||
|
||||
def assert_output(output, expected_entries, prefix=None, normalize=False):
|
||||
lines = tuple(filter(None, output.split('\n')))
|
||||
|
||||
if prefix is not None:
|
||||
|
|
@ -277,6 +297,9 @@ def assert_output(output, expected_entries, prefix=None):
|
|||
if not line.startswith(prefix):
|
||||
raise OutputFailure(line)
|
||||
|
||||
if normalize:
|
||||
verify_normalize(lines, prefix)
|
||||
|
||||
any_mismatch = False
|
||||
result = ''
|
||||
template = u'\n{line!s:%s} {expected_entry} {arrow}' % max(map(len, lines))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue