Claude Code for deepdiff: Deep Comparison and Diff in Python — Claude Skills 360 Blog
Blog / AI / Claude Code for deepdiff: Deep Comparison and Diff in Python
AI

Claude Code for deepdiff: Deep Comparison and Diff in Python

Published: April 8, 2028
Read time: 5 min read
By: Claude Skills 360

deepdiff compares nested Python objects — dicts, lists, sets, and custom classes. pip install deepdiff. Basic: from deepdiff import DeepDiff; diff = DeepDiff(t1, t2). Result keys: type_changes, values_changed, dictionary_item_added, dictionary_item_removed, iterable_item_added, iterable_item_removed, set_item_added, set_item_removed, attribute_added, attribute_removed. Ignore order: DeepDiff(t1, t2, ignore_order=True). Floats: DeepDiff(t1, t2, significant_digits=2). Exclude: DeepDiff(t1, t2, exclude_paths=["root['ts']"]). Regex exclude: exclude_regex_paths=[r"root\['\w+_at'\]"]. Flat view: DeepDiff(t1, t2, verbose_level=2). Text: diff.to_json(). Delta: from deepdiff import Delta; delta = Delta(diff); t1 + delta == t2. Reverse: t2 - delta == t1. DeepHash: from deepdiff import DeepHash; dh = DeepHash(obj); dh[obj]. Grep: from deepdiff import grep; result = t1 | grep("needle"). Custom: DeepDiff(t1, t2, custom_operators=[IsEmptyOperator()]). Report: diff.pretty(). Tree view: diff.tree. Claude Code generates deepdiff comparison helpers, test assertion utilities, and change-detection pipelines.

CLAUDE.md for deepdiff

## deepdiff Stack
- Version: deepdiff >= 6.7 | pip install deepdiff
- Compare: DeepDiff(t1, t2) → diff dict with change-type keys
- Ignore order: DeepDiff(t1, t2, ignore_order=True) — treat lists as sets
- Exclude: exclude_paths=["root['key']"] | exclude_regex_paths=[r"root\['ts'\]"]
- Floats: significant_digits=2 — tolerance-based float comparison
- Delta: Delta(diff) | t1 + delta → t2 | t2 - delta → t1
- DeepHash: DeepHash(obj)[obj] — stable content hash for any object

deepdiff Comparison Pipeline

# app/diff_utils.py — deepdiff comparison, delta, deephash, and test helpers
from __future__ import annotations

import json
import re
from typing import Any

from deepdiff import DeepDiff, DeepHash, Delta, grep


# ─────────────────────────────────────────────────────────────────────────────
# 1. Core comparison helpers
# ─────────────────────────────────────────────────────────────────────────────

def diff(
    t1: Any,
    t2: Any,
    ignore_order: bool = False,
    significant_digits: int | None = None,
    exclude_paths: list[str] | None = None,
    exclude_regex_paths: list[str] | None = None,
    ignore_string_case: bool = False,
    verbose_level: int = 1,
) -> DeepDiff:
    """
    Compare two objects and return a DeepDiff result.
    Returns an empty dict-like object when t1 == t2 (no diff).
    """
    kwargs: dict[str, Any] = {
        "ignore_order": ignore_order,
        "verbose_level": verbose_level,
    }
    if significant_digits is not None:
        kwargs["significant_digits"] = significant_digits
    if exclude_paths:
        kwargs["exclude_paths"] = exclude_paths
    if exclude_regex_paths:
        kwargs["exclude_regex_paths"] = exclude_regex_paths
    if ignore_string_case:
        kwargs["ignore_string_case"] = True
    return DeepDiff(t1, t2, **kwargs)


def is_equal(
    t1: Any,
    t2: Any,
    ignore_order: bool = False,
    significant_digits: int | None = None,
) -> bool:
    """Return True if t1 and t2 are deeply equal."""
    return not diff(
        t1, t2,
        ignore_order=ignore_order,
        significant_digits=significant_digits,
    )


