Parity
Run old and new code on the same calls. Keep reproducible evidence of what changed.
Parity checks dependency upgrades, backend replacements and rewrites when the old implementation still exists. Targets run in separate processes and can use different Python environments, checkouts or languages. It compares returned values, exceptions and input mutation, then saves confirmed differences for exact replay.
Try a real behaviour difference
Install the controller with Python 3.11 or later:
python -m pip install parity-check
Save these requests as calls.jsonl:
{"args":["hello"]}
{"args":["hello world"]}
{"args":["a/b"]}
Compare two standard-library URL encoders:
parity compare urllib.parse:quote urllib.parse:quote_plus --calls calls.jsonl
The ordinary word matches. Spaces and slashes expose a behavioural difference.
Parity exits 1, identifies the first differing call and retains an exact replay
artifact. This example needs no application code or configuration.
For your code, supply importable functions and representative requests:
parity compare old_orders:quote new_orders:quote --calls tests/orders.jsonl \
--json .parity/report.json --junit .parity/junit.xml
parity evidence verify .parity/report.json
Each line is {"args": [...], "kwargs": {...}}; either field may be omitted.
Whole calls stay together, including correlated arguments. Exit codes are 0 for
matching behaviour, 1 for differences and 2 for invalid or unreliable execution.
Numbers compare exactly by default; set --rtol or --atol to accept a tolerance.
A pass covers the supplied calls, not every possible input. Direct comparisons do not generate or shrink inputs. Empty and malformed corpora fail before targets run. See the direct comparison guide for the full contract.
When to use it
| Situation | Best starting point |
|---|---|
| Two simple functions in one environment | pytest, with Hypothesis if you need generated inputs |
| An upgrade needs conflicting dependencies | Parity with separate target environments |
| Existing requests must survive a rewrite | parity compare --calls |
| A dataframe migration needs order, dtype, null or numeric policies | A configured Parity campaign |
| Differences need repeatable CI evidence and exact replay | Parity reports and retained findings |
| No examples, reference or agreed behaviour exist | Define the contract first; Parity cannot infer it |
The value is the integrated isolation, comparison policy and evidence workflow. It does not determine business intent or replace ordinary tests. The usefulness review records the evidence and limitations.
Dependency versions and checkouts
Run an unchanged wrapper in two existing environments:
parity compare migration:run migration:run --calls calls.jsonl \
--reference-python .venv-old/bin/python \
--candidate-python .venv-new/bin/python \
--record-distribution your-library
Each target environment needs its application dependencies and PyArrow, not the full
Parity installation. For different source checkouts, add --reference-workdir ../before
and --candidate-workdir ../after. The same module name can resolve to different source
on each side. Small wrappers can map different APIs into a shared input/output contract.
To have Parity prepare independently locked environments:
parity migration init \
--reference-path ../before-upgrade \
--candidate-path ../after-upgrade
Released targets use --reference-package 'your-library==1.2.3' and
--candidate-package 'your-library==2.0.0'. See the user guide
for setup, review and execution.
Search beyond known examples
parity init
parity check
This creates a runnable example campaign. Replace its functions and input domain with the behaviour you are migrating. Configured campaigns support Hypothesis search, shrinking, dataframe schemas, relational inputs, multiple findings and optional performance measurements after semantic success.
Parity's unit of work is an explicit callable(*args, **kwargs) contract. A complete
call can combine ordinary JSON, frames and project-generated structures such as
recursive programs and event streams. Install parity-check[pandas] or
parity-check[polars] when the controller needs those dataframe adapters.
Use parity.verify for live functions and a Hypothesis strategy.
Cross-language targets use the adapter SDK.
For a configured campaign in GitHub Actions:
- uses: leighshepperson/parity@v0
with:
config: parity.toml
performance: "false"
Python and pytest
from parity import compare
result = compare("old_orders:quote", "new_orders:quote", calls="calls.jsonl")
assert result.passed
The pytest fixture adds a readable failure summary:
def test_upgrade(parity):
parity.compare("old_orders:quote", "new_orders:quote", calls="tests/orders.jsonl")
Executable evidence
| Case | What it establishes |
|---|---|
| Pydantic order requests | Five supplied calls expose four documented changes; control passes and all findings replay |
| Generated Pydantic campaign | Search and shrinking across conflicting dependency environments |
| pandas 2.3 → 3.0 | An unchanged group-by callable exposes a changed default |
| PyTimeTK pandas → Polars | Five public API comparisons with stock and repaired candidates |
| JavaScript → Python | Recursive programs, domain exceptions and minimized defects |
| C++ → Python | Stateful event streams and replay |
These are bounded engineering studies using synthetic inputs, not evidence that every application is compatible or that independent teams have adopted Parity. More studies are listed in the external validation log.
Evidence and boundaries
Terminal, JSON and JUnit reports omit compared values. Finding directories contain
actual inputs and outputs; keep them private. parity replay <artifact-directory>
reproduces a saved comparison and still exits 1 when the difference remains.
parity evidence verify .parity/report.json exits 0 when all findings reproduce.
Direct-comparison findings do not need the original calls file to replay.
Targets must be trusted and repeatable. Process isolation handles failures and dependency conflicts; it is not a security sandbox. Wrappers own filesystem, database and network cleanup. For stateful behaviour, pass a complete event stream and reset state within the wrapper. Parity does not capture production traffic.
| Need | Documentation |
|---|---|
| Compare existing calls | Direct comparison |
| Build a generated campaign | User guide |
| Look up TOML fields | Configuration reference |
| Integrate with CI | GitHub Action |
| Review intentional differences | Compatibility budgets |
| Retire the old implementation | Distilled contracts |
| Understand execution and evidence | Architecture, security |
Apache-2.0 licensed and pre-1.0. The current minor release is the supported line; minor releases may change public contracts.
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 parity_check-0.21.0.tar.gz.
File metadata
- Download URL: parity_check-0.21.0.tar.gz
- Upload date:
- Size: 811.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5ba840b173a6e6fc55a635319c3d3cc98b45783082473be12672cc390c01f89f
|
|
| MD5 |
7891735085db2f821c24e0efa032e42d
|
|
| BLAKE2b-256 |
dde5664c9044ceffdd9589b3181d092ffc4445959221a8ccef4730b9d1ca30f7
|
Provenance
The following attestation bundles were made for parity_check-0.21.0.tar.gz:
Publisher:
release.yml on leighshepperson/parity
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
parity_check-0.21.0.tar.gz -
Subject digest:
5ba840b173a6e6fc55a635319c3d3cc98b45783082473be12672cc390c01f89f - Sigstore transparency entry: 2743683299
- Sigstore integration time:
-
Permalink:
leighshepperson/parity@2a1824e58b40067ce930482a23beb4b469f74328 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/leighshepperson
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@2a1824e58b40067ce930482a23beb4b469f74328 -
Trigger Event:
workflow_run
-
Statement type:
File details
Details for the file parity_check-0.21.0-py3-none-any.whl.
File metadata
- Download URL: parity_check-0.21.0-py3-none-any.whl
- Upload date:
- Size: 263.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
64be26942c73d6619693733ac9a92e36d4744390dd17f61a31c57c563d5ea86b
|
|
| MD5 |
98fbb7ef32c3d78e6185f725612439af
|
|
| BLAKE2b-256 |
c52985c44b7efc864fe6b6f6646c22f972d0b706ab74b78fec91a487fc21d1ce
|
Provenance
The following attestation bundles were made for parity_check-0.21.0-py3-none-any.whl:
Publisher:
release.yml on leighshepperson/parity
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
parity_check-0.21.0-py3-none-any.whl -
Subject digest:
64be26942c73d6619693733ac9a92e36d4744390dd17f61a31c57c563d5ea86b - Sigstore transparency entry: 2743683303
- Sigstore integration time:
-
Permalink:
leighshepperson/parity@2a1824e58b40067ce930482a23beb4b469f74328 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/leighshepperson
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@2a1824e58b40067ce930482a23beb4b469f74328 -
Trigger Event:
workflow_run
-
Statement type: