Compare two ASAM ODS hierarchy instances and write a structured diff result.
Project description
odsbox-diff
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.mdfor 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
-
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-machineconfig.oidc.example.toml— OIDC (interactive browser login)
-
Store the secret (password /
client_secret) in your OS keyring (see Keyring secrets below) or inline it in the config file. -
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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file odsbox_diff-1.0.0.tar.gz.
File metadata
- Download URL: odsbox_diff-1.0.0.tar.gz
- Upload date:
- Size: 25.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.8 {"installer":{"name":"uv","version":"0.11.8","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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
191b1d67e58a5f3f5c73502505ffda6c2f7753007e0e8efc9cd9ea59b05ea4c1
|
|
| MD5 |
24ab9b94f9b41e7dad4408a53033be15
|
|
| BLAKE2b-256 |
cc7028004082abda0770f7910438b8db05fc5e387b89ca77a8e5dfeae5925ee5
|
File details
Details for the file odsbox_diff-1.0.0-py3-none-any.whl.
File metadata
- Download URL: odsbox_diff-1.0.0-py3-none-any.whl
- Upload date:
- Size: 30.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.8 {"installer":{"name":"uv","version":"0.11.8","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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5b15327f31345e2583a01609583db6bd6ec92998cdb568545c3e705f4d1d622e
|
|
| MD5 |
533afde7553c2f04df9911ea51ef8372
|
|
| BLAKE2b-256 |
495fff75c371f51795ae0ace2d6ef020fa581ec3691255e80f209d2c31606e22
|