def test_watch(): class Foo(object): def __init__(self): self.x = 2 def square(self): self.x **= 2 @pysnooper.snoop(watch=( 'foo.x', 'io.__name__', 'len(foo.__dict__["x"] * "abc")', )) def my_function(): foo = Foo() for i in range(2): foo.square() with sys_tools.OutputCapturer(stdout=False, stderr=True) as output_capturer: result = my_function() assert result is None output = output_capturer.string_io.getvalue() assert_output( output, (VariableEntry('Foo'), VariableEntry('io.__name__', "'io'"), CallEntry('def my_function():'), LineEntry('foo = Foo()'), VariableEntry('foo'), VariableEntry('foo.x', '2'), VariableEntry('len(foo.__dict__["x"] * "abc")', '6'), LineEntry(), VariableEntry('i', '0'), LineEntry(), VariableEntry('foo.x', '4'), VariableEntry('len(foo.__dict__["x"] * "abc")', '12'), LineEntry(), VariableEntry('i', '1'), LineEntry(), VariableEntry('foo.x', '16'), VariableEntry('len(foo.__dict__["x"] * "abc")', '48'), LineEntry(), ReturnEntry(), ReturnValueEntry('None')))
def test_unavailable_source(): with temp_file_tools.create_temp_folder(prefix='pysnooper') as folder, \ sys_tools.TempSysPathAdder(str(folder)): module_name = 'iaerojajsijf' python_file_path = folder / ('%s.py' % (module_name, )) content = textwrap.dedent(u''' import pysnooper @pysnooper.snoop() def f(x): return x ''') with python_file_path.open('w') as python_file: python_file.write(content) module = __import__(module_name) python_file_path.unlink() with sys_tools.OutputCapturer(stdout=False, stderr=True) as output_capturer: result = getattr(module, 'f')(7) assert result == 7 output = output_capturer.output assert_output(output, ( VariableEntry(stage='starting'), CallEntry('SOURCE IS UNAVAILABLE'), LineEntry('SOURCE IS UNAVAILABLE'), ReturnEntry('SOURCE IS UNAVAILABLE'), ReturnValueEntry('7'), ))
def test_variables_classes(): class WithSlots(object): __slots__ = ('x', 'y') def __init__(self): self.x = 3 self.y = 4 @pysnooper.snoop(watch=( pysnooper.Keys('_d', exclude='c'), pysnooper.Attrs('_d'), # doesn't have attributes pysnooper.Attrs('_s'), pysnooper.Indices('_lst')[-3:], )) def my_function(): _d = {'a': 1, 'b': 2, 'c': 'ignore'} _s = WithSlots() _lst = list(range(1000)) with sys_tools.OutputCapturer(stdout=False, stderr=True) as output_capturer: result = my_function() assert result is None output = output_capturer.string_io.getvalue() assert_output( output, (VariableEntry('WithSlots'), CallEntry('def my_function():'), LineEntry(), VariableEntry('_d'), VariableEntry("_d['a']", '1'), VariableEntry("_d['b']", '2'), LineEntry(), VariableEntry('_s'), VariableEntry('_s.x', '3'), VariableEntry('_s.y', '4'), LineEntry(), VariableEntry('_lst'), VariableEntry('_lst[997]', '997'), VariableEntry('_lst[998]', '998'), VariableEntry( '_lst[999]', '999'), ReturnEntry(), ReturnValueEntry('None')))
def test_method_and_prefix(): class Baz(object): def __init__(self): self.x = 2 @pysnooper.snoop(watch=('self.x', ), prefix='ZZZ') def square(self): foo = 7 self.x **= 2 return self baz = Baz() with sys_tools.OutputCapturer(stdout=False, stderr=True) as output_capturer: result = baz.square() assert result is baz assert result.x == 4 output = output_capturer.string_io.getvalue() assert_output(output, ( VariableEntry('self', prefix='ZZZ'), VariableEntry('self.x', '2', prefix='ZZZ'), CallEntry('def square(self):', prefix='ZZZ'), LineEntry('foo = 7', prefix='ZZZ'), VariableEntry('foo', '7', prefix='ZZZ'), LineEntry('self.x **= 2', prefix='ZZZ'), VariableEntry('self.x', '4', prefix='ZZZ'), LineEntry(prefix='ZZZ'), ReturnEntry(prefix='ZZZ'), ReturnValueEntry(prefix='ZZZ'), ), prefix='ZZZ')
def test_single_watch_no_comma(): class Foo(object): def __init__(self): self.x = 2 def square(self): self.x **= 2 @pysnooper.snoop(watch='foo') def my_function(): foo = Foo() for i in range(2): foo.square() with sys_tools.OutputCapturer(stdout=False, stderr=True) as output_capturer: result = my_function() assert result is None output = output_capturer.string_io.getvalue() assert_output(output, (VariableEntry('Foo'), CallEntry('def my_function():'), LineEntry('foo = Foo()'), VariableEntry('foo'), LineEntry(), VariableEntry('i', '0'), LineEntry(), LineEntry(), VariableEntry('i', '1'), LineEntry(), LineEntry(), ReturnEntry(), ReturnValueEntry('None')))
def test_repr_exception(): class Bad(object): def __repr__(self): 1 / 0 @pysnooper.snoop() def my_function(): bad = Bad() with sys_tools.OutputCapturer(stdout=False, stderr=True) as output_capturer: result = my_function() assert result is None output = output_capturer.string_io.getvalue() assert_output( output, ( VariableEntry('Bad'), CallEntry('def my_function():'), LineEntry('bad = Bad()'), VariableEntry('bad', value_regex=r'<Bad instance at 0x\w+ \(__repr__ raised ZeroDivisionError\)>'), ReturnEntry(), ReturnValueEntry('None') ) )
def test_watch_explode(): class Foo: def __init__(self, x, y): self.x = x self.y = y @pysnooper.snoop(watch_explode=('_d', '_point', 'lst + []')) def my_function(): _d = {'a': 1, 'b': 2, 'c': 'ignore'} _point = Foo(x=3, y=4) lst = [7, 8, 9] lst.append(10) with sys_tools.OutputCapturer(stdout=False, stderr=True) as output_capturer: result = my_function() assert result is None output = output_capturer.string_io.getvalue() assert_output( output, (VariableEntry('Foo'), CallEntry('def my_function():'), LineEntry(), VariableEntry('_d'), VariableEntry("_d['a']", '1'), VariableEntry("_d['b']", '2'), VariableEntry( "_d['c']", "'ignore'"), LineEntry(), VariableEntry('_point'), VariableEntry('_point.x', '3'), VariableEntry( '_point.y', '4'), LineEntry(), VariableEntry( '(lst + [])[0]', '7'), VariableEntry('(lst + [])[1]', '8'), VariableEntry('(lst + [])[2]', '9'), VariableEntry('lst'), VariableEntry('lst + []'), LineEntry(), VariableEntry('(lst + [])[3]', '10'), VariableEntry('lst'), VariableEntry('lst + []'), ReturnEntry(), ReturnValueEntry('None')))
def assert_output(verbose, expect): torchsnooper.register_snoop(verbose=verbose) with sys_tools.OutputCapturer(stdout=False, stderr=True) as output_capturer: assert sys.gettrace() is None snoop(func)() assert sys.gettrace() is None output = output_capturer.string_io.getvalue() output = ansi_escape.sub('', output) assert clean_output(output) == clean_output(expect) snoop.config = default_config
def test_long_variable(): @pysnooper.snoop() def my_function(): foo = list(range(1000)) return foo with sys_tools.OutputCapturer(stdout=False, stderr=True) as output_capturer: result = my_function() assert result == list(range(1000)) output = output_capturer.string_io.getvalue() assert_output( output, (CallEntry('def my_function():'), LineEntry('foo = list(range(1000))'), VariableEntry('foo', '[0, 1, 2, 3, 4, 5, ...]'), LineEntry(), ReturnEntry(), ReturnValueEntry('[0, 1, 2, 3, 4, 5, ...]')))
def test_long_variable(): @pysnooper.snoop() def my_function(): foo = list(range(1000)) return foo with sys_tools.OutputCapturer(stdout=False, stderr=True) as output_capturer: result = my_function() assert result == list(range(1000)) output = output_capturer.string_io.getvalue() regex = r'^\[0, 1, 2, .*\.\.\..*, 997, 998, 999\]$' assert_output( output, (CallEntry('def my_function():'), LineEntry('foo = list(range(1000))'), VariableEntry('foo', value_regex=regex), LineEntry(), ReturnEntry(), ReturnValueEntry(value_regex=regex)))
def assert_sample_output(module): with sys_tools.OutputCapturer(stdout=False, stderr=True) as output_capturer: module.main() time = '21:10:42.298924' time_pattern = re.sub(r'\d', r'\\d', time) def normalise(out): return re.sub(time_pattern, time, out).strip() output = output_capturer.string_io.getvalue() try: assert (normalise(output) == normalise(module.expected_output)) 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)
def test_repr_exception(): class Bad(object): def __repr__(self): 1 / 0 @pysnooper.snoop() def my_function(): bad = Bad() with sys_tools.OutputCapturer(stdout=False, stderr=True) as output_capturer: result = my_function() assert result is None output = output_capturer.string_io.getvalue() assert_output( output, (VariableEntry('Bad'), CallEntry('def my_function():'), LineEntry('bad = Bad()'), VariableEntry('bad', value='REPR FAILED'), ReturnEntry(), ReturnValueEntry('None')))
def test_thread_info(): @pysnooper.snoop(thread_info=True) def my_function(foo): x = 7 y = 8 return y + x with sys_tools.OutputCapturer(stdout=False, stderr=True) as output_capturer: result = my_function('baba') assert result == 15 output = output_capturer.string_io.getvalue() assert_output(output, ( VariableEntry('foo', value_regex="u?'baba'"), CallEntry('def my_function(foo):'), LineEntry('x = 7'), VariableEntry('x', '7'), LineEntry('y = 8'), VariableEntry('y', '8'), LineEntry('return y + x'), ReturnEntry('return y + x'), ReturnValueEntry('15'), ))
def assert_sample_output(module): with sys_tools.OutputCapturer(stdout=False, stderr=True) as output_capturer: assert sys.gettrace() is None module.main() assert sys.gettrace() is None time = '12:34:56.78' time_pattern = re.sub(r'\d', r'\\d', time) output = output_capturer.string_io.getvalue() normalised = re.sub(time_pattern, time, output).strip() normalised = re.sub(r'0x\w+', '0xABC', normalised) normalised = normalised.replace('<genexpr>.<genexpr>', '<genexpr>') normalised = normalised.replace('<list_iterator', '<tupleiterator') normalised = normalised.replace('<listiterator', '<tupleiterator') normalised = normalised.replace('<tuple_iterator', '<tupleiterator') normalised = normalised.replace('<sequenceiterator', '<tupleiterator') try: assert (normalised == module.expected_output.strip()) except AssertionError: if os.environ.get('FIX_SNOOP_TESTS'): path = module.__file__.rstrip('c') contents = file_to_string(path) match = re.search( r'expected_output = r?"""', contents, ) contents = contents[:match.end(0)] + '\n{}\n"""\n'.format( normalised) string_to_file(contents, path) else: print('\n\nNormalised actual output:\n\n' + normalised) raise # show pytest diff (may need -vv flag to see in full)
def test_with_block(): # Testing that a single Tracer can handle many mixed uses snoop = pysnooper.snoop() def foo(x): if x == 0: bar1(x) qux() return with snoop: # There should be line entries for these three lines, # no line entries for anything else in this function, # but calls to all bar functions should be traced foo(x - 1) bar2(x) qux() int(4) bar3(9) return x @snoop def bar1(_x): qux() @snoop def bar2(_x): qux() @snoop def bar3(_x): qux() def qux(): return 9 # not traced, mustn't show up with sys_tools.OutputCapturer(stdout=False, stderr=True) as output_capturer: result = foo(2) assert result == 2 output = output_capturer.string_io.getvalue() assert_output( output, ( # In first with VariableEntry('bar1'), VariableEntry('bar2'), VariableEntry('bar3'), VariableEntry('foo'), VariableEntry('qux'), VariableEntry('snoop'), VariableEntry('x', '2'), LineEntry('foo(x - 1)'), # In with in recursive call VariableEntry('bar1'), VariableEntry('bar2'), VariableEntry('bar3'), VariableEntry('foo'), VariableEntry('qux'), VariableEntry('snoop'), VariableEntry('x', '1'), LineEntry('foo(x - 1)'), # Call to bar1 from if block outside with VariableEntry('_x', '0'), VariableEntry('qux'), CallEntry('def bar1(_x):'), LineEntry('qux()'), ReturnEntry('qux()'), ReturnValueEntry('None'), # In with in recursive call LineEntry('bar2(x)'), # Call to bar2 from within with VariableEntry('_x', '1'), VariableEntry('qux'), CallEntry('def bar2(_x):'), LineEntry('qux()'), ReturnEntry('qux()'), ReturnValueEntry('None'), # In with in recursive call LineEntry('qux()'), # Call to bar3 from after with VariableEntry('_x', '9'), VariableEntry('qux'), CallEntry('def bar3(_x):'), LineEntry('qux()'), ReturnEntry('qux()'), ReturnValueEntry('None'), # -- Similar to previous few sections, # -- but from first call to foo # In with in first call LineEntry('bar2(x)'), # Call to bar2 from within with VariableEntry('_x', '2'), VariableEntry('qux'), CallEntry('def bar2(_x):'), LineEntry('qux()'), ReturnEntry('qux()'), ReturnValueEntry('None'), # In with in first call LineEntry('qux()'), # Call to bar3 from after with VariableEntry('_x', '9'), VariableEntry('qux'), CallEntry('def bar3(_x):'), LineEntry('qux()'), ReturnEntry('qux()'), ReturnValueEntry('None'), ), )
def test_multi_thread_info(): @pysnooper.snoop(thread_info=True) def my_function(foo): x = 7 y = 8 return y + x with sys_tools.OutputCapturer(stdout=False, stderr=True) as output_capturer: my_function('baba') t1 = threading.Thread(target=my_function, name="test123", args=['bubu']) t1.start() t1.join() t1 = threading.Thread(target=my_function, name="bibi", args=['bibi']) t1.start() t1.join() output = output_capturer.string_io.getvalue() calls = [line for line in output.split("\n") if "call" in line] main_thread = calls[0] assert len(main_thread) == len(calls[1]) assert len(main_thread) == len(calls[2]) main_thread_call_str = main_thread.find("call") assert main_thread_call_str == calls[1].find("call") assert main_thread_call_str == calls[2].find("call") thread_info_regex = '([0-9]+-{name}+[ ]+)' assert_output(output, ( VariableEntry('foo', value_regex="u?'baba'"), CallEntry( 'def my_function(foo):', thread_info_regex=thread_info_regex.format(name="MainThread")), LineEntry( 'x = 7', thread_info_regex=thread_info_regex.format(name="MainThread")), VariableEntry('x', '7'), LineEntry( 'y = 8', thread_info_regex=thread_info_regex.format(name="MainThread")), VariableEntry('y', '8'), LineEntry( 'return y + x', thread_info_regex=thread_info_regex.format(name="MainThread")), ReturnEntry('return y + x'), ReturnValueEntry('15'), VariableEntry('foo', value_regex="u?'bubu'"), CallEntry('def my_function(foo):', thread_info_regex=thread_info_regex.format(name="test123")), LineEntry('x = 7', thread_info_regex=thread_info_regex.format(name="test123")), VariableEntry('x', '7'), LineEntry('y = 8', thread_info_regex=thread_info_regex.format(name="test123")), VariableEntry('y', '8'), LineEntry('return y + x', thread_info_regex=thread_info_regex.format(name="test123")), ReturnEntry('return y + x'), ReturnValueEntry('15'), VariableEntry('foo', value_regex="u?'bibi'"), CallEntry('def my_function(foo):', thread_info_regex=thread_info_regex.format(name='bibi')), LineEntry('x = 7', thread_info_regex=thread_info_regex.format(name='bibi')), VariableEntry('x', '7'), LineEntry('y = 8', thread_info_regex=thread_info_regex.format(name='bibi')), VariableEntry('y', '8'), LineEntry('return y + x', thread_info_regex=thread_info_regex.format(name='bibi')), ReturnEntry('return y + x'), ReturnValueEntry('15'), ))