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.
Run uv run odsbox-diff --help to see the available commands (diff, collect,
and config). Use uv run odsbox-diff COMMAND --help for command-specific
options.
Quick start
- Create a starter config:
uv run odsbox-diff config
Default output is ./odsbox-diff.config.toml with three use-case server
entries: default (basic), production (m2m), and staging (oidc).
Or 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:
</code></pre> </li> </ol> <p>uv run odsbox-diff diff <code> --config my-config.toml </code> --entity TestStep <code> -id1 5 </code> -id2 7</p> <pre><code> With multiple named servers, prefix instance IDs with the server name: ```powershell uv run odsbox-diff 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 diff ` --config my-config.toml ` --entity TestStep ` -id1 file:baseline.json ` -id2 file:current.json
The historical form without the explicit `diff` subcommand still works for backward compatibility.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(recommended diff mode)Flag Description -c,--configPath to a TOML or JSON config file (required). --entityRoot entity name to compare (e.g. TestStep,Measurement).-id1/-id2Instance reference: plain ID ( 42),server:id(prod:5), orfile:path.jsonto load from disk.-rf,--result_fileOverride the result-file path from config defaults. -ep,--exclude_pathExtra DeepDiff path to exclude (repeatable). -erp,--exclude_regex_pathExtra regex path exclusion (repeatable). -dd,--dump_dictionariesAlso dump the collected hierarchies as <result>.inst1.json/.inst2.json.-bn,--no_bulkSkip bulk LocalColumn hashing. -bpb,--bulk_progress_barShow a progress bar during bulk hashing. --cached-related ENTITY [...]Resolve relation IDs to names for the listed entities. -v,--verboseINFO logging with timestamps. -q,--quietSuppress 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.For backward compatibility,
odsbox-diff --config ... --entity ... -id1 ... -id2 ...still runs the diff command implicitly.odsbox-diff collect(collect mode)Flag Description -c,--configPath to a TOML or JSON config file (required). --entityRoot entity name to collect. -idInstance ID or server:id.-o,--outputOutput file path ( .jsonor.zip).--validateAfter saving, reload and self-diff to verify round-trip fidelity. -rf,--result_filePath for the self-diff result (only with --validate).-bn,--no_bulkSkip bulk LocalColumn hashing. -bpb,--bulk_progress_barShow a progress bar during bulk hashing. --cached-related ENTITY [...]Resolve relation IDs to names. -v,--verboseINFO logging with timestamps. -q,--quietSuppress all logging. odsbox-diff config(config scaffolding)Flag Description -o,--outputOutput path (default: ./odsbox-diff.config.toml).--forceOverwrite output file if it already exists. --single-auth {basic,m2m,oidc}Generate only one server section. --with-queries/--no-queriesInclude or skip queries.firstandqueries.second.--include-example-comments/--minimalVerbose guided output or compact output without comments. Exit codes
Code Meaning 0No differences found (or collect completed successfully). 100Differences found; result file written. 1Argument or server:idvalidation error.-1Uncaught 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
keyringCLI: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 thedefaultserver) or one[servers.<name>]table per named server. Common fields:For single-server usage,
[servers.default]is supported and behaves the same as[server].Field Type Notes urlstring ODS REST endpoint (required). verify_certificatebool Default true.methodstring basic,m2m, oroidc. Defaultbasic.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_filestring "diff_ods_tests_result.json"exclude_pathslist []exclude_regex_pathslist [](defaultId/DateCreated/Versionexclusions are always applied)cached_relatedlist []bulk_progress_barbool falseno_bulkbool falsedump_dictionariesbool falseverbosebool falsequietbool falseDevelopment
uv sync uv run pytest uv run ruff check src tests uv run mypy src
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.2.0.tar.gz.
File metadata
- Download URL: odsbox_diff-1.2.0.tar.gz
- Upload date:
- Size: 29.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.25 {"installer":{"name":"uv","version":"0.11.25","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 |
d7c568ed78304a743b4c2ab56eb2b5c8b5f1d40260d90b7f867b812e27815eff
|
|
| MD5 |
ef990a67ad9e837ab965e79b16ca37c5
|
|
| BLAKE2b-256 |
aec001c0e8a243460d0be13c7e69f8a6a961b5193b272eb450bedff0826aa47e
|
File details
Details for the file odsbox_diff-1.2.0-py3-none-any.whl.
File metadata
- Download URL: odsbox_diff-1.2.0-py3-none-any.whl
- Upload date:
- Size: 34.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.25 {"installer":{"name":"uv","version":"0.11.25","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 |
f0e9af1fc050181f194b5228dc6e21b4ac54094aa693a9c9b80d88ae6d911c17
|
|
| MD5 |
d1ba5827af8b9691573e53c6d6b2bff1
|
|
| BLAKE2b-256 |
4be6850a2cee86956d23d307560071c64be47eec6a33235723386ed7e695a809
|