Skip to main content

provably

Z3-backed formal verification for Python -- via decorators and refinement types

PyPI Python License: MIT CI Typed Docs


from provably import verified

@verified(
    pre=lambda val, lo, hi: lo <= hi,
    post=lambda val, lo, hi, result: (result >= lo) & (result <= hi),
)
def clamp(val: float, lo: float, hi: float) -> float:
    if val < lo:
        return lo
    elif val > hi:
        return hi
    else:
        return val

clamp.__proof__.verified   # True — for ALL inputs where lo <= hi
str(clamp.__proof__)       # "[Q.E.D.] clamp"

verified=True is a mathematical proof. Z3 determined that no input satisfying the precondition can violate the postcondition.

Install

pip install provably
# or: uv add provably

Examples

Pre/post contracts

@verified(
    pre=lambda a, b: b > 0,
    post=lambda a, b, result: (result >= 0) & (result < b),
)
def modulo(a: int, b: int) -> int:
    return a % b

modulo.__proof__.verified        # True
modulo.__proof__.solver_time_ms  # ~2ms

Refinement types

from typing import Annotated
from provably.types import Between, Gt, NonNegative

@verified(post=lambda p, x, result: result >= 0)
def scale(
    p: Annotated[float, Between(0, 1)],
    x: Annotated[float, Gt(0)],
) -> NonNegative:
    return p * x

scale.__proof__.verified  # True

Counterexample extraction

@verified(
    pre=lambda n: n >= 0,
    post=lambda n, result: result * result == n,  # wrong
)
def bad_sqrt(n: int) -> int:
    return n // 2

bad_sqrt.__proof__.counterexample  # {'n': 3, '__return__': 1}

Compositionality

@verified(
    contracts={"my_abs": my_abs.__contract__},
    post=lambda x, y, result: result >= 0,
)
def manhattan(x: float, y: float) -> float:
    return my_abs(x) + my_abs(y)

manhattan.__proof__.verified  # True

While loops

Bounded while loops are unrolled (up to 256 iterations), just like for loops:

@verified(
    pre=lambda n: (n >= 0) & (n <= 10),
    post=lambda n, result: result == n * (n + 1) // 2,
)
def triangle(n: int) -> int:
    total = 0
    i = 0
    while i < n:  # variant: n - i
        i += 1
        total += i
    return total

triangle.__proof__.verified  # True

Walrus operator

The walrus operator (:=) is supported for inline assignments:

@verified(
    post=lambda x, result: (result >= 0) & ((result == x) | (result == -x)),
)
def my_abs(x: float) -> float:
    return (neg := -x) if x < 0 else x

my_abs.__proof__.verified  # True

Match/case (Python 3.10+)

match/case statements are desugared to if/elif/else for Z3:

@verified(
    pre=lambda code: (code >= 0) & (code <= 3),
    post=lambda code, result: (result >= 10) & (result <= 40),
)
def dispatch(code: int) -> int:
    match code:
        case 0: return 10
        case 1: return 20
        case 2: return 30
        case _: return 40

dispatch.__proof__.verified  # True

Tuple returns

Functions can return tuples. Each element is accessible via constant subscript:

@verified(
    post=lambda x, y, result: result >= 0,
)
def sum_and_diff(x: float, y: float) -> tuple:
    return (x + y, x - y)

Lean4 backend

Cross-check Z3 results with an independent proof assistant:

from provably import verify_with_lean4, export_lean4

# Verify with Lean4 type checker (requires Lean4 installed)
cert = verify_with_lean4(clamp, pre=lambda v, lo, hi: lo <= hi,
                         post=lambda v, lo, hi, r: (r >= lo) & (r <= hi))

# Export as .lean theorem file
lean_code = export_lean4(clamp, output_path="clamp.lean")

Supported constructs

Construct Supported
+, -, *, //, /, %, **n Yes
<, <=, >, >=, ==, != Yes
and, or, not, &, |, ~ Yes
if/elif/else/ternary Yes
match/case (Python 3.10+) Yes (desugared to if/elif/else)
min, max, abs Yes
pow, bool, int, float, len, round Yes
sum, any, all Yes
Annotated refinement types Yes
Calls via contracts= Yes
Walrus operator (:=) Yes
Tuple returns + constant subscript (t[0]) Yes
while loops (bounded, max 256 iterations) Yes (unrolled)
for i in range(N) (literal N, max 256) Yes (unrolled)
assert statements Yes (become proof obligations)
Lean4 backend (verify_with_lean4) Yes (requires Lean4)
Recursion No
str, list, dict No
Unbounded loops, generators, async No

Comparison

Library Approach Proof strength Call-site overhead
provably SMT / Z3 Mathematical proof Zero solver overhead
deal Runtime contracts Bug finding Per-call
icontract Runtime contracts Bug finding Per-call
CrossHair Symbolic execution Property testing Test-time
beartype Runtime types Type checking Per-call

Links

Download files

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

Source Distribution

provably-0.3.0.tar.gz (86.1 kB view details)

Uploaded Source

Built Distribution

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

provably-0.3.0-py3-none-any.whl (43.1 kB view details)

Uploaded Python 3

File details

Details for the file provably-0.3.0.tar.gz.

File metadata

  • Download URL: provably-0.3.0.tar.gz
  • Upload date:
  • Size: 86.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for provably-0.3.0.tar.gz
Algorithm Hash digest
SHA256 9f9e53d57719995046dc749a3973feedf6539207b0c5921c4b4c38bbfe825f1b
MD5 cb74f5a7cf91fb3673c14a335d3e5c8b
BLAKE2b-256 58651466125a64e08ff503fc5f0c4d4f60ef70fb3c643b1c1eee7d5d2002ab91

See more details on using hashes here.

Provenance

The following attestation bundles were made for provably-0.3.0.tar.gz:

Publisher: release.yml on awkronos/provably

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

File details

Details for the file provably-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: provably-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 43.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for provably-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c10e1697d5be2592e3329c0260a83eed0d50d50cfbd5c41286ae3a745d3d5288
MD5 7613702a256d66568048b036113f65bc
BLAKE2b-256 667bca445ebe5d855349fad294a0470f232e9ba265780d72f685a985b1f3f44b

See more details on using hashes here.

Provenance

The following attestation bundles were made for provably-0.3.0-py3-none-any.whl:

Publisher: release.yml on awkronos/provably

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

Release history Release notifications | RSS feed

This release

0.3.0 This release

2 files

0.2.1

2 files

0.2.0

2 files

0.1.1

2 files

0.1.0

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page