def changed_paths(d: DeepDiff) -> list[str]:
    """Return a flat list of all changed paths from a DeepDiff result."""
    paths: list[str] = []
    for change_type, changes in d.items():
        if isinstance(changes, dict):
            paths.extend(changes.keys())
        elif isinstance(changes, set):
            paths.extend(str(x) for x in changes)
    return sorted(paths)


def summary(d: DeepDiff) -> dict[str, int]:
    """Count changes by type."""
    return {k: len(v) for k, v in d.items()}


def to_json(d: DeepDiff, indent: int = 2) -> str:
    """Serialize a DeepDiff result to JSON string."""
    return d.to_json(indent=indent)


def from_json(json_str: str) -> DeepDiff:
    """Restore a DeepDiff result from its JSON representation."""
    return DeepDiff.from_json(json_str)


# ─────────────────────────────────────────────────────────────────────────────
# 2. Delta — apply and reverse diffs
# ─────────────────────────────────────────────────────────────────────────────

def make_delta(d: DeepDiff) -> Delta:
    """
    Create a Delta from a DeepDiff result.
    Delta supports + (apply forward) and - (apply backward) operators.

    Example:
        d    = diff(old, new)
        delt = make_delta(d)
        assert old + delt == new
        assert new - delt == old
    """
    return Delta(d)


def apply_delta(obj: Any, d: DeepDiff) -> Any:
    """Apply a diff forward — transform obj toward the second value."""
    return obj + make_delta(d)


def revert_delta(obj: Any, d: DeepDiff) -> Any:
    """Apply a diff backward — revert obj to the first value."""
    return obj - make_delta(d)


def patch(original: Any, serialized_delta: str) -> Any:
    """
    Apply a serialized delta (JSON string from delta.to_json()) to original.
    Useful for transmitting diffs over a network.
    """
    delt = Delta(delta_dict=json.loads(serialized_delta))
    return original + delt


# ─────────────────────────────────────────────────────────────────────────────
# 3. DeepHash — stable content hashing
# ─────────────────────────────────────────────────────────────────────────────

def content_hash(obj: Any, ignore_order: bool = True) -> str:
    """
    Compute a stable hash string for any Python object.
    Useful for content-addressable storage, cache keys, and deduplication.
    ignore_order=True: treats lists as sets when hashing.
    """
    dh = DeepHash(obj, ignore_order=ignore_order)
    return dh[obj]


def objects_have_same_content(
    a: Any,
    b: Any,
    ignore_order: bool = True,
) -> bool:
    """Return True if a and b have the same content hash."""
    return content_hash(a, ignore_order) == content_hash(b, ignore_order)


def deduplicate(items: list[Any], ignore_order: bool = True) -> list[Any]:
    """
    Remove duplicate items from a list by content hash.
    Preserves first occurrence order.
    """
    seen: set[str] = set()
    result: list[Any] = []
    for item in items:
        h = content_hash(item, ignore_order)
        if h not in seen:
            seen.add(h)
            result.append(item)
    return result


# ─────────────────────────────────────────────────────────────────────────────
# 4. Grep — search inside nested structures
# ─────────────────────────────────────────────────────────────────────────────

def search(obj: Any, needle: Any, verbose_level: int = 2) -> dict:
    """
    Search for needle inside a nested structure using deepdiff grep.
    Returns dict with 'matched_values' and 'matched_keys'.

    Example:
        result = search(data, "admin")
        print(result["matched_values"])  # paths where value == "admin"
    """
    return obj | grep(needle, verbose_level=verbose_level)


def find_paths(obj: Any, needle: Any) -> list[str]:
    """Return all paths where needle appears as a value."""
    result = search(obj, needle)
    matched = result.get("matched_values", {})
    if hasattr(matched, "keys"):
        return list(matched.keys())
    return [str(x) for x in matched]


# ─────────────────────────────────────────────────────────────────────────────
# 5. Config / record diff helpers
# ─────────────────────────────────────────────────────────────────────────────

_TS_PATTERN = re.compile(r"_at$|_ts$|timestamp|created|updated")


def diff_configs(old: dict, new: dict) -> DeepDiff:
    """
    Compare two config dicts, ignoring timestamp-like keys.
    """
    return diff(
        old, new,
        exclude_regex_paths=[r"root\['" + _TS_PATTERN.pattern + r"'\]"],
    )


def diff_records(
    old: list[dict],
    new: list[dict],
    key: str = "id",
) -> dict[str, list[dict]]:
    """
    Compare two lists of dicts (records) keyed by `key`.
    Returns {'added': [...], 'removed': [...], 'changed': [...]}.
    """
    old_map = {r[key]: r for r in old if key in r}
    new_map = {r[key]: r for r in new if key in r}

    added   = [new_map[k] for k in new_map if k not in old_map]
    removed = [old_map[k] for k in old_map if k not in new_map]
    changed = [
        {"id": k, "diff": diff(old_map[k], new_map[k])}
        for k in old_map
        if k in new_map and diff(old_map[k], new_map[k])
    ]
    return {"added": added, "removed": removed, "changed": changed}


# ─────────────────────────────────────────────────────────────────────────────
# 6. Test assertion helpers
# ─────────────────────────────────────────────────────────────────────────────

class DiffAssertions:
    """
    Mixin for test classes — provides assert_deep_equal and assert_changed.

    Usage (pytest / unittest):
        class TestMyService(DiffAssertions):
            def test_update(self):
                self.assert_deep_equal(result, expected, ignore_order=True)
    """

    def assert_deep_equal(
        self,
        actual: Any,
        expected: Any,
        ignore_order: bool = False,
        significant_digits: int | None = None,
        exclude_paths: list[str] | None = None,
        msg: str | None = None,
    ) -> None:
        d = diff(
            expected, actual,
            ignore_order=ignore_order,
            significant_digits=significant_digits,
            exclude_paths=exclude_paths,
        )
        if d:
            error = msg or f"Objects differ:\n{d.pretty()}"
            raise AssertionError(error)

    def assert_changed(
        self,
        before: Any,
        after: Any,
        expected_changes: list[str],
    ) -> None:
        """Assert that exactly the listed paths changed."""
        d = diff(before, after)
        actual = set(changed_paths(d))
        expected = set(expected_changes)
        missing  = expected - actual
        extra    = actual - expected
        if missing or extra:
            raise AssertionError(
                f"Change mismatch.\nMissing: {missing}\nUnexpected: {extra}\nDiff: {d.pretty()}"
            )


def assert_deep_equal(
    actual: Any,
    expected: Any,
    ignore_order: bool = False,
    significant_digits: int | None = None,
    exclude_paths: list[str] | None = None,
) -> None:
    """Standalone assert — raises AssertionError with pretty diff on failure."""
    DiffAssertions().assert_deep_equal(
        actual, expected,
        ignore_order=ignore_order,
        significant_digits=significant_digits,
        exclude_paths=exclude_paths,
    )


# ─────────────────────────────────────────────────────────────────────────────
# Demo
# ─────────────────────────────────────────────────────────────────────────────

