Skip to main content

Structural code fingerprinting and diffing — Python bindings

Project description

sigil-diff

Python bindings for sigil — structural code fingerprinting and diffing.

pip install sigil-diff
import sigil

Powered by Rust via PyO3. Native speed, no subprocess overhead, works with in-memory strings.

Quick Start

import sigil

old = '{"body": {"text": "Hello world"}, "header": {"text": ""}}'
new = '{"body": {"text": "Hello universe"}, "header": {"text": ""}}'

result = sigil.diff_json(old, new)

for f in result["files"]:
    for entity in f["entities"]:
        print(f"{entity['change']}: {entity['name']}")
        for tc in entity.get("token_changes", []):
            print(f"  \"{tc['from']}\" -> \"{tc['to']}\"")

Output:

modified: body.text
  ""text": "Hello world"," -> ""text": "Hello universe","

API

sigil.diff_json(old: str, new: str) -> dict

Diff two JSON strings. Returns a structured dict with all changes.

This is the primary function for comparing JSON documents (notification templates, config files, API responses, etc.). Minified JSON is automatically formatted before diffing.

result = sigil.diff_json(old_json_str, new_json_str)

Features applied automatically:

  • Parent-aware matching (no cross-matching between body.text and header.text)
  • Derived field suppression (_-prefixed fields hidden from output)
  • Array item identity matching (objects matched by id/key/name/text/type)
  • Qualified names (body.text instead of bare text)
  • Parent entity suppression (when children carry the detail)

sigil.diff_files(old_path: str, new_path: str) -> dict

Diff two files on disk. Supports JSON, YAML, TOML, Markdown, Python, Rust, Go, JavaScript, TypeScript, Java, Ruby, C, C++, C#, Swift, Kotlin.

result = sigil.diff_files("templates/old.json", "templates/new.json")

sigil.diff_refs(repo_path: str, base_ref: str, head_ref: str) -> dict

Diff two git refs in a repository. Works with any ref format: commit SHAs, branch names, tags, HEAD~N.

result = sigil.diff_refs("/path/to/repo", "main", "feature-branch")
result = sigil.diff_refs(".", "HEAD~1", "HEAD")

sigil.index_json(source: str) -> list[dict]

Parse a JSON string into structural entities. Returns a list of entity dicts.

entities = sigil.index_json('{"name": "test", "tags": ["a", "b"], "_internal": "hidden"}')

for e in entities:
    derived = " (derived)" if e.get("meta") and "derived" in e["meta"] else ""
    parent = f" -> {e['parent']}" if e.get("parent") else ""
    print(f"{e['name']}: {e['kind']}{parent}{derived}")

Output:

name: property
tags: array
[0]: element -> tags
[1]: element -> tags
_internal: property (derived)

Return Format

All diff functions return a dict with this structure:

{
    "meta": {
        "base_ref": "old",
        "head_ref": "new",
        "sigil_version": "0.2.4"
    },
    "summary": {
        "files_changed": 1,
        "added": 0,
        "removed": 0,
        "modified": 1,
        "renamed": 0,
        "has_breaking": False,
        "natural_language": "1 modified."
    },
    "files": [
        {
            "file": "doc.json",
            "summary": {"added": 0, "modified": 1, "removed": 0, ...},
            "entities": [
                {
                    "change": "modified",       # added | removed | modified | renamed | formatting_only
                    "name": "body.text",         # qualified name for JSON entities
                    "kind": "property",          # property | object | array | element | function | class | ...
                    "line": 3,
                    "line_end": 3,
                    "sig_changed": False,        # signature (key name/type) changed?
                    "body_changed": True,         # value changed?
                    "breaking": False,
                    "token_changes": [
                        {
                            "type": "value_changed",
                            "from": "Hello world",
                            "to": "Hello universe"
                        }
                    ],
                    "context": {                  # source snippets for display
                        "hunks": [
                            {"kind": "removed", "text": "..."},
                            {"kind": "added", "text": "..."}
                        ]
                    }
                }
            ]
        }
    ],
    "breaking": [],       # list of breaking change entries
    "patterns": [],       # cross-file patterns (e.g., same rename across files)
    "moves": []           # entities moved between files
}

Examples

Compare notification templates

import sigil
import json

