diff --git a/pysnooper/tracer.py b/pysnooper/tracer.py index f029535..583074f 100644 --- a/pysnooper/tracer.py +++ b/pysnooper/tracer.py @@ -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('^$') -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 diff --git a/pysnooper/utils.py b/pysnooper/utils.py index 2de6948..91e855f 100644 --- a/pysnooper/utils.py +++ b/pysnooper/utils.py @@ -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) + + + diff --git a/pysnooper/variables.py b/pysnooper/variables.py new file mode 100644 index 0000000..f224024 --- /dev/null +++ b/pysnooper/variables.py @@ -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, '', '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]