mirror of
https://github.com/cool-RR/PySnooper.git
synced 2026-01-23 10:15:11 +00:00
Add classes to automatically track attributes, keys, etc. of variables
# Conflicts: # pysnooper/tracer.py
This commit is contained in:
parent
5111a534d4
commit
56d3389500
3 changed files with 152 additions and 47 deletions
|
|
@ -6,59 +6,19 @@ import re
|
|||
import collections
|
||||
import datetime as datetime_module
|
||||
import itertools
|
||||
try:
|
||||
import reprlib
|
||||
import builtins
|
||||
except ImportError:
|
||||
import repr as reprlib
|
||||
import __builtin__ as builtins
|
||||
|
||||
from .variables import Variable
|
||||
from .third_party import six
|
||||
from . import utils
|
||||
from .utils import get_shortish_repr, ensure_tuple
|
||||
|
||||
ipython_filename_pattern = re.compile('^<ipython-input-([0-9]+)-.*>$')
|
||||
|
||||
|
||||
class Repr(reprlib.Repr, object): # reprlib.Repr is old-style in Python 2
|
||||
def __init__(self):
|
||||
super(Repr, self).__init__()
|
||||
self.maxother = 100
|
||||
|
||||
def repr(self, x):
|
||||
try:
|
||||
return super(Repr, self).repr(x)
|
||||
except Exception as e:
|
||||
return '<{} instance at {:#x} (__repr__ raised {})>'.format(
|
||||
x.__class__.__name__, id(x), e.__class__.__name__)
|
||||
|
||||
def repr_instance(self, x, level):
|
||||
s = builtins.repr(x)
|
||||
if len(s) > self.maxother:
|
||||
i = max(0, (self.maxother - 3) // 2)
|
||||
j = max(0, self.maxother - 3 - i)
|
||||
s = s[:i] + '...' + s[len(s) - j:]
|
||||
return s
|
||||
|
||||
|
||||
repr_instance = Repr()
|
||||
|
||||
|
||||
def get_shortish_repr(item):
|
||||
r = repr_instance.repr(item)
|
||||
r = r.replace('\r', '').replace('\n', '')
|
||||
return r
|
||||
|
||||
|
||||
def get_local_reprs(frame, variables=()):
|
||||
result = {key: get_shortish_repr(value) for key, value
|
||||
in frame.f_locals.items()}
|
||||
for variable in variables:
|
||||
try:
|
||||
result[variable] = get_shortish_repr(
|
||||
eval(variable, (frame.f_globals or {}), frame.f_locals)
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
result.update(variable.items(frame))
|
||||
return result
|
||||
|
||||
|
||||
|
|
@ -143,9 +103,10 @@ class Tracer:
|
|||
self.target_code_object = target_code_object
|
||||
self._write = write
|
||||
self.truncate = truncate
|
||||
if isinstance(variables, six.string_types):
|
||||
variables = (variables,)
|
||||
self.variables = variables
|
||||
self.variables = [
|
||||
v if isinstance(v, Variable) else Variable(v)
|
||||
for v in ensure_tuple(variables)
|
||||
]
|
||||
self.frame_to_old_local_reprs = collections.defaultdict(lambda: {})
|
||||
self.frame_to_local_reprs = collections.defaultdict(lambda: {})
|
||||
self.depth = depth
|
||||
|
|
|
|||
|
|
@ -2,9 +2,17 @@
|
|||
# This program is distributed under the MIT license.
|
||||
|
||||
import abc
|
||||
import sys
|
||||
|
||||
from .pycompat import ABC
|
||||
from .third_party import six
|
||||
|
||||
try:
|
||||
import reprlib
|
||||
import builtins
|
||||
except ImportError:
|
||||
import repr as reprlib
|
||||
import __builtin__ as builtins
|
||||
|
||||
|
||||
def _check_methods(C, *methods):
|
||||
mro = C.__mro__
|
||||
|
|
@ -31,6 +39,7 @@ class WritableStream(ABC):
|
|||
return NotImplemented
|
||||
|
||||
|
||||
|
||||
file_reading_errors = (
|
||||
IOError,
|
||||
OSError,
|
||||
|
|
@ -38,7 +47,47 @@ file_reading_errors = (
|
|||
)
|
||||
|
||||
|
||||
|
||||
def shitcode(s):
|
||||
return ''.join(
|
||||
(c if (0 < ord(c) < 256) else '?') for c in s
|
||||
)
|
||||
|
||||
|
||||
class Repr(reprlib.Repr, object): # reprlib.Repr is old-style in Python 2
|
||||
def __init__(self):
|
||||
super(Repr, self).__init__()
|
||||
self.maxother = 100
|
||||
|
||||
def repr(self, x):
|
||||
try:
|
||||
return super(Repr, self).repr(x)
|
||||
except Exception as e:
|
||||
return '<{} instance at {:#x} (__repr__ raised {})>'.format(
|
||||
x.__class__.__name__, id(x), e.__class__.__name__)
|
||||
|
||||
def repr_instance(self, x, level):
|
||||
s = builtins.repr(x)
|
||||
if len(s) > self.maxother:
|
||||
i = max(0, (self.maxother - 3) // 2)
|
||||
j = max(0, self.maxother - 3 - i)
|
||||
s = s[:i] + '...' + s[len(s) - j:]
|
||||
return s
|
||||
|
||||
|
||||
repr_instance = Repr()
|
||||
|
||||
|
||||
def get_shortish_repr(item):
|
||||
r = repr_instance.repr(item)
|
||||
r = r.replace('\r', '').replace('\n', '')
|
||||
return r
|
||||
|
||||
|
||||
def ensure_tuple(x):
|
||||
if isinstance(x, six.string_types):
|
||||
x = (x,)
|
||||
return tuple(x)
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
95
pysnooper/variables.py
Normal file
95
pysnooper/variables.py
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
import itertools
|
||||
from copy import deepcopy
|
||||
|
||||
from .utils import get_shortish_repr, ensure_tuple
|
||||
|
||||
|
||||
class Variable(object):
|
||||
def __init__(self, source, exclude=()):
|
||||
self.source = source
|
||||
self.exclude = ensure_tuple(exclude)
|
||||
self.code = compile(source, '<variable>', 'eval')
|
||||
|
||||
def items(self, frame):
|
||||
try:
|
||||
main_value = eval(self.code, frame.f_globals, frame.f_locals)
|
||||
except Exception:
|
||||
return ()
|
||||
result = [(self.source, get_shortish_repr(main_value))]
|
||||
for key in self._safe_keys(main_value):
|
||||
if key in self.exclude:
|
||||
continue
|
||||
try:
|
||||
value = self._get_value(main_value, key)
|
||||
except Exception:
|
||||
continue
|
||||
result.append((
|
||||
'({}){}'.format(self.source, self._format_key(key)),
|
||||
get_shortish_repr(value)
|
||||
))
|
||||
return result
|
||||
|
||||
def _safe_keys(self, main_value):
|
||||
try:
|
||||
for key in self._keys(main_value):
|
||||
yield key
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def _keys(self, main_value):
|
||||
return ()
|
||||
|
||||
def _format_key(self, key):
|
||||
raise NotImplementedError()
|
||||
|
||||
def _get_value(self, main_value, key):
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class Attrs(Variable):
|
||||
def _keys(self, main_value):
|
||||
return itertools.chain(
|
||||
getattr(main_value, '__dict__', ()),
|
||||
getattr(main_value, '__slots__', ())
|
||||
)
|
||||
|
||||
def _format_key(self, key):
|
||||
return '.' + key
|
||||
|
||||
def _get_value(self, main_value, key):
|
||||
return getattr(main_value, key)
|
||||
|
||||
|
||||
class Keys(Variable):
|
||||
def _keys(self, main_value):
|
||||
return main_value.keys()
|
||||
|
||||
def _format_key(self, key):
|
||||
return '[{!r}]'.format(key)
|
||||
|
||||
def _get_value(self, main_value, key):
|
||||
return main_value[key]
|
||||
|
||||
|
||||
class Indices(Keys):
|
||||
_slice = slice(None)
|
||||
|
||||
def _keys(self, main_value):
|
||||
return range(len(main_value))[self._slice]
|
||||
|
||||
def __getitem__(self, item):
|
||||
assert isinstance(item, slice)
|
||||
result = deepcopy(self)
|
||||
result._slice = item
|
||||
return result
|
||||
|
||||
|
||||
class Enumerate(Variable):
|
||||
def _keys(self, main_value):
|
||||
return enumerate(main_value)
|
||||
|
||||
def _format_key(self, key):
|
||||
return '<{}>'.format(key[0])
|
||||
|
||||
def _get_value(self, main_value, key):
|
||||
return key[1]
|
||||
Loading…
Add table
Add a link
Reference in a new issue