old_template = json.dumps({
    "body": {
        "text": "Hi {{user.name}}, your order #{{order_id}} is confirmed.",
        "_parsed_text": "Hi {{1}}, your order #{{2}} is confirmed.",
        "_examples": ["John", "12345"]
    },
    "buttons": [
        {"text": "Track Order", "type": "URL"}
    ]
})

new_template = json.dumps({
    "body": {
        "text": "Hi {{user.name}}, order #{{order_id}} is on its way!",
        "_parsed_text": "Hi {{1}}, order #{{2}} is on its way!",
        "_examples": ["John", "12345"]
    },
    "buttons": [
        {"text": "Track Order", "type": "URL"}
    ]
})

result = sigil.diff_json(old_template, new_template)

if result["summary"]["modified"] > 0:
    for f in result["files"]:
        for e in f["entities"]:
            print(f"Changed: {e['name']}")
            for tc in e.get("token_changes", []):
                print(f"  {tc['from']}")
                print(f"  {tc['to']}")

Check if two documents are identical

result = sigil.diff_json(old_str, new_str)
is_identical = result["summary"]["modified"] == 0 and result["summary"]["added"] == 0 and result["summary"]["removed"] == 0

Get changed field names

result = sigil.diff_json(old_str, new_str)
changed_fields = [
    e["name"]
    for f in result["files"]
    for e in f["entities"]
]

Development

# Clone the repo
git clone https://github.com/gauravverma/sigil.git
cd sigil/python

# Create venv and build
python3 -m venv .venv
source .venv/bin/activate
pip install maturin
maturin develop

# Test
python3 -c "import sigil; print(sigil.diff_json('{\"a\":1}', '{\"a\":2}'))"

Build a wheel

maturin build --release
# Output: target/wheels/sigil_diff-0.2.4-cp3XX-*.whl

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

