Skip to main content

Proof-carrying Python functions via Z3 — annotate, verify, ship.

Project description

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

Project details


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.

Supported by

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