Skip to main content

Compare two ASAM ODS hierarchy instances and write a structured diff result.

Project description

odsbox-diff

Build PyPI version Python versions License: Apache 2.0

A CLI and Python library to compare two instance hierarchies on one or two ASAM ODS servers and write a structured diff result. Built on odsbox for ODS access and deepdiff for the comparison.

Typical use cases:

  • Verify that a test, test step or measurement was migrated faithfully between two servers.
  • Detect unintended changes between two snapshots of the same instance.
  • Hash bulk LocalColumn data to detect changes in measured signals.
  • Regression testing — compare a saved baseline file against a live server instance.
  • Create baselines — collect a hierarchy snapshot to a file for later comparison, with optional round-trip validation.
  • Offline comparison — diff two previously saved hierarchy files without any server connection.

Full user guide: see docs/usage.md for comprehensive CLI examples, Python API reference, pytest integration patterns, and troubleshooting.

Installation

uv add odsbox-diff
# or, with pip
pip install odsbox-diff

The package requires Python 3.14+ and ships a console script odsbox-diff.

Quick start

  1. Copy one of the example configs from configs/ and adjust it:

    • config.example.toml — basic auth (single or multiple servers)
    • config.m2m.example.toml — OAuth2 machine-to-machine
    • config.oidc.example.toml — OIDC (interactive browser login)
  2. Store the secret (password / client_secret) in your OS keyring (see Keyring secrets below) or inline it in the config file.

  3. Run the diff:

    uv run odsbox-diff `
        --config my-config.toml `
        --entity TestStep `
        -id1 5 `
        -id2 7
    

    With multiple named servers, prefix instance IDs with the server name:

    uv run odsbox-diff `
        --config my-config.toml `
        --entity TestStep `
        -id1 prod:1898 `
        -id2 staging:2
    

    Compare two saved JSON files (no server connection needed):

    uv run odsbox-diff `
        --config my-config.toml `
        --entity TestStep `
        -id1 file:baseline.json `
        -id2 file:current.json
    

    Collect a hierarchy to a file and self-validate:

    uv run odsbox-diff collect `
        --config my-config.toml `
        --entity TestStep `
        -id 42 `
        -o baseline.json `
        --validate
    

CLI reference

odsbox-diff (diff mode)

Flag Description
-c, --config Path to a TOML or JSON config file (required).
--entity Root entity name to compare (e.g. TestStep, Measurement).
-id1 / -id2 Instance reference: plain ID (42), server:id (prod:5), or file:path.json to load from disk.
-rf, --result_file Override the result-file path from config defaults.
-ep, --exclude_path Extra DeepDiff path to exclude (repeatable).
-erp, --exclude_regex_path Extra regex path exclusion (repeatable).
-dd, --dump_dictionaries Also dump the collected hierarchies as <result>.inst1.json / .inst2.json.
-bn, --no_bulk Skip bulk LocalColumn hashing.
-bpb, --bulk_progress_bar Show a progress bar during bulk hashing.
--cached-related ENTITY [...] Resolve relation IDs to names for the listed entities.
-v, --verbose INFO logging with timestamps.
-q, --quiet Suppress all logging.

CLI flags always override config defaults. List options (exclude_path, exclude_regex_path, cached-related) extend the config defaults rather than replacing them.

odsbox-diff collect (collect mode)

Flag Description
-c, --config Path to a TOML or JSON config file (required).
--entity Root entity name to collect.
-id Instance ID or server:id.
-o, --output Output file path (.json or .zip).
--validate After saving, reload and self-diff to verify round-trip fidelity.
-rf, --result_file Path for the self-diff result (only with --validate).
-bn, --no_bulk Skip bulk LocalColumn hashing.
-bpb, --bulk_progress_bar Show a progress bar during bulk hashing.
--cached-related ENTITY [...] Resolve relation IDs to names.
-v, --verbose INFO logging with timestamps.
-q, --quiet Suppress all logging.

Exit codes

Code Meaning
0 No differences found (or collect completed successfully).
100 Differences found; result file written.
1 Argument or server:id validation error.
-1 Uncaught exception.

Keyring secrets

Secrets are read from the OS keyring under the service name odsbox-diff:

Auth method Keyring key
basic <url>:<username> (password)
m2m / oidc <token_endpoint>:<client_id> (client_secret)

Example with the keyring CLI:

keyring set odsbox-diff "http://localhost:57481/api:admin"

Library usage

High-level API (recommended for test frameworks)

from odsbox_diff import diff_file_to_file, diff_file_to_server, collect_to_file
from odsbox_diff.connection import AppConfig, ServerConfig, AuthMethod

# Compare two saved files — no server needed
diff = diff_file_to_file("baseline.json", "current.json")
assert not diff  # falsy = no differences

# Regression test: baseline file vs live server
diff = diff_file_to_server("my-config.toml", "TestStep", 42, "baseline.json")
assert not diff

# Build config in code (no config file needed)
cfg = AppConfig(servers={"default": ServerConfig(
    url="http://localhost:8080/api",
    username="admin",
    password="secret",
)})
diff = diff_file_to_server(cfg, "TestStep", 42, "baseline.json")

# Collect a baseline and validate round-trip fidelity
result = collect_to_file("my-config.toml", "TestStep", 42, "baseline.json", validate=True)
assert not result  # falsy = round-trip is clean

Low-level building blocks

from odsbox_diff import (
    collect,
    diff_dictionaries,
    dump_diff_as_json,
    save_collect_results,
)
from odsbox_diff.connection import create_connection, load_config

app_config = load_config("my-config.toml")
server = next(iter(app_config.servers.values()))

with create_connection(server) as con_i:
    tree_a, _ = collect(con_i, "TestStep", 5)
    tree_b, _ = collect(con_i, "TestStep", 7)

diff = diff_dictionaries(tree_a, tree_b, [], [])
dump_diff_as_json("diff.json", diff)

Configuration reference

[server] / [servers.<name>]

Either a single [server] table (stored internally as the default server) or one [servers.<name>] table per named server. Common fields:

Field Type Notes
url string ODS REST endpoint (required).
verify_certificate bool Default true.
method string basic, m2m, or oidc. Default basic.

Method-specific fields are documented in the example configs in configs/.

When using file-to-file comparisons only, the [server] section can be omitted entirely — only [defaults] is needed.

[defaults]

Optional defaults for diff behavior:

Field Type Default
result_file string "diff_ods_tests_result.json"
exclude_paths list []
exclude_regex_paths list [] (default Id / DateCreated / Version exclusions are always applied)
cached_related list []
bulk_progress_bar bool false
no_bulk bool false
dump_dictionaries bool false
verbose bool false
quiet bool false

Development

uv sync
uv run pytest
uv run ruff check src tests
uv run mypy src

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

odsbox_diff-1.0.2.tar.gz (25.4 kB view details)

Uploaded Source

Built Distribution

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

odsbox_diff-1.0.2-py3-none-any.whl (30.9 kB view details)

Uploaded Python 3

File details

Details for the file odsbox_diff-1.0.2.tar.gz.

File metadata

  • Download URL: odsbox_diff-1.0.2.tar.gz
  • Upload date:
  • Size: 25.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.24 {"installer":{"name":"uv","version":"0.11.24","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for odsbox_diff-1.0.2.tar.gz
Algorithm Hash digest
SHA256 30aaa25d29b6ada7d6e8cc33d59a2713941bf3fc710efdbf772c7d1d7221df1f
MD5 783931b295fd71588f13ff6f02af79c0
BLAKE2b-256 b993e4a055c623ab46217df4f2971d3430fec397d624b8a330fc06a18abf1a2d

See more details on using hashes here.

File details

Details for the file odsbox_diff-1.0.2-py3-none-any.whl.

File metadata

  • Download URL: odsbox_diff-1.0.2-py3-none-any.whl
  • Upload date:
  • Size: 30.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.24 {"installer":{"name":"uv","version":"0.11.24","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for odsbox_diff-1.0.2-py3-none-any.whl
Algorithm Hash digest
SHA256 f8ea218da5e88b54556a770ded25a28444c33eea3a82223bf4e8e1617097c80b
MD5 2a7ab20024254677607e12e1622d31f4
BLAKE2b-256 ed2fb5c98d121f2d2e6aa2572cce16fd0186bd814fb09e5401345fdf1e293b50

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