Proof-carrying Python functions via Z3 — annotate, verify, ship.
Project description
provably
Z3-backed formal verification for Python -- via decorators and refinement types
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
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 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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9f9e53d57719995046dc749a3973feedf6539207b0c5921c4b4c38bbfe825f1b
|
|
| MD5 |
cb74f5a7cf91fb3673c14a335d3e5c8b
|
|
| BLAKE2b-256 |
58651466125a64e08ff503fc5f0c4d4f60ef70fb3c643b1c1eee7d5d2002ab91
|
Provenance
The following attestation bundles were made for provably-0.3.0.tar.gz:
Publisher:
release.yml on awkronos/provably
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
provably-0.3.0.tar.gz -
Subject digest:
9f9e53d57719995046dc749a3973feedf6539207b0c5921c4b4c38bbfe825f1b - Sigstore transparency entry: 1006265081
- Sigstore integration time:
-
Permalink:
awkronos/provably@d42a9f10dae01b12d032186ba7fb19928ab2d98b -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/awkronos
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@d42a9f10dae01b12d032186ba7fb19928ab2d98b -
Trigger Event:
push
-
Statement type:
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c10e1697d5be2592e3329c0260a83eed0d50d50cfbd5c41286ae3a745d3d5288
|
|
| MD5 |
7613702a256d66568048b036113f65bc
|
|
| BLAKE2b-256 |
667bca445ebe5d855349fad294a0470f232e9ba265780d72f685a985b1f3f44b
|
Provenance
The following attestation bundles were made for provably-0.3.0-py3-none-any.whl:
Publisher:
release.yml on awkronos/provably
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
provably-0.3.0-py3-none-any.whl -
Subject digest:
c10e1697d5be2592e3329c0260a83eed0d50d50cfbd5c41286ae3a745d3d5288 - Sigstore transparency entry: 1006265084
- Sigstore integration time:
-
Permalink:
awkronos/provably@d42a9f10dae01b12d032186ba7fb19928ab2d98b -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/awkronos
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@d42a9f10dae01b12d032186ba7fb19928ab2d98b -
Trigger Event:
push
-
Statement type: