playground.py.* for all annotated functions.Editing the source or selector clears the results. Loading an example replaces the source and undo history; reloading the page restores the initial example. The editor is read-only during verification.
Use EN / JP to switch languages. Opening Guide or Reference and switching languages preserve your work. Drag the panel dividers to resize the workspace; focused dividers also accept arrow keys. The browser accepts one module up to 1 MiB and verifies it without executing Python.
Use #/playground, #/guide or
#/reference for page links. A section link uses
#/reference/value-contracts. Existing .html
links also work. Sample selection updates the URL;
#/playground/division loads signed division directly.
Use Annotated and Refine to constrain a
value. The first lambda parameter receives that value; additional
return-predicate parameters name function inputs.
from refinepy import Refine
from typing import Annotated
Nat = Annotated[int, Refine(lambda v: v >= 0)]
def increment(x: Nat) -> Annotated[int, Refine(lambda result, x: result == x + 1)]:
return x + 1This requires a nonnegative input and a result one greater than that
input. Change x + 1 to x - 1 to try a failing
contract.
Use @requires for a condition on function inputs.
from refinepy import requires
@requires(
lambda xs, i: 0 <= i < len(xs)
)
def read_at(xs: list[int], i: int) -> int:
return xs[i]The verifier checks this condition at every call.
Use @ensures with a base return type. Its first binder
receives the result; remaining binders name entry inputs. A
None result omits the result binder.
from refinepy import ensures
@ensures(lambda result, n: result == n + 1)
def increment(n: int) -> int:
return n + 1
@ensures(lambda n: n + 0 == n)
def identity_lemma(n: int) -> None:
return NoneProve that an index is in bounds before reading a character. Strings support content equality, length, concatenation and step-1 slicing.
from typing import Annotated
from refinepy import Refine, requires
@requires(lambda text: len(text) > 0)
def first(text: str) -> Annotated[str, Refine(lambda result: len(result) == 1)]:
return text[0]
def append(left: str, right: str) -> Annotated[str, Refine(lambda result, left, right: len(result) == len(left) + len(right))]:
return left + rightNegative indices count from the end. String methods and f-strings are unsupported.
Establish a nonzero divisor with a precondition or branch. Division and remainder follow Python’s floor-rounding and sign rules.
from refinepy import requires, ensures
@requires(lambda d: d != 0)
@ensures(lambda r, a, d: a == r[0] * d + r[1] and
(0 <= r[1] < d if d > 0 else d < r[1] <= 0))
def divide(a: int, d: int) -> tuple[int, int]:
return a // d, a % dAn unguarded divisor that can be zero fails verification. Nonlinear
arithmetic and variable-divisor proofs may return
unknown.
Build a fresh list under one local name to update it while preserving input contents.
from refinepy import requires, ensures
@requires(lambda xs, i: -len(xs) <= i < len(xs))
@ensures(lambda r, xs, i, value:
len(r) == len(xs) and r[i] == value
and r == xs[:i if i >= 0 else len(xs) + i] + [value]
+ xs[(i if i >= 0 else len(xs) + i) + 1:])
def replace(xs: list[int], i: int, value: int) -> list[int]:
out = xs[:]
out[i] = value
return outappend, extend, += and integer
item assignment are supported. Empty lists need a type such as
out: list[int] = []. Element and whole-list contracts are
checked after writes. Input lists, shared aliases and extracted nested
lists cannot be written. See Reference for
helper-result ownership and container limits.
Use @decreases to prove that a direct recursive call
reduces a measure.
import refinepy
from refinepy import Refine
from typing import Annotated
Nat = Annotated[int, Refine(lambda v: v >= 0)]
@refinepy.decreases(lambda n: n)
def count(n: Nat) -> Annotated[int, Refine(lambda r, n: r == n)]:
return 0 if n == 0 else 1 + count(n - 1)Each recursive step must satisfy
0 <= next < entry. A tuple of integers can express
lexicographic descent. All called functions must terminate too.
Place invariant and decreases marker calls
immediately before the loop. The invariant must hold initially and after
each iteration; the measure must remain nonnegative and strictly
decrease on every back edge.
from typing import Annotated
from refinepy import Refine, invariant, decreases
Nat = Annotated[int, Refine(lambda v: v >= 0)]
def count_to(n: Nat) -> Annotated[int, Refine(lambda r, n: r == n)]:
i = 0
invariant(lambda i, n: 0 <= i <= n)
decreases(lambda i, n: n - i)
while i < n:
i += 1
if i % 2 == 0:
continue
return iEvery nested loop needs its own measure. Without termination evidence, verification applies only when the function returns normally.
Use @reflect to make a checked definition available in
predicates. A lemma states the equation as its return contract.
from __future__ import annotations
import refinepy
from refinepy import Refine
from typing import Annotated
@refinepy.reflect
def inc(x: int) -> int:
return x + 1
def lemma(x: int) -> Annotated[None, Refine(lambda _, x: inc(inc(x)) == x + 2)]:
return NoneSelect lemma and verify; changing x + 2 to
x + 3 fails the proof. Reflected functions cannot have
preconditions, refined annotations, loops or container writes. Recursive
definitions and inductive lemmas have additional Reference
restrictions.
Add this to the preceding example. Assert checks a
proposition at a bound local variable; using supplies a
checked, independently terminating lemma. Python does not execute these
local annotations.
from typing import Annotated
from refinepy import Assert
def use_lemma(x: int) -> int:
x: Annotated[int, Assert(
lambda value: inc(inc(value)) == value + 2,
using=lambda arg: lemma(arg),
)]
return xWithout using, the proposition must follow from current
facts. Later assignments are not constrained by Assert; use
Refine for a lasting value contract. See Reference for binding
rules.
Each function has a status and a separate termination summary. Verification details shows diagnostics and proof evidence; Full JSON report shows the complete report.
| Status | Meaning |
|---|---|
verified |
The verifier discharged the required obligations. |
counterexample |
A validated source witness violates the contract. |
unproved |
An obligation remains unproved; this alone does not establish a source bug. |
unsupported |
The source uses a form outside the supported domain. |
unknown |
Limits or solver uncertainty prevented a result. |
error |
Source, contracts, configuration or execution failed; read the diagnostic. |
verified applies to inputs satisfying the contracts and
the modeled runtime. It does not establish termination by itself or
enforce contracts in Python. Recursion-depth and resource limits remain
outside the guarantee. See Reference for
supported syntax.