Skip to main content

A powerful, expressive and lightweight design-by-contract framework

Project description

ContractMe

coverage Checked with pyright Code style: black

A lightweight and adaptable framework for design-by-contract in python

Example code

Here are some examples:

result

@precondition(lambda x: x >= 0)
@postcondition(lambda x, result: eps_eq(result * result, x))
def square_root(x: float) -> float:
    return x**0.5

old

@precondition(lambda l, n: n >= 0 and round(n) == n)
@postcondition(lambda l, n: len(l) > 0)
@postcondition(lambda l, n: l[-1] == n)
@postcondition(lambda l, n, old: l[:-1] == old.l)
def append_count(l: list[int], n: int):
    l.append(n)

Using annotations

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

Supports annotations and PEP-593 using the annotated-types library. 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.

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

Writing tests and having test generation

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

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

@annotated
def div(d: Positive) return 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

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:

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

Test

uv run pytest

Deploy new version

  • uv build
  • Push the resulting new lock file
  • Git tag as v<number>
  • Gitlab will take care of doing the release

Changelog

  • v1.3.0

Binding and helpers to hypothesis library for test data generation.

  • v1.2.0

Full support of annotated-types library for checking PEP-593 compatible type annotations automatically through the @annotated decorator.

Generated contracted functions are now of a ContractedFunction class, with a original_call attribute that contains the function without contracts checking.

Pyright check for the totality of the code.

  • v1.1.0

Contracts can be disabled at runtime with ignore_preconditions() and ignore_postconditions()

Contracts are disabled from the start with python optimized (-O) flag.

Fix a bug where contracts would hide an incorrect function call

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

contractme-1.3.0.tar.gz (20.7 kB view details)

Uploaded Source

Built Distribution

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

contractme-1.3.0-py3-none-any.whl (11.7 kB view details)

Uploaded Python 3

File details

Details for the file contractme-1.3.0.tar.gz.

File metadata

  • Download URL: contractme-1.3.0.tar.gz
  • Upload date:
  • Size: 20.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.6.2

File hashes

Hashes for contractme-1.3.0.tar.gz
Algorithm Hash digest
SHA256 c71289e0f7a50d6565cc863c720c5fcbcac0077aadb306cc22663898d3287573
MD5 d79ff64b9c21d45e7afa877d22a8e6b4
BLAKE2b-256 d2f8406bc3367ba850bbf69e846f641b3e2f84daf671a4285850f71a563477f9

See more details on using hashes here.

File details

Details for the file contractme-1.3.0-py3-none-any.whl.

File metadata

File hashes

Hashes for contractme-1.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 85bc23c28605f76c5168155e673aaed94632b484cd945a003266e85a9a21bd4c
MD5 fe4210638992fee23fed742878d4236e
BLAKE2b-256 229f6449d4d1b3a716e830922f31aef40f7982c4471be397f71fcc323eaa1bfc

See more details on using hashes here.

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