mirror of
https://github.com/cool-RR/PySnooper.git
synced 2026-01-23 02:14:04 +00:00
Make Tracer a class based decorator
This commit is contained in:
parent
e856f7a181
commit
6d78ee4c84
3 changed files with 85 additions and 102 deletions
|
|
@ -17,7 +17,7 @@ changed in the decorated function.
|
|||
For more information, see https://github.com/cool-RR/PySnooper
|
||||
'''
|
||||
|
||||
from .pysnooper import snoop
|
||||
from .tracer import Tracer as snoop
|
||||
from .variables import Attrs, Exploding, Indices, Keys
|
||||
import collections
|
||||
|
||||
|
|
|
|||
|
|
@ -1,95 +0,0 @@
|
|||
# Copyright 2019 Ram Rachum and collaborators.
|
||||
# This program is distributed under the MIT license.
|
||||
|
||||
import sys
|
||||
|
||||
from .third_party import six
|
||||
from .third_party import decorator
|
||||
|
||||
from . import utils
|
||||
from . import pycompat
|
||||
from .tracer import Tracer
|
||||
|
||||
|
||||
def get_write_and_truncate_functions(output):
|
||||
if output is None:
|
||||
def write(s):
|
||||
stderr = sys.stderr
|
||||
try:
|
||||
stderr.write(s)
|
||||
except UnicodeEncodeError:
|
||||
# God damn Python 2
|
||||
stderr.write(utils.shitcode(s))
|
||||
truncate = None
|
||||
elif isinstance(output, (pycompat.PathLike, str)):
|
||||
def write(s):
|
||||
with open(six.text_type(output), 'a') as output_file:
|
||||
output_file.write(s)
|
||||
def truncate():
|
||||
with open(six.text_type(output), 'w') as output_file:
|
||||
pass
|
||||
elif callable(output):
|
||||
write = output
|
||||
truncate = None
|
||||
else:
|
||||
assert isinstance(output, utils.WritableStream)
|
||||
def write(s):
|
||||
output.write(s)
|
||||
truncate = None
|
||||
|
||||
return (write, truncate)
|
||||
|
||||
|
||||
def snoop(output=None, watch=(), watch_explode=(), depth=1,
|
||||
prefix='', overwrite=False):
|
||||
'''
|
||||
Snoop on the function, writing everything it's doing to stderr.
|
||||
|
||||
This is useful for debugging.
|
||||
|
||||
When you decorate a function with `@pysnooper.snoop()`, you'll get a log of
|
||||
every line that ran in the function and a play-by-play of every local
|
||||
variable that changed.
|
||||
|
||||
If stderr is not easily accessible for you, you can redirect the output to
|
||||
a file::
|
||||
|
||||
@pysnooper.snoop('/my/log/file.log')
|
||||
|
||||
See values of some expressions that aren't local variables::
|
||||
|
||||
@pysnooper.snoop(watch=('foo.bar', 'self.x["whatever"]'))
|
||||
|
||||
Expand values to see all their attributes or items of lists/dictionaries:
|
||||
|
||||
@pysnooper.snoop(watch_explode=('foo', 'self'))
|
||||
|
||||
(see Advanced Usage in the README for more control)
|
||||
|
||||
Show snoop lines for functions that your function calls::
|
||||
|
||||
@pysnooper.snoop(depth=2)
|
||||
|
||||
Start all snoop lines with a prefix, to grep for them easily::
|
||||
|
||||
@pysnooper.snoop(prefix='ZZZ ')
|
||||
|
||||
'''
|
||||
write, truncate = get_write_and_truncate_functions(output)
|
||||
if truncate is None and overwrite:
|
||||
raise Exception("`overwrite=True` can only be used when writing "
|
||||
"content to file.")
|
||||
def decorate(function):
|
||||
target_code_object = function.__code__
|
||||
tracer = Tracer(
|
||||
target_code_object=target_code_object, write=write,
|
||||
truncate=truncate, watch=watch, watch_explode=watch_explode,
|
||||
depth=depth, prefix=prefix, overwrite=overwrite
|
||||
)
|
||||
|
||||
def inner(function_, *args, **kwargs):
|
||||
with tracer:
|
||||
return function(*args, **kwargs)
|
||||
return decorator.decorate(function, inner)
|
||||
|
||||
return decorate
|
||||
|
|
@ -8,8 +8,8 @@ import datetime as datetime_module
|
|||
import itertools
|
||||
|
||||
from .variables import CommonVariable, Exploding, BaseVariable
|
||||
from .third_party import six
|
||||
from . import utils
|
||||
from .third_party import six, decorator
|
||||
from . import utils, pycompat
|
||||
|
||||
|
||||
ipython_filename_pattern = re.compile('^<ipython-input-([0-9]+)-.*>$')
|
||||
|
|
@ -99,11 +99,80 @@ def get_source_from_frame(frame):
|
|||
|
||||
|
||||
class Tracer:
|
||||
def __init__(self, target_code_object, write, truncate, watch=(),
|
||||
watch_explode=(), depth=1, prefix='', overwrite=False):
|
||||
self.target_code_object = target_code_object
|
||||
def __init__(
|
||||
self,
|
||||
output=None,
|
||||
watch=(),
|
||||
watch_explode=(),
|
||||
depth=1,
|
||||
prefix='',
|
||||
overwrite=False,
|
||||
):
|
||||
'''
|
||||
Snoop on the function, writing everything it's doing to stderr.
|
||||
|
||||
This is useful for debugging.
|
||||
|
||||
When you decorate a function with `@pysnooper.snoop()`, you'll get a log of
|
||||
every line that ran in the function and a play-by-play of every local
|
||||
variable that changed.
|
||||
|
||||
If stderr is not easily accessible for you, you can redirect the output to
|
||||
a file::
|
||||
|
||||
@pysnooper.snoop('/my/log/file.log')
|
||||
|
||||
See values of some expressions that aren't local variables::
|
||||
|
||||
@pysnooper.snoop(watch=('foo.bar', 'self.x["whatever"]'))
|
||||
|
||||
Expand values to see all their attributes or items of lists/dictionaries:
|
||||
|
||||
@pysnooper.snoop(watch_explode=('foo', 'self'))
|
||||
|
||||
(see Advanced Usage in the README for more control)
|
||||
|
||||
Show snoop lines for functions that your function calls::
|
||||
|
||||
@pysnooper.snoop(depth=2)
|
||||
|
||||
Start all snoop lines with a prefix, to grep for them easily::
|
||||
|
||||
@pysnooper.snoop(prefix='ZZZ ')
|
||||
|
||||
'''
|
||||
self.truncate = None
|
||||
if output is None:
|
||||
def write(s):
|
||||
stderr = sys.stderr
|
||||
try:
|
||||
stderr.write(s)
|
||||
except UnicodeEncodeError:
|
||||
# God damn Python 2
|
||||
stderr.write(utils.shitcode(s))
|
||||
elif isinstance(output, (pycompat.PathLike, str)):
|
||||
def write(s):
|
||||
with open(six.text_type(output), 'a') as output_file:
|
||||
output_file.write(s)
|
||||
|
||||
def truncate():
|
||||
with open(six.text_type(output), 'w'):
|
||||
pass
|
||||
|
||||
self.truncate = truncate
|
||||
elif callable(output):
|
||||
write = output
|
||||
else:
|
||||
assert isinstance(output, utils.WritableStream)
|
||||
|
||||
def write(s):
|
||||
output.write(s)
|
||||
|
||||
if self.truncate is None and overwrite:
|
||||
raise Exception("`overwrite=True` can only be used when writing "
|
||||
"content to file.")
|
||||
|
||||
self._write = write
|
||||
self.truncate = truncate
|
||||
self.watch = [
|
||||
v if isinstance(v, BaseVariable) else CommonVariable(v)
|
||||
for v in utils.ensure_tuple(watch)
|
||||
|
|
@ -119,6 +188,15 @@ class Tracer:
|
|||
self._did_overwrite = False
|
||||
assert self.depth >= 1
|
||||
|
||||
def __call__(self, function):
|
||||
self.target_code_object = function.__code__
|
||||
|
||||
def inner(function_, *args, **kwargs):
|
||||
with self:
|
||||
return function(*args, **kwargs)
|
||||
|
||||
return decorator.decorate(function, inner)
|
||||
|
||||
def write(self, s):
|
||||
if self.overwrite and not self._did_overwrite:
|
||||
self.truncate()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue