Skip to main content

Parity

Behavioural compatibility testing for software migrations.

Parity runs a reference and a candidate on the same inputs, compares what they return or raise, searches for differences, shrinks failing inputs and saves replayable evidence.

canonical input ──┬──> reference ──┐
                  └──> candidate ──┴──> compare ──> shrink ──> replay

The two sides can use different dependency versions, APIs, implementations, Python environments or languages. They only need a small shared behavioural contract.

Use Parity for dependency upgrades, refactors, backend changes, branch/worktree comparisons and cross-language rewrites. It verifies a migration; it does not write or repair one.

PASSED means Parity found no difference in the configured domain and search budget. It is executable evidence, not a proof of equivalence.

Five-minute start

The Parity controller requires Python 3.11 or later.

python -m pip install parity-check
parity init
parity check

parity init creates a runnable parity.toml and parity_example.py. Edit the two example functions to call the old and new behaviour, then rerun parity check.

The base package uses Arrow and does not install pandas or Polars. Add parity-check[pandas] or parity-check[polars] when targets in the controller environment use those adapters. Managed package-upgrade environments use parity-check[workspace].

Put it around real code

A useful first case needs a representative input and an explicit comparison policy:

version = 1

[[cases]]
name = "orders"
fixture = "tests/fixtures/orders.parquet"

[cases.reference]
target = "migration_adapters:reference_orders"
adapter = "pandas"

[cases.candidate]
target = "migration_adapters:candidate_orders"
adapter = "polars"

[cases.comparison]
row_order = "keyed"
row_keys = ["order_id"]
dtype = "compatible"
rtol = 1e-7

[cases.generation]
max_examples = 250
max_findings = 4

[cases.performance]
enabled = false

Paths are relative to parity.toml. Targets use module:callable syntax and should be small, project-owned wrappers around the public behaviour being migrated.

parity doctor --config parity.toml
parity check --config parity.toml
parity check --case orders --max-examples 1000
parity check --json .parity/report.json --junit .parity/junit.xml

The CLI has a stable outcome contract:

  • exit 0: PASSED;
  • exit 1: FAILED because behaviour or an enforced performance policy differed; and
  • exit 2: ERROR because configuration or execution could not produce reliable evidence.

What Parity checks

  • Arrow, pandas and Polars inputs, including two- and three-frame joins or lookups.
  • Returned frames and JSON-like values, raised exceptions and input mutation.
  • Column and row order, keyed rows, dtypes, null/NaN rules, numeric tolerances and datetimes.
  • Fixtures, deterministic edge cases, Hypothesis generation and shrinking.
  • Several independent mismatch classes in one run, each with a stable ms3: identifier.
  • Runtime and selected dependency provenance for each isolated target.
  • Optional paired runtime and peak-memory regression evidence after semantic success.

Reference and candidate signatures do not need to match. Adapt each side into the shared input and output contract:

def reference_quote(frame):
    from legacy import calculate

    row = frame.iloc[0]
    return calculate(row.x, row.y, row.currency)


def candidate_quote(frame):
    from rewritten import Data, Engine

    row = frame.iloc[0]
    return Engine(row.currency).calculate(Data(row.x, row.y))

Keep side-specific imports inside their wrappers when the two environments intentionally contain different packages. A configured canonicalizer can project a successful domain object into an Arrow or JSON-like result. Target exceptions remain observable behaviour.

Upgrade two released packages

The managed workspace creates separate, locked target environments. The controller and targets do not share a dependency graph.

python -m pip install "parity-check[workspace]"
parity migration init \
  --reference-package 'your-library==1.2.3' \
  --candidate-package 'your-library==2.0.0' \
  --scaffold \
  --json

This creates a deliberately incomplete adapter, tiny JSON fixture, case configuration, migration inventory, workspace and four-item review checklist under migrations/. Implement the adapter, review the fixture/domain and comparison policy, resolve the checklist, then run:

parity migration validate --json
parity migration run --json

Validation does not create environments or invoke targets. It remains non-passing until the generated contract has been reviewed. The final run prepares both sides, checks every declared case in every dependency lane and writes a data-safe report per lane.

Each side may instead be an existing checkout, so the same workflow covers released/local and local/local comparisons:

parity migration init \
  --reference-path ../main-worktree \
  --candidate-path ../feature-worktree
parity migration run

Parity does not clone, switch or edit worktrees. It installs each checkout separately and binds its Git/content identity into the evidence. See the user guide for custom targets, dependency lanes and rolling A→B→C upgrades.

Cross-language targets

Any local executable can be a reference or candidate through Parity's versioned Arrow/JSON target protocol. For a Python boundary around C, C++, Fortran, Rust, Java or a legacy CLI, scaffold the protocol process and implement only the domain translation:

parity adapter init adapters/legacy.py --program bin/legacy-target
[cases.reference]
command = ["parity", "adapter", "serve", "adapters/legacy.py"]

The adapter process needs Parity; the wrapped program does not. Compilation, images and dependency installation stay outside the behavioural contract. Start with the adapter SDK guide; implement the language-neutral protocol directly only when Python is unsuitable.

Findings and replay

A confirmed difference creates a private artifact containing the minimized Arrow input, hashes, comparison contract, runtime identities and exact replay information:

parity replay .parity/orders/<finding-directory>
parity evidence verify .parity/report.json --json .parity/evidence-status.json

replay reproduces the semantic result, so a reproduced incompatibility still exits 1. evidence verify answers a different question and exits 0 when every report-referenced finding reproduces its recorded mismatch class.

Terminal, JSON, Markdown and JUnit reports omit compared values. Counterexample and distilled contract directories contain real inputs and outputs; keep them private and upload them only when your data policy permits it.

Reviewed intentional differences can be recorded as exact case/finding approvals. Discovered regressions can also be distilled into a candidate-only contract before the old implementation is removed. See compatibility budgets and distilled contracts.

CI

permissions:
  contents: read

steps:
  - uses: actions/checkout@v4
  - uses: leighshepperson/parity@v0
    with:
      config: parity.toml
      performance: "false"

The moving v0 Action installs the same Parity source revision as the selected action. Pin a reviewed full commit SHA when CI must be immutable. Artifact upload is opt-in because findings can contain sensitive inputs. See the GitHub Action guide.

Read next

Need Document
Build a practical campaign User guide
Look up every TOML field Configuration reference
Decide whether Parity fits Use cases and boundaries
Verify a coding-agent migration Agent migration protocol
Use pytest Pytest integration
Understand internals and contracts Architecture
Handle artifacts and untrusted code Security and privacy
Explore executable examples Fault corpus and case studies

Boundaries and status

Parity compares canonical returns, raises, mutation and process performance. A reviewed wrapper can project a CLI, file or database result into that contract, but Parity does not yet capture and restore filesystem, database or network effects itself. Target processes isolate failures; they are not a security sandbox for hostile code.

Parity is Apache-2.0 licensed and pre-1.0. The current minor release is the supported line, and a minor release may deliberately change public contracts before 1.0.

Download files

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

Source Distribution

parity_check-0.19.1.tar.gz (524.4 kB view details)

Uploaded Source

Built Distribution

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

parity_check-0.19.1-py3-none-any.whl (254.2 kB view details)

Uploaded Python 3

File details

Details for the file parity_check-0.19.1.tar.gz.

File metadata

  • Download URL: parity_check-0.19.1.tar.gz
  • Upload date:
  • Size: 524.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for parity_check-0.19.1.tar.gz
Algorithm Hash digest
SHA256 93841cbff3550100fa4abe225895ef109f24f05027be488551b6c1ebb45e2af0
MD5 7b6d248d9ae424d2420a0ebe3c9d4496
BLAKE2b-256 22b8cbc8fd2120f3fa07679dc3793988d984e9e19bbc691b7024b412ef59954f

See more details on using hashes here.

Provenance

The following attestation bundles were made for parity_check-0.19.1.tar.gz:

Publisher: release.yml on leighshepperson/parity

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file parity_check-0.19.1-py3-none-any.whl.

File metadata

  • Download URL: parity_check-0.19.1-py3-none-any.whl
  • Upload date:
  • Size: 254.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for parity_check-0.19.1-py3-none-any.whl
Algorithm Hash digest
SHA256 58aa233ebdb70e48d6cd4a652536a7c574f1027bdcab1477ff4f86f986dd8b6f
MD5 2d9cfd1544ff5a3a542d09521279d898
BLAKE2b-256 74d89451da7c23665fc2be14d92c590363e91f54b02b8e49d0e9018f81e295db

See more details on using hashes here.

Provenance

The following attestation bundles were made for parity_check-0.19.1-py3-none-any.whl:

Publisher: release.yml on leighshepperson/parity

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.21.0

2 files

0.20.1

2 files

0.20.0

2 files

This release

0.19.1 This release

2 files

0.19.0

2 files

0.18.1

2 files

0.18.0

2 files

0.17.0

2 files

0.16.0

2 files

0.14.3

2 files

0.14.2

2 files

0.14.1

2 files

0.14.0

2 files

0.13.0

2 files

0.12.0

2 files

0.10.0

2 files

0.9.2

2 files

0.9.1

2 files

0.8.0

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page