if __name__ == "__main__":
    print("=== Basic diff ===")
    t1 = {"name": "Alice", "age": 30, "scores": [10, 20, 30], "city": "NYC"}
    t2 = {"name": "Alice", "age": 31, "scores": [10, 25, 30], "country": "US"}
    d = diff(t1, t2)
    print(d.pretty())
    print("Summary:", summary(d))

    print("\n=== Ignore order ===")
    a = {"tags": ["python", "api", "fast"]}
    b = {"tags": ["fast", "python", "api"]}
    print("With order:   ", bool(diff(a, b)))
    print("Ignore order: ", bool(diff(a, b, ignore_order=True)))

    print("\n=== Float tolerance ===")
    x = {"pi": 3.14159}
    y = {"pi": 3.14200}
    print("Exact:       ", bool(diff(x, y)))
    print("2 sig digits:", bool(diff(x, y, significant_digits=2)))

    print("\n=== Delta (apply + revert) ===")
    old = {"version": 1, "config": {"debug": False, "workers": 4}}
    new = {"version": 2, "config": {"debug": True,  "workers": 8}}
    d = diff(old, new)
    restored = revert_delta(new, d)
    print("Applied delta → old:", apply_delta(old, d) == new)
    print("Reverted delta → old:", restored == old)

    print("\n=== DeepHash deduplication ===")
    records = [
        {"id": 1, "val": "a"},
        {"id": 2, "val": "b"},
        {"id": 1, "val": "a"},  # duplicate
        {"id": 3, "val": "c"},
    ]
    unique = deduplicate(records)
    print(f"Original: {len(records)}, Unique: {len(unique)}")

    print("\n=== Grep / search ===")
    data = {"users": [{"role": "admin", "name": "Bob"}, {"role": "user", "name": "Alice"}]}
    paths = find_paths(data, "admin")
    print(f"'admin' found at: {paths}")

    print("\n=== Record diff ===")
    old_records = [
        {"id": 1, "name": "Alice", "age": 30},
        {"id": 2, "name": "Bob",   "age": 25},
        {"id": 3, "name": "Carol", "age": 28},
    ]
    new_records = [
        {"id": 1, "name": "Alice", "age": 31},   # changed
        {"id": 3, "name": "Carol", "age": 28},   # unchanged
        {"id": 4, "name": "Dave",  "age": 35},   # added
    ]
    changes = diff_records(old_records, new_records)
    print(f"  Added:   {[r['id'] for r in changes['added']]}")
    print(f"  Removed: {[r['id'] for r in changes['removed']]}")
    print(f"  Changed: {[c['id'] for c in changes['changed']]}")

For the jsondiff alternative — jsondiff is JSON-specific and works well for simple JSON string diffs; deepdiff handles arbitrary Python objects (dataclasses, custom classes, sets, tuples, numpy arrays) with richer change metadata, Delta for patching, and DeepHash for content-based hashing. For the dictdiffer alternative — dictdiffer is lightweight and produces patch-format output; deepdiff gives structured change types (values_changed, dictionary_item_added, type_changes), supports ignore_order, significant_digits for floats, and supports reversible Deltas — making it better suited for data pipelines and test assertions. The Claude Skills 360 bundle includes deepdiff skill sets covering DeepDiff() core comparison, ignore_order/significant_digits/exclude_paths options, changed_paths()/summary() helpers, to_json()/from_json() serialization, Delta make/apply/revert, content_hash() and deduplicate() via DeepHash, search()/find_paths() grep, diff_configs() with timestamp exclusion, diff_records() keyed record comparison, and DiffAssertions test mixin. Start with the free tier to try deep comparison code generation.

Keep Reading

AI

Claude Code for email.contentmanager: Python Email Content Accessors

Read and write EmailMessage body content with Python's email.contentmanager module and Claude Code — email contentmanager ContentManager for the class that maps content types to get and set handler functions allowing EmailMessage to support get_content and set_content with type-specific behaviour, email contentmanager raw_data_manager for the ContentManager instance that handles raw bytes and str payloads without any conversion, email contentmanager content_manager for the standard ContentManager instance used by email.policy.default that intelligently handles text plain text html multipart and binary content types, email contentmanager get_content_text for the handler that returns the decoded text payload of a text-star message part as a str, email contentmanager get_content_binary for the handler that returns the raw decoded bytes payload of a non-text message part, email contentmanager get_data_manager for the get-handler lookup used by EmailMessage get_content to find the right reader function for the content type, email contentmanager set_content text for the handler that creates and sets a text part correctly choosing charset and transfer encoding, email contentmanager set_content bytes for the handler that creates and sets a binary part with base64 encoding and optional filename Content-Disposition, email contentmanager EmailMessage get_content for the method that reads the message body using the registered content manager handlers, email contentmanager EmailMessage set_content for the method that sets the message body and MIME headers in one call, email contentmanager EmailMessage make_alternative make_mixed make_related for the methods that convert a simple message into a multipart container, email contentmanager EmailMessage add_attachment for the method that attaches a file or bytes to a multipart message, and email contentmanager integration with email.message and email.policy and email.mime and io for building high-level email readers attachment extractors text body accessors HTML readers and policy-aware MIME construction pipelines.

