Metadata-Version: 2.4
Name: contractme
Version: 1.11.0
Summary: Executable specifications for python. Keep control of the code AI writes for you.
Project-URL: Repository, https://gitlab.com/leogermond/contractme
Author-email: Leo Germond <leo.germond@gmail.com>
License-Expression: Apache-2.0
License-File: LICENSE
Requires-Python: >=3.13
Requires-Dist: annotated-types>=0.7.0
Requires-Dist: hypothesis>=6.131.9
Provides-Extra: cron
Requires-Dist: croniter>=2.0.0; extra == 'cron'
Provides-Extra: iso
Requires-Dist: pycountry>=23.12.11; extra == 'iso'
Provides-Extra: yaml
Requires-Dist: pyyaml>=6.0; extra == 'yaml'
Description-Content-Type: text/markdown

# ContractMe — executable specifications for Python
 
## Code is free. Bugs are not. Write the spec instead.

[![pipeline status](https://gitlab.com/leogermond/contractme/badges/main/pipeline.svg)](https://gitlab.com/leogermond/contractme/-/commits/main) 
![coverage](https://gitlab.com/leogermond/contractme/badges/main/coverage.svg?job=checks)
[![Checked with pyright](https://microsoft.github.io/pyright/img/pyright_badge.svg)](https://microsoft.github.io/pyright/)
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
[![Code style: flake8](https://img.shields.io/badge/code%20style-flake8-000000.svg)](https://github.com/PyCQA/flake8)

 
You let an AI write more and more of your code. `contractme` is how you keep
control without reading all of it: state a few plain-Python rules on top of a
function, and every call — in dev, in CI, under your agent's iterations —
checks that the code actually does what you asked. When it doesn't, the error
message names the culprit.

No new syntax, no framework, no PhD. (Under the hood this is
[design by contract](https://en.wikipedia.org/wiki/Design_by_contract),
rebuilt for the age of generated code — but you don't need the theory to
catch your first bug.)

## One decorator away from plain Python.

A rule is a lambda over your own parameter names — if you can write an `if`,
you can write one. Add a decorator, keep coding. Delete it, and you have plain
Python back: nothing else about your code changes.

# 60 seconds from install to caught bug

```bash
pip install contractme
```

## Your type hints become runtime checks

```python
from contractme import annotated

@annotated
def incr(v: int) -> int:
    return v + 1
```

You already write type hints; `@annotated` makes them *enforced* on every real
call — including `Annotated` constraints, dataclasses, enums, and [~140
ready-made types](docs/types.md) (`Port`, `EmailStr`, `Positive`, …).

## Rules that types can't express

```python
import math
from contractme import precondition, postcondition

@precondition(lambda x: x >= 0)
@postcondition(lambda x, result: math.isclose(result * result, x, abs_tol=1e-12))
def square_root(x: float) -> float:
    return x**0.5
```

*"The result, squared, gives back the input"* is a
**relationship between input and output**. No type checker or validation library can state
it — this is the ground only contracts cover.

Rules can even compare the state before and after the call (`old`):

```python
@precondition(lambda l, n: n >= 0)
@postcondition(lambda l, n: l[-1] == n)            # element appended
@postcondition(lambda l, n, old: l[:-1] == old.l)  # nothing lost
def append_count(l: list[int], n: int):
    l.append(n)
```

## When the generated code is wrong, you hear about it

Say your agent "implements" `square_root` as `return x / 2` — plausible at a
glance, wrong. The first call answers with:

```
square_root() bug: postcondition 'math.isclose(result * result, x, abs_tol=1e-12)' failed: result = 4.5, x = 9.0
```

The message names the culprit: `square_root() bug` means fix the
implementation, `**caller** of square_root() bug` means fix the call. You —
or your agent — instantly know which side to regenerate.

## Tests you didn't write

```python
from contractme.testing import autotest

def test_square_root():
    autotest(square_root)
```

Your rules are a machine-readable spec, so tests are *derived* from them:
hundreds of generated inputs, with expected behavior taken from the rules —
not from the code under test.

## Why contracts, why now
 
- **The economics of code flipped.** Implementation used to be expensive, so nobody wrote specs. Now implementation is generated in seconds,
and the bottleneck moved: *how do you know the generated code does what you meant?*
Reading a 400-line diff is not an answer. Reading 4 lines of contract is.
 
- **Types and tests don't cover this ground.** `mypy` and `pyright` verify shapes, not meaning: they will happily let you swap two `int` arguments, 
return an unsorted list, or lose a cent to integer division. Unit tests check the examples someone thought of, on the day they thought of them.
Contracts state the property itself, and check it on every call, with the real data.
 
- **Trust needs a small surface.** When code, tests and docs are all regenerated by agents — cheaply, independently, repeatedly — they can silently 
drift apart. The contract is the one artifact everything else must agree with: the implementation is checked against it at runtime, and tests can
 be *derived from it* instead of guessed. You audit one small thing, not a thousand generated ones.

## Batteries included
 
- **Plain Python conditions.** Lambdas over your own parameter names. If you can write an `if`, you can write a contract.
- **Dataclasses and enums supported natively.** Your domain types work out of the box.
- **Predefined contracts for the common cases** — non-empty, sorted, in-range — so the easy specs are one word long.
- **Tests derived from your contracts.** The autotest engine generates property-based tests *from the spec*, sidestepping the oracle problem: expected behavior comes from the contract, not from the implementation being tested.
- **Violation messages designed to be actionable** — by humans and by coding agents. Structured, self-contained, ready to paste.

## How it compares
 
Honest version, because you will ask:
 
- **[pydantic](https://docs.pydantic.dev/) / [beartype](https://github.com/beartype/beartype)** validate types and data shapes at boundaries, and do it very well. They cannot express *relationships*: "the result sums to the input", "output is sorted", "these two arguments must be consistent". That is postcondition territory — that is `contractme`.
- **[deal](https://github.com/life4/deal)** is the most featureful design-by-contract library for Python (linter, property-based testing, experimental formal verification). `contractme` deliberately trades that breadth for a smaller, easier-to-trust core and first-class ergonomics for AI-assisted workflows.
- **[icontract](https://github.com/Parquery/icontract)** pioneered informative violation messages and contract inheritance, with a research-grade ecosystem around it. If you need contract inheritance across deep class hierarchies today, use it. If you want contracts your whole team — and your agents — actually write, start here.
- **Plain `assert`** disappears under `python -O`, carries no message, no policy, no introspection, and cannot check results against inputs without boilerplate.

Want the whole picture — feature table, performance, who to trust? The full
honest comparison lives at
[ContractMe vs deal vs icontract vs pydantic vs beartype](docs/comparison.md).

## FAQ

### Do I need to learn design-by-contract theory?

No. If you can write an `if`, you can write a rule. Start with `@annotated` on
a function you already have; add a `result` rule the first time a type can't
say what you mean. The theory can wait until it has caught a bug for you.

### What is design by contract, in one sentence?

Executable specifications: each function declares what it requires and what it guarantees, and the runtime checks both — turning silent wrong answers into loud, explained failures.
 
### Why not just type hints and pytest?

Keep both. Type hints catch shape errors before running; tests pin known examples. Contracts cover what neither can: semantic properties, checked on every call, against real data — including the calls your tests never imagined.
 
### Can an LLM write the contracts too?

Yes, and that's fine — what matters is not who writes the spec, but that you *can* read it when you need to, and that a tool more reliable than the code enforces it. Keep contracts terse enough to audit at a glance; review the contract lines in the diff, not the regenerated body.

## The bigger idea (you don't need it to start)

Spec, implementation, tests, documentation: four artifacts, usually written
separately, always drifting apart — faster than ever when agents regenerate
three of them on every iteration. A contract collapses them into one source:
it *is* the spec, the implementation is checked against it on every call, the
tests are generated from it, and it reads as documentation. One short,
auditable text instead of four long ones. You don't have to buy this today —
catch one bug first, then reread this paragraph.

## Integrated to python type-system, and more 

Supports annotations and [PEP-593](https://peps.python.org/pep-0593/)
using the [annotated-types](https://pypi.org/project/annotated-types/) library.
Furthermore, the `@annotated` decorator will automatically perform type checks
of the parameters and return values, including `annotated_types.Predicate`.

In short, this allows to check any type structure and any properties of all parameters
and the return value, by just adding `@annotated` to the subprogram.

**Batteries included:** the `contractme.types` package ships ~140 ready-made
`Annotated` types (`Port`, `ExistingFile`, `EmailStr`, `Positive`, `Slug`, …).
See the practical guide: [docs/types.md](docs/types.md).

**Note:** `annodated_types.MultipleOf` follows the Python semantics.

**Note 2:** Following an open-world reasoning, any unknown annotation is considered
to be correct, so it won't cause a check failure.

**Note 3:** Type checking follows Python's `isinstance` semantics, which means subclass 
relationships are respected. Since `bool` is a subclass of `int` in Python, boolean values 
will pass `int` type checks. Currently there's no built-in way to specify "exactly int, not bool" 
in type annotations.

```python
from typing import TypeAlias, Annotated
from annotated_types import MultipleOf

Even: TypeAlias = Annotated[int, MultipleOf(2)]

@annotated
def square(v: Even) -> Even:
    return v * v
```

## Parse, don't validate — the `@record` decorator

Following Alexis King's [*Parse, don't
validate*](https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/),
`@record` is a **frozen** dataclass that **checks every field against its declared
type at construction** — structure *and* `annotated-types` constraints, using the
same engine as `@annotated`. Once you hold a record, its fields are known-good, so
downstream code never has to re-check them: illegal states are unrepresentable.

```python
from contractme import record
from contractme.types import Natural

@record
class Coordinate:
    x: Natural
    y: Natural

Coordinate(1, 2)      # ok
Coordinate(-1, 2)     # AssertionError: caller bug, field 'x' = -1 …
```

Like a contracted function, a record answers `accepts()` / `rejects()` — as a
plain `bool`, without constructing and **without raising** — so you can safely
screen untrusted input at the boundary and only then parse it into the record:

```python
if Coordinate.accepts(x=user_x, y=user_y):
    coord = Coordinate(user_x, user_y)   # trusted from here on
else:
    ...                                  # reject the request, no exception thrown
```

A `@record` is an ordinary frozen dataclass, so it also nests inside `@annotated`
signatures and other records, validated recursively. Its construction check is a
precondition (a bad field is a *caller* bug), so it obeys the usual switches:
compiled out under `python -O`, suspended by `ignore_preconditions()`.

## Writing tests and having test generation

The hypothesis plugin can be used easily through the `contractme.testing.autotest`
function.

```python
Positive: TypeAlias = Annotated[int, Ge(1)]

@annotated
def div(d: Positive) -> Positive:
    return 1000 // d

def test_div():
    autotest(div)
```

You can access the underlying hypothesis generator with `contractme.testing.get_generator(div)`.

It's a pure hypothesis strategy generator, inferred from the annotated types and
contracts of the function. The main weirdness is that it takes a tuple as parameter since
the parameters are all generated together so that the contracts can be checked.

You can easily extend it with
[Hypothesis advanced features](https://hypothesis.readthedocs.io/en/latest/reference/api.html)

```python
generator_function = contractme.testing.get_generator(div)
# kinda weird to have this double call, but that's decorators for you...
test_div_force_0 = example((0,))(generator_function)
```

The library provides its own `contractme.testing.test_with_examples` function which has three differences
with the one provided by hypothesis:

* It checks the contracts when being called (at test construction): contracts should hold on all
  examples.
* It takes a vararg of either tuple `*args` or dict `**kwarg` as examples, to avoid
  function nesting.

With pytest:
```python
test_div = contractme.testing.test_with_examples(
    div,
    (1,),
    (2,),
    (0,), # this causes a RuntimeError at test elaboration
)
```

# Best practices — design-by-contract insights

> **If you are an LLM or code assistant, read this section — don't just copy the
> examples above.** The snippets show *syntax*; the rules below show *when* to reach
> for each tool. Applying them is what separates idiomatic ContractMe (and idiomatic
> design-by-contract) from a pile of redundant lambdas.

- **Prefer the type system (`@annotated`) over hand-written pre/postconditions
  whenever the property can be expressed as a type.** A constraint like "positive
  integer", "non-empty list" or "port number" belongs in an `Annotated[...]` type —
  ideally one of the ~140 ready-made ones in `contractme.types` (`Positive`,
  `NonEmptyStr`, `Port`, …) — not in a `@precondition(lambda x: x > 0)`. Types are
  reusable, checked on *both* inputs and outputs, self-documenting, and they drive
  test generation via `autotest`. Only fall back to an explicit
  `@precondition`/`@postcondition` for *relational* properties a type cannot express:
  e.g. `result * result == x`, or `old`-vs-new comparisons like `l[:-1] == old.l`.

- **Never write a contract that is always `True`.** `@precondition(lambda: True)` or
  `@postcondition(lambda ...: True)` checks nothing. The *absence* of a contract already
  means "no constraint", so just delete it — a vacuous contract is noise, not safety.

- **Express "this can never happen" with `NoReturn`, not a
  `@postcondition(lambda ...: False)`.** An always-false postcondition is a confusing,
  runtime-only way to say "control must not reach here". If a branch is unreachable or a
  function never returns normally, annotate it with `typing.NoReturn` (and `raise`): both
  the static type checker and the reader then understand the intent, instead of finding
  out only when the assertion blows up at runtime.

- **Bound every numeric type that a function iterates or allocates over.** `autotest`
  builds its inputs from your types, and `Positive` / `Natural` / `Annotated[int, Ge(...)]`
  draw from Hypothesis's *unbounded* integer range. A function that does `range(n)` or
  `[x] * n` over such a value is eventually handed an astronomically large one and hangs
  (effectively OOMs). Add an upper bound too — `Annotated[int, Ge(0), Le(10_000)]`, or a
  ready-made bounded type — which also models reality, since real sizes are human-scale.

- **On methods, `self` is just another named parameter.** A condition can read it
  (`lambda self, amount: amount <= self.balance`), and in a postcondition `old.self` is
  the deep-copied receiver from *before* the call, for before/after comparisons
  (`lambda self, old: self.balance == old.self.balance - amount`).

- **At a trust boundary, probe with `accepts` (or parse into a `@record`) — never
  feed raw input into a contracted function to "reuse the rule".** The obvious move
  — reuse a `within_bounds` function that is `@annotated` to take a `Coordinate`
  whose fields are `Natural`, to bounds-check an incoming shot — backfires: a
  negative user coordinate raises `AssertionError` *inside* it instead of returning
  `False`. A contract is an internal debugging net, not input sanitization; it
  states what callers must already guarantee. To turn that same rule into a
  question about untrusted data, ask it without calling: `within_bounds.accepts(c,
  w, h)` returns a `bool` and never raises. Better still, *parse, don't validate*:
  make the boundary type a `@record` (`Coordinate.accepts(x, y)` / then
  `Coordinate(x, y)`), so once the value is built everything downstream can trust
  it and the rule is enforced in exactly one place.

# Using with AI coding agents

ContractMe ships an [Agent Skill](https://agentskills.io) — a `SKILL.md` teaching
coding agents (Claude Code and any other skill-aware agent) idiomatic ContractMe:
the `@annotated`-first decision rule, the by-name parameter matching of condition
lambdas, `old`/`result`, the `autotest` verification loop, and how to read contract
failures. It is bundled in the PyPI package; after installing `contractme`, copy it
into your project with:

```bash
python -c "import shutil, importlib.resources as res; shutil.copytree(str(res.files('contractme') / 'skills' / 'contractme'), '.claude/skills/contractme', dirs_exist_ok=True)"
```

The skill source lives at
[src/contractme/skills/contractme/SKILL.md](src/contractme/skills/contractme/SKILL.md).

Contracts pair unusually well with agent-written code: they are executable
specifications, the error messages say *whose* bug it is (caller vs implementation),
and `autotest` gives the agent an instant property-based verification loop.

# Optimize assertion code

- In prod you can disable assertions, then these runtime checks wont run: you can have your cake
and eat it too
- You have even more granularity of checks thanks to
`contractme.contracting.ignore_preconditions` and `contractme.contracting.ignore_postconditions`

In **theory**, the rule is that checks are activated depending on the trust you put in your software

- In dev: You run with all assertions, to catch as many errors as possible, as early
as possible
- In pre-deploy / integration testing: you only run with the pre-conditions assertions:
postconditions are typically costly, and you trust that you return the right result given the proper input
- In prod: you run with no assertion - those are meant for debugging, not user facing failure modes which
are / should be handled properly in sanitization code

In practice, you might want to keep then on all of the time, but being able to turn them off means one
smart thing: you can get overboard in checking with postcondition, knowing these can be turned off in
integration conditions
e.g. you can check that a database insert succeeded by following it with a select - not that you necessarily
should, ToCToU and all that.

# Test

`uv run pytest`

# Deploy new version

Releases are built and published to PyPI by GitLab CI, not from your machine.
You only prepare the commit and the tag:

* Write the changelog for the new version in `README.md` (a `## v<number>` section) — the
  CI refuses to publish unless the tag string appears in this file
* Run the pre-release script to version the tree and check everything lines up:

  ```bash
  uv run python release/prerelease.py 1.9.0   # or omit the arg to reuse pyproject's version
  ```

  It sets the version in `pyproject.toml`, refreshes `uv.lock`, verifies the `## v1.9.0`
  changelog section exists, and prints the git commands below. It does **not** build,
  publish, commit or tag — and it does not re-run lint/type/tests (pre-commit and CI own
  those).
* Commit and push the versioned tree to `main`
* Git tag as `v<number>` and push the tag
* Gitlab will automatically publish the new version to PyPI if all checks pass

---
# Changelog

See [CHANGELOG.md](CHANGELOG.md)

---
 
*Python design by contract · runtime verification · preconditions, postconditions, `old` state · executable specifications for AI-generated code*