From 83853df7340bbfa892eb5506a9ae6aa629f3fc50 Mon Sep 17 00:00:00 2001 From: SoundsSerious Date: Thu, 30 Oct 2025 23:32:38 -0400 Subject: [PATCH 1/7] Add input comparison and is_same method to Diffr class --- diffgetr/diff_get.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/diffgetr/diff_get.py b/diffgetr/diff_get.py index 8d1b05c..3be24d8 100644 --- a/diffgetr/diff_get.py +++ b/diffgetr/diff_get.py @@ -19,6 +19,7 @@ def __init__( else: self.deep_diff_kw = deep_diff_kw + ### Compare Basic Type Inputs threshold = 1.0 / (10 ** self.deep_diff_kw.get("significant_digits", 3)) # TODO: fail here for type differences @@ -100,6 +101,15 @@ def __getitem__(self, key): # self.diff_data(sys.stdout,bytes=False) self.diff_summary() raise KeyError(f"{self.location} | key missing: {key}") + + def is_same(self) -> bool: + df = self.diff_obj + + if self.ignore_added: + df = {k:v for k,v in df.items() if 'added' not in k} + return not bool(df) + + @property def path(self): From 9b6c0266b0e2940db701b1fc54eaf6adf1977463 Mon Sep 17 00:00:00 2001 From: SoundsSerious Date: Wed, 21 Jan 2026 17:27:37 -0500 Subject: [PATCH 2/7] Update .gitignore to include Sphinx documentation build directory and modify print_here method for enhanced comparison output --- .gitignore | 2 ++ diffgetr/diff_get.py | 85 ++++++++++++++++++++++++++++++++------------ pyproject.toml | 2 +- 3 files changed, 65 insertions(+), 24 deletions(-) diff --git a/.gitignore b/.gitignore index bb3cc7b..77f3c84 100644 --- a/.gitignore +++ b/.gitignore @@ -75,6 +75,8 @@ instance/ # Sphinx documentation docs/_build/ +docs/*build/ + # PyBuilder .pybuilder/ diff --git a/diffgetr/diff_get.py b/diffgetr/diff_get.py index 3be24d8..867f3ca 100644 --- a/diffgetr/diff_get.py +++ b/diffgetr/diff_get.py @@ -21,6 +21,7 @@ def __init__( ### Compare Basic Type Inputs threshold = 1.0 / (10 ** self.deep_diff_kw.get("significant_digits", 3)) + self.threshold = threshold # TODO: fail here for type differences st0 = type(s0) @@ -205,26 +206,64 @@ def __str__(self): buff = fil.getvalue().decode("utf-8") return buff - def print_here(self): - d0 = { - k: ( - "{...}" - if isinstance(v, dict) - else v if not isinstance(v, (list, tuple)) else "[...]" - ) - for k, v in self.s0.items() - } - d1 = { - k: ( - "{...}" - if isinstance(v, dict) - else v if not isinstance(v, (list, tuple)) else "[...]" - ) - for k, v in self.s1.items() - } - - pprint(d0, indent=2) - pprint(d1, indent=2) + def print_here(self, line_width=120, print_same=True): + """Print a side-by-side comparison table of s0 and s1 keys.""" + def format_value(v): + if isinstance(v, dict): + return "{...}" + elif isinstance(v, (list, tuple)): + return "[...]" + else: + s = str(v) + return s[:20] + "..." if len(s) > 23 else s + + def values_match(v0, v1): + if type(v0) != type(v1): + return False + if isinstance(v0, (int, float)) and isinstance(v1, (int, float)): + if v0 == v1: + return True + if v0 == 0 and v1 == 0: + return True + if v0 == 0 or v1 == 0: + return False + pct = abs((v1 - v0) / max(abs(v0), abs(v1))) + return pct <= self.threshold + return v0 == v1 + + all_keys = set(self.s0.keys()) | set(self.s1.keys()) + + # Calculate column widths + key_width = max(len(str(k)) for k in all_keys) if all_keys else 10 + match_width = 5 # For " ✓ " or " ✗ " + remaining = line_width - key_width - match_width - 10 # 10 for separators " | " + val_width = remaining // 2 + + # Print header + header = f"{'KEY':<{key_width}} | {'MATCH':^{match_width}} | {'D0':^{val_width}} | {'D1':^{val_width}}" + print(f'DATA FOR PATH: {self.path}') + print(header) + print("-" * line_width) + + # Print rows + for k in sorted(all_keys, key=str): + in_s0 = k in self.s0 + in_s1 = k in self.s1 + v0_raw = self.s0[k] if in_s0 else None + v1_raw = self.s1[k] if in_s1 else None + + if in_s0 and in_s1: + match = values_match(v0_raw, v1_raw) + else: + match = False + + if not print_same and match: + continue + + v0 = format_value(v0_raw) if in_s0 else "" + v1 = format_value(v1_raw) if in_s1 else "" + match_str = " ✓ " if match else " ✗ " + print(f"{str(k):<{key_width}} | {match_str:^{match_width}} | {v0:^{val_width}} | {v1:^{val_width}}") def print_below(self): print(f"## BASE") @@ -355,11 +394,11 @@ def parent_key(key): try: v0_num = float(v0) except (ValueError, TypeError): - pass + continue try: v1_num = float(v1) except (ValueError, TypeError): - pass + continue diff = "-" if v0_num is not None and v1_num is not None: try: @@ -373,7 +412,7 @@ def parent_key(key): pct = abs(diff / v0_num) pct_diff = f"{pct:10.3%}" except Exception: - pass + continue v0s = ( json.dumps(v0, ensure_ascii=False) if not isinstance(v0, str) diff --git a/pyproject.toml b/pyproject.toml index ad7b066..5e8dc51 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "diffgetr" -version = "0.2.1" +version = "0.2.2" description = "A Python library for comparing nested data structures with detailed diff reporting and interactive navigation." authors = [ { name = "Your Name", email = "your.email@example.com" } From 389e999a1a2196241fea6d4054c5d9b97bef04f6 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Wed, 21 Jan 2026 22:30:00 +0000 Subject: [PATCH 3/7] =?UTF-8?q?=F0=9F=A4=96=20Auto-format=20code=20with=20?= =?UTF-8?q?black?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- diffgetr/diff_get.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/diffgetr/diff_get.py b/diffgetr/diff_get.py index 867f3ca..2b22148 100644 --- a/diffgetr/diff_get.py +++ b/diffgetr/diff_get.py @@ -102,16 +102,14 @@ def __getitem__(self, key): # self.diff_data(sys.stdout,bytes=False) self.diff_summary() raise KeyError(f"{self.location} | key missing: {key}") - + def is_same(self) -> bool: df = self.diff_obj if self.ignore_added: - df = {k:v for k,v in df.items() if 'added' not in k} + df = {k: v for k, v in df.items() if "added" not in k} return not bool(df) - - @property def path(self): return ".".join(self.loc) @@ -208,6 +206,7 @@ def __str__(self): def print_here(self, line_width=120, print_same=True): """Print a side-by-side comparison table of s0 and s1 keys.""" + def format_value(v): if isinstance(v, dict): return "{...}" @@ -232,7 +231,7 @@ def values_match(v0, v1): return v0 == v1 all_keys = set(self.s0.keys()) | set(self.s1.keys()) - + # Calculate column widths key_width = max(len(str(k)) for k in all_keys) if all_keys else 10 match_width = 5 # For " ✓ " or " ✗ " @@ -241,7 +240,7 @@ def values_match(v0, v1): # Print header header = f"{'KEY':<{key_width}} | {'MATCH':^{match_width}} | {'D0':^{val_width}} | {'D1':^{val_width}}" - print(f'DATA FOR PATH: {self.path}') + print(f"DATA FOR PATH: {self.path}") print(header) print("-" * line_width) @@ -251,19 +250,21 @@ def values_match(v0, v1): in_s1 = k in self.s1 v0_raw = self.s0[k] if in_s0 else None v1_raw = self.s1[k] if in_s1 else None - + if in_s0 and in_s1: match = values_match(v0_raw, v1_raw) else: match = False - + if not print_same and match: continue - + v0 = format_value(v0_raw) if in_s0 else "" v1 = format_value(v1_raw) if in_s1 else "" match_str = " ✓ " if match else " ✗ " - print(f"{str(k):<{key_width}} | {match_str:^{match_width}} | {v0:^{val_width}} | {v1:^{val_width}}") + print( + f"{str(k):<{key_width}} | {match_str:^{match_width}} | {v0:^{val_width}} | {v1:^{val_width}}" + ) def print_below(self): print(f"## BASE") From 817bbb6eeb1a0ce4821c32ac1690896ca765ccf6 Mon Sep 17 00:00:00 2001 From: SoundsSerious Date: Wed, 21 Jan 2026 18:36:26 -0500 Subject: [PATCH 4/7] Enhance diff_greater function and update is_same method for improved comparison logic --- diffgetr/diff_get.py | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/diffgetr/diff_get.py b/diffgetr/diff_get.py index 867f3ca..4f92438 100644 --- a/diffgetr/diff_get.py +++ b/diffgetr/diff_get.py @@ -6,6 +6,17 @@ import argparse from pprint import pprint +def diff_greater(item_diff,tol): + nv = item_diff['new_value'] + ov = item_diff['old_value'] + + try: + nv = float(nv) + ov = float(ov) + except: + return True + + return abs(nv - ov) > tol class Diffr: @@ -106,12 +117,9 @@ def __getitem__(self, key): def is_same(self) -> bool: df = self.diff_obj - if self.ignore_added: - df = {k:v for k,v in df.items() if 'added' not in k} - return not bool(df) - - + return not bool(df) + @property def path(self): return ".".join(self.loc) @@ -262,7 +270,7 @@ def values_match(v0, v1): v0 = format_value(v0_raw) if in_s0 else "" v1 = format_value(v1_raw) if in_s1 else "" - match_str = " ✓ " if match else " ✗ " + match_str = " Y " if match else " N " print(f"{str(k):<{key_width}} | {match_str:^{match_width}} | {v0:^{val_width}} | {v1:^{val_width}}") def print_below(self): @@ -278,6 +286,13 @@ def diff_obj(self) -> deepdiff.DeepDiff: for k in list(df): if "added" in k: df.pop(k) + + if 'values_changed' in df: + vc = df.pop('values_changed') + vc = {k:v for k,v in vc.items() if diff_greater(v,self.threshold)} + if vc: + df['values_changed'] = vc + return df def diff_all(self, indent=2, file=None): From 5c0413d331c37e2081c349a2577b1f5ccb4bcb7c Mon Sep 17 00:00:00 2001 From: SoundsSerious Date: Wed, 21 Jan 2026 18:37:15 -0500 Subject: [PATCH 5/7] Refactor is_same method and improve print_here output formatting --- diffgetr/diff_get.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/diffgetr/diff_get.py b/diffgetr/diff_get.py index 4f92438..cbdfa22 100644 --- a/diffgetr/diff_get.py +++ b/diffgetr/diff_get.py @@ -113,13 +113,10 @@ def __getitem__(self, key): # self.diff_data(sys.stdout,bytes=False) self.diff_summary() raise KeyError(f"{self.location} | key missing: {key}") - + def is_same(self) -> bool: df = self.diff_obj - - return not bool(df) - @property def path(self): return ".".join(self.loc) @@ -216,6 +213,7 @@ def __str__(self): def print_here(self, line_width=120, print_same=True): """Print a side-by-side comparison table of s0 and s1 keys.""" + def format_value(v): if isinstance(v, dict): return "{...}" @@ -240,7 +238,7 @@ def values_match(v0, v1): return v0 == v1 all_keys = set(self.s0.keys()) | set(self.s1.keys()) - + # Calculate column widths key_width = max(len(str(k)) for k in all_keys) if all_keys else 10 match_width = 5 # For " ✓ " or " ✗ " @@ -249,7 +247,7 @@ def values_match(v0, v1): # Print header header = f"{'KEY':<{key_width}} | {'MATCH':^{match_width}} | {'D0':^{val_width}} | {'D1':^{val_width}}" - print(f'DATA FOR PATH: {self.path}') + print(f"DATA FOR PATH: {self.path}") print(header) print("-" * line_width) @@ -259,19 +257,22 @@ def values_match(v0, v1): in_s1 = k in self.s1 v0_raw = self.s0[k] if in_s0 else None v1_raw = self.s1[k] if in_s1 else None - + if in_s0 and in_s1: match = values_match(v0_raw, v1_raw) else: match = False - + if not print_same and match: continue - + v0 = format_value(v0_raw) if in_s0 else "" v1 = format_value(v1_raw) if in_s1 else "" - match_str = " Y " if match else " N " - print(f"{str(k):<{key_width}} | {match_str:^{match_width}} | {v0:^{val_width}} | {v1:^{val_width}}") + + match_str = " ✓ " if match else " ✗ " + print( + f"{str(k):<{key_width}} | {match_str:^{match_width}} | {v0:^{val_width}} | {v1:^{val_width}}" + ) def print_below(self): print(f"## BASE") From 7f6ac4e7c4e03fe2425a953ab1a422ffe3649c63 Mon Sep 17 00:00:00 2001 From: SoundsSerious Date: Thu, 22 Jan 2026 19:36:03 -0500 Subject: [PATCH 6/7] Update import statement for Diffr and adjust significant digits in test case --- README.md | 2 +- diffgetr/__init__.py | 1 + diffgetr/tests/test_diff_get.py | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5a82b23..2b29c82 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ pip install . #### Basic Usage ```python -from diffgetr.diff_get import Diffr +from diffgetr import Diffr # Basic comparison diff = Diffr(obj1, obj2) diff --git a/diffgetr/__init__.py b/diffgetr/__init__.py index e69de29..f32a879 100644 --- a/diffgetr/__init__.py +++ b/diffgetr/__init__.py @@ -0,0 +1 @@ +from diffgetr.diff_get import Diffr \ No newline at end of file diff --git a/diffgetr/tests/test_diff_get.py b/diffgetr/tests/test_diff_get.py index d0319ac..457268b 100644 --- a/diffgetr/tests/test_diff_get.py +++ b/diffgetr/tests/test_diff_get.py @@ -72,7 +72,7 @@ def test_custom_deep_diff_params(self): assert len(diff1.diff_obj) == 0 # With high precision, should see difference - diff2 = Diffr(s0, s1, deep_diff_kw={"significant_digits": 6}) + diff2 = Diffr(s0, s1, deep_diff_kw={"significant_digits": 9}) assert len(diff2.diff_obj) > 0 def test_keyerror_handling(self): From 1a407e10d7b67cde74ea503585b01a2dde9c6822 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Fri, 23 Jan 2026 00:37:16 +0000 Subject: [PATCH 7/7] =?UTF-8?q?=F0=9F=A4=96=20Auto-format=20code=20with=20?= =?UTF-8?q?black?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- diffgetr/__init__.py | 2 +- diffgetr/diff_get.py | 18 ++++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/diffgetr/__init__.py b/diffgetr/__init__.py index f32a879..f0c5ac0 100644 --- a/diffgetr/__init__.py +++ b/diffgetr/__init__.py @@ -1 +1 @@ -from diffgetr.diff_get import Diffr \ No newline at end of file +from diffgetr.diff_get import Diffr diff --git a/diffgetr/diff_get.py b/diffgetr/diff_get.py index cf7e843..b959590 100644 --- a/diffgetr/diff_get.py +++ b/diffgetr/diff_get.py @@ -6,9 +6,10 @@ import argparse from pprint import pprint -def diff_greater(item_diff,tol): - nv = item_diff['new_value'] - ov = item_diff['old_value'] + +def diff_greater(item_diff, tol): + nv = item_diff["new_value"] + ov = item_diff["old_value"] try: nv = float(nv) @@ -18,6 +19,7 @@ def diff_greater(item_diff,tol): return abs(nv - ov) > tol + class Diffr: def __init__( @@ -289,12 +291,12 @@ def diff_obj(self) -> deepdiff.DeepDiff: if "added" in k: df.pop(k) - if 'values_changed' in df: - vc = df.pop('values_changed') - vc = {k:v for k,v in vc.items() if diff_greater(v,self.threshold)} + if "values_changed" in df: + vc = df.pop("values_changed") + vc = {k: v for k, v in vc.items() if diff_greater(v, self.threshold)} if vc: - df['values_changed'] = vc - + df["values_changed"] = vc + return df def diff_all(self, indent=2, file=None):