5 min read Feb 12, 2029
AI

Claude Code for email.charset: Python Email Charset Encoding

Control header and body encoding for international email with Python's email.charset module and Claude Code — email charset Charset for the class that wraps a character set name with the encoding rules for header encoding and body encoding describing how to encode text for that charset in email messages, email charset Charset header_encoding for the attribute specifying whether headers using this charset should use QP quoted-printable encoding BASE64 encoding or no encoding, email charset Charset body_encoding for the attribute specifying the Content-Transfer-Encoding to use for message bodies in this charset such as QP or BASE64, email charset Charset output_codec for the attribute giving the Python codec name used to encode the string to bytes for the wire format, email charset Charset input_codec for the attribute giving the Python codec name used to decode incoming bytes to str, email charset Charset get_output_charset for returning the output charset name, email charset Charset header_encode for encoding a header string using the charset's header_encoding method, email charset Charset body_encode for encoding body content using the charset's body_encoding, email charset Charset convert for converting a string from the input_codec to the output_codec, email charset add_charset for registering a new charset with custom encoding rules in the global charset registry, email charset add_alias for adding an alias name that maps to an existing registered charset, email charset add_codec for registering a codec name mapping for use by the charset machinery, and email charset integration with email.message and email.mime and email.policy and email.encoders for building international email senders non-ASCII header encoders Content-Transfer-Encoding selectors charset-aware message constructors and MIME encoding pipelines.

5 min read Feb 11, 2029
AI

Claude Code for email.utils: Python Email Address and Header Utilities

Parse and format RFC 2822 email addresses and dates with Python's email.utils module and Claude Code — email utils parseaddr for splitting a display-name plus angle-bracket address string into a realname and email address tuple, email utils formataddr for combining a realname and address string into a properly quoted RFC 2822 address with angle brackets, email utils getaddresses for parsing a list of raw address header strings each potentially containing multiple comma-separated addresses into a list of realname address tuples, email utils parsedate for parsing an RFC 2822 date string into a nine-tuple compatible with time.mktime, email utils parsedate_tz for parsing an RFC 2822 date string into a ten-tuple that includes the UTC offset timezone in seconds, email utils parsedate_to_datetime for parsing an RFC 2822 date string into an aware datetime object with timezone, email utils formatdate for formatting a POSIX timestamp or the current time as an RFC 2822 date string with optional usegmt and localtime flags, email utils format_datetime for formatting a datetime object as an RFC 2822 date string, email utils make_msgid for generating a globally unique Message-ID string with optional idstring and domain components, email utils decode_rfc2231 for decoding an RFC 2231 encoded parameter value into a tuple of charset language and value, email utils encode_rfc2231 for encoding a string as an RFC 2231 encoded parameter value, email utils collapse_rfc2231_value for collapsing a decoded RFC 2231 tuple to a Unicode string, and email utils integration with email.message and email.headerregistry and datetime and time for building address parsers date formatters message-id generators header extractors and RFC-compliant email construction utilities.

5 min read Feb 10, 2029

Put these ideas into practice

Claude Skills 360 gives you production-ready skills for everything in this article — and 2,350+ more. Start free or go all-in.

Back to Blog

Get 360 skills free