sigil_diff-0.3.0.tar.gz (621.0 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

sigil_diff-0.3.0-cp313-cp313-manylinux_2_28_x86_64.whl (2.8 MB view details)

Uploaded CPython 3.13manylinux: glibc 2.28+ x86-64

sigil_diff-0.3.0-cp313-cp313-manylinux_2_28_aarch64.whl (2.7 MB view details)

Uploaded CPython 3.13manylinux: glibc 2.28+ ARM64

sigil_diff-0.3.0-cp312-cp312-manylinux_2_28_x86_64.whl (2.8 MB view details)

Uploaded CPython 3.12manylinux: glibc 2.28+ x86-64

sigil_diff-0.3.0-cp312-cp312-manylinux_2_28_aarch64.whl (2.7 MB view details)

Uploaded CPython 3.12manylinux: glibc 2.28+ ARM64

sigil_diff-0.3.0-cp311-cp311-win_amd64.whl (2.5 MB view details)

Uploaded CPython 3.11Windows x86-64

sigil_diff-0.3.0-cp311-cp311-manylinux_2_28_x86_64.whl (2.8 MB view details)

Uploaded CPython 3.11manylinux: glibc 2.28+ x86-64

sigil_diff-0.3.0-cp311-cp311-manylinux_2_28_aarch64.whl (2.7 MB view details)

Uploaded CPython 3.11manylinux: glibc 2.28+ ARM64

sigil_diff-0.3.0-cp311-cp311-macosx_11_0_arm64.whl (2.7 MB view details)

Uploaded CPython 3.11macOS 11.0+ ARM64

sigil_diff-0.3.0-cp311-cp311-macosx_10_12_x86_64.whl (2.6 MB view details)

Uploaded CPython 3.11macOS 10.12+ x86-64

sigil_diff-0.3.0-cp310-cp310-manylinux_2_28_x86_64.whl (2.8 MB view details)

Uploaded CPython 3.10manylinux: glibc 2.28+ x86-64

sigil_diff-0.3.0-cp310-cp310-manylinux_2_28_aarch64.whl (2.7 MB view details)

Uploaded CPython 3.10manylinux: glibc 2.28+ ARM64

sigil_diff-0.3.0-cp39-cp39-manylinux_2_28_x86_64.whl (2.8 MB view details)

Uploaded CPython 3.9manylinux: glibc 2.28+ x86-64

sigil_diff-0.3.0-cp39-cp39-manylinux_2_28_aarch64.whl (2.7 MB view details)

Uploaded CPython 3.9manylinux: glibc 2.28+ ARM64

sigil_diff-0.3.0-cp38-cp38-manylinux_2_28_x86_64.whl (2.8 MB view details)

Uploaded CPython 3.8manylinux: glibc 2.28+ x86-64

sigil_diff-0.3.0-cp38-cp38-manylinux_2_28_aarch64.whl (2.7 MB view details)

Uploaded CPython 3.8manylinux: glibc 2.28+ ARM64

File details

Details for the file sigil_diff-0.3.0.tar.gz.

File metadata

  • Download URL: sigil_diff-0.3.0.tar.gz
  • Upload date:
  • Size: 621.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: maturin/1.13.1

File hashes

Hashes for sigil_diff-0.3.0.tar.gz
Algorithm Hash digest
SHA256 2bd8f5d8a968d150aa265d24996502e48b10bbfc7dd7d8e5c0879b4f3b0850bd
MD5 1a8e3e8d1fa1b5bc61a0aeeb24f82ef7
BLAKE2b-256 7b6152785e5dd7b8bc0d284243e1308efcd33328c436913e8608837b94de6dfc

See more details on using hashes here.

File details

Details for the file sigil_diff-0.3.0-cp313-cp313-manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for sigil_diff-0.3.0-cp313-cp313-manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 3d61c308e2c249c342aa5309caad1036940cedfb5b7d1862e36c57887db8cece
MD5 f965f8ae47e461151027eacabdce004a
BLAKE2b-256 d8e6c0947ea56ea99656c95ca90b3b84c01a02e1d5e1a0c5b02d0d3b6ac186bb

See more details on using hashes here.

File details

Details for the file sigil_diff-0.3.0-cp313-cp313-manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for sigil_diff-0.3.0-cp313-cp313-manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 80d9e4743ebcdb2f0a70fb61bbe975c4b680eac8f600c6e6c4879a2ee2afa971
MD5 a8c877a906e71c02690f813228963862
BLAKE2b-256 4a2c991dd12bf9a1825bf44ce8e0d6fe21f97d20fd77856acbc6bcc3b909f39a

See more details on using hashes here.

File details

Details for the file sigil_diff-0.3.0-cp312-cp312-manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for sigil_diff-0.3.0-cp312-cp312-manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 30c36a6cf0d06feaa4a617b5afb8556c439076f57a119b5c59afa17ff101b05c
MD5 543b79f3762c7aede8f5b10871067d41
BLAKE2b-256 9dcb1fd1ba308d52bdaacf629886c8d7bad0e038f689d3d3eb60c0fb438ff8c3

See more details on using hashes here.

File details

Details for the file sigil_diff-0.3.0-cp312-cp312-manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for sigil_diff-0.3.0-cp312-cp312-manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 090c52e045860901fa1a14824ebffe802271dc82df24a6cb81944dccf72d90e1
MD5 b2a6283032a292cc80e56793f817906c
BLAKE2b-256 d3f6937bc648ebf6634b7d2c395a227b6044f3eb346294691097e59a73da08b4

See more details on using hashes here.

File details

Details for the file sigil_diff-0.3.0-cp311-cp311-win_amd64.whl.

File metadata

File hashes

Hashes for sigil_diff-0.3.0-cp311-cp311-win_amd64.whl
Algorithm Hash digest
SHA256 80f70fb1b51365cb5777efa9edd9ab08c7cb0d5cafe4913e97f3ed353e46f2b5
MD5 8bb1f031db9c0dd90f5c64f7716346c6
BLAKE2b-256 e9cf3cee748c8573f0aced4d364cd20df6a568bc500003f3bc67f11124046e94

See more details on using hashes here.

File details

Details for the file sigil_diff-0.3.0-cp311-cp311-manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for sigil_diff-0.3.0-cp311-cp311-manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 4c1ecde858378daa9c2ada289d98d8e07093d9f0d4b7e07ab2deaf4f3be0f6bb
MD5 3b63eb762f496b137dc3fda13c02b707
BLAKE2b-256 96ca5548f2d41a329f08d7c33178ecb3b5c749b5782ebb79a04809d1566cad68

See more details on using hashes here.

File details

Details for the file sigil_diff-0.3.0-cp311-cp311-manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for sigil_diff-0.3.0-cp311-cp311-manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 466cd7c8dd4e6ec5d8baea98f4b1a011ff653ffa109a7e1d0334a74a5c50ebbe
MD5 a0d599ef4a5486d7e12aab8f2c272c16
BLAKE2b-256 3efe0792adce0544520c607c5c7ecb31095f32a15aad1d3999921ed5293e4bf5

See more details on using hashes here.

File details

Details for the file sigil_diff-0.3.0-cp311-cp311-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for sigil_diff-0.3.0-cp311-cp311-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 f189e53de251c391a1581b0d8c2dcb28cec798fbadfb2537813bbb1e0e7d8c89
MD5 fa5e5fcb06a4b6d9d45c473c82d455e3
BLAKE2b-256 49b5d80b9124cd995a0a6f90a15dc2c8630cee7d9221755cf92c0fa5fd5665b7

See more details on using hashes here.

File details

Details for the file sigil_diff-0.3.0-cp311-cp311-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for sigil_diff-0.3.0-cp311-cp311-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 786ccd2face81ba9d2aff4f880d3e72d7fe6671854dff55fdb4f2ad92bfb216a
MD5 f06fb8c62a7fd04095bb53f52f20dbf2
BLAKE2b-256 dc9db1864a8f21ef51eed36f8fb38db3b8b5455e0d397b0af70107ca7a3c7d0f

See more details on using hashes here.

File details

Details for the file sigil_diff-0.3.0-cp310-cp310-manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for sigil_diff-0.3.0-cp310-cp310-manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 4baa3e3e0875933ace05c5b2e070f3eedec133c10e20dd5a5e5134a76c2eb109
MD5 defda3044cf7747d9cdc652f66ff94ab
BLAKE2b-256 7de98bfaeb025a8d697e6377a89b394c8d8b68455af3c0c62301fc0631214c69

See more details on using hashes here.

File details

Details for the file sigil_diff-0.3.0-cp310-cp310-manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for sigil_diff-0.3.0-cp310-cp310-manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 b130ef1daa1073c8037adc401b458374209c60b5ae57f8f479fffd9c693c9ea0
MD5 a7b98dd594a97275d02000f678b278f1
BLAKE2b-256 bccfabed122da73604b3c48649248094d2dd69587a66275bdcde86e3e8e97e08

See more details on using hashes here.

File details

Details for the file sigil_diff-0.3.0-cp39-cp39-manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for sigil_diff-0.3.0-cp39-cp39-manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 2bffc011bdb29e40050276c2bf57ec863699bfd1946a7154267ab55649c1276f
MD5 ba3d5c46c47ecb9a46a295e8344287d9
BLAKE2b-256 48c475116b483250b5146d7bc1efbf19fc5db22f4c47de9eec740c36e68afdba

See more details on using hashes here.

File details

Details for the file sigil_diff-0.3.0-cp39-cp39-manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for sigil_diff-0.3.0-cp39-cp39-manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 bea018163ecdf598d7a60e9c8090294970b637f7d5ac9d2ba355587d2e28f167
MD5 f16f89cf3a8a6d4390095d9abb3bce81
BLAKE2b-256 38611691a30182ff6d7111896d485ae3c4a6d8f4246289f9b23fe96f63c652e4

See more details on using hashes here.

File details

Details for the file sigil_diff-0.3.0-cp38-cp38-manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for sigil_diff-0.3.0-cp38-cp38-manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 601475c8f18efdde1865ab1305eb23d1f9709a6adcf7aa0f3d036471c500aa1c
MD5 cb1314a55e2e057e4b62edd7f1560a56
BLAKE2b-256 167ceff4e82fd9ac0b88d1218a5babac6aa40431a44d6d44358e3866b7760e69

See more details on using hashes here.

File details

Details for the file sigil_diff-0.3.0-cp38-cp38-manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for sigil_diff-0.3.0-cp38-cp38-manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 fb98f886542a1cfc7e093ae584a9b4435320d0b9d2de4538116063c2a8f3481a
MD5 746c0de0efa8e4b0dc32bc2a6c3b1880
BLAKE2b-256 8289b5cb6c1978075b811714ff7b942695ada5d6d0ac1f900e73f7492f396f53

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page