From 313bc5a2e4e249590a8827a5b06a3d4150e86c6b Mon Sep 17 00:00:00 2001 From: Co1lin Date: Sat, 3 Feb 2024 00:14:53 -0500 Subject: [PATCH 1/2] feat: add src_dir to only snoop lines that are in the specified source directories --- pysnooper/tracer.py | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/pysnooper/tracer.py b/pysnooper/tracer.py index 00b7a68..10a6153 100644 --- a/pysnooper/tracer.py +++ b/pysnooper/tracer.py @@ -147,6 +147,13 @@ def get_write_function(output, overwrite): return write +def _file_in_dir(file, directory): + # ref: https://stackoverflow.com/q/3812849/12388699 + # assert file == os.path.realpath(file), 'property of frame.f_code.co_filename' + directory = os.path.join(directory, '') + return os.path.commonprefix([file, directory]) == directory + + class FileWriter(object): def __init__(self, path, overwrite): self.path = pycompat.text_type(path) @@ -191,6 +198,10 @@ class Tracer: Show snoop lines for functions that your function calls:: @pysnooper.snoop(depth=2) + + Only snoop lines that are in the specified source directories:: + + @pysnooper.snoop(src_dir='path/to/myproj') Start all snoop lines with a prefix, to grep for them easily:: @@ -225,7 +236,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, normalize=False, relative_time=False, - color=True): + src_dir=[], color=True): self._write = get_write_function(output, overwrite) self.watch = [ @@ -253,6 +264,12 @@ class Tracer: self.max_variable_length = max_variable_length self.normalize = normalize self.relative_time = relative_time + if isinstance(src_dir, str): + self.src_dirs = [src_dir] + else: + assert isinstance(src_dir, list) + self.src_dirs = src_dir + self.src_dirs = list(map(os.path.realpath, self.src_dirs)) self.color = color and sys.platform in ('linux', 'linux2', 'cygwin', 'darwin') @@ -397,7 +414,11 @@ class Tracer: # We should trace this line either if it's in the decorated function, # or the user asked to go a few levels deeper and we're within that # number of levels deeper. - + if self.src_dirs and not ( + frame.f_code.co_filename[0] == '/' and # in case of '' + any(_file_in_dir(frame.f_code.co_filename, d) for d in self.src_dirs) + ): + return None if not (frame.f_code in self.target_codes or frame in self.target_frames): if self.depth == 1: # We did the most common and quickest check above, because the From 41d53c65f9f2a3c520b90c6eacce3d41215f0633 Mon Sep 17 00:00:00 2001 From: Co1lin Date: Mon, 5 Feb 2024 09:29:45 -0500 Subject: [PATCH 2/2] feat: add exclude_paths --- pysnooper/tracer.py | 39 ++++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/pysnooper/tracer.py b/pysnooper/tracer.py index 10a6153..b3f99a0 100644 --- a/pysnooper/tracer.py +++ b/pysnooper/tracer.py @@ -147,11 +147,12 @@ def get_write_function(output, overwrite): return write -def _file_in_dir(file, directory): +def _path_eq_or_sub(c, p): # ref: https://stackoverflow.com/q/3812849/12388699 # assert file == os.path.realpath(file), 'property of frame.f_code.co_filename' - directory = os.path.join(directory, '') - return os.path.commonprefix([file, directory]) == directory + p_slash = os.path.join(p, '') + return c.rstrip(os.sep) == p.rstrip(os.sep) or \ + os.path.commonprefix([c, p_slash]).rstrip(os.sep) == p.rstrip(os.sep) class FileWriter(object): @@ -236,7 +237,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, normalize=False, relative_time=False, - src_dir=[], color=True): + color=True, source_paths=[], exclude_paths=[]): self._write = get_write_function(output, overwrite) self.watch = [ @@ -264,15 +265,8 @@ class Tracer: self.max_variable_length = max_variable_length self.normalize = normalize self.relative_time = relative_time - if isinstance(src_dir, str): - self.src_dirs = [src_dir] - else: - assert isinstance(src_dir, list) - self.src_dirs = src_dir - self.src_dirs = list(map(os.path.realpath, self.src_dirs)) self.color = color and sys.platform in ('linux', 'linux2', 'cygwin', 'darwin') - if self.color: self._FOREGROUND_BLUE = '\x1b[34m' self._FOREGROUND_CYAN = '\x1b[36m' @@ -298,6 +292,18 @@ class Tracer: self._STYLE_NORMAL = '' self._STYLE_RESET_ALL = '' + def process_paths(paths): + ret = [] + if isinstance(paths, str): + ret = [paths] + else: + assert isinstance(paths, list) + ret = paths + return list(map(os.path.realpath, ret)) + + self.source_paths = process_paths(source_paths) + self.exclude_paths = process_paths(exclude_paths) + def __call__(self, function_or_class): if DISABLED: return function_or_class @@ -414,11 +420,18 @@ class Tracer: # We should trace this line either if it's in the decorated function, # or the user asked to go a few levels deeper and we're within that # number of levels deeper. - if self.src_dirs and not ( + if self.source_paths and not ( frame.f_code.co_filename[0] == '/' and # in case of '' - any(_file_in_dir(frame.f_code.co_filename, d) for d in self.src_dirs) + any(_path_eq_or_sub(frame.f_code.co_filename, p) for p in self.source_paths) ): return None + + if self.exclude_paths and ( + frame.f_code.co_filename[0] == '/' and # in case of '' + any(_path_eq_or_sub(frame.f_code.co_filename, p) for p in self.exclude_paths) + ): + return None + if not (frame.f_code in self.target_codes or frame in self.target_frames): if self.depth == 1: # We did the most common and quickest check above, because the