Skip to main content

interface-contract

Strict runtime interface contracts for Python, with definition-time failures and signature-aware structural checks.

pip install interface-contract
from interface_contract import Interface, default


class Repository(Interface):
    def find(self, item_id: int) -> str: ...
    def save(self, item: str) -> None: ...

    @property
    def name(self) -> str: ...

    @default
    def describe(self) -> str:
        return f"repository<{self.name}>"


class SqlRepository(Repository):
    def find(self, item_id: int) -> str:
        return f"row {item_id}"

    def save(self, item: str) -> None:
        pass

    @property
    def name(self) -> str:
        return "sql"

If SqlRepository misses a required member, changes its descriptor kind, or has an incompatible signature, class creation raises InterfaceError. You do not need to wait until an instance is created or a method is called.

Why interface-contract?

Python already has abc.ABC and typing.Protocol; this package targets a different boundary: strict runtime validation for plugin systems, application architecture, dependency injection, and dynamically loaded code.

Capability abc.ABC runtime Protocol interface-contract
Missing method detected at runtime instantiation isinstance class definition
Runtime signature validation no no yes
Property/static/class method kind validation no no yes
Signature-aware structural isinstance no no yes
Explicit default implementations concrete method concrete method @default
Optional instance-field contracts annotations only presence only presence + shallow type check
Runtime adapter registry no no yes

This is not a replacement for static typing. Use mypy or another type checker for whole-program analysis, and use interface-contract where runtime boundaries must fail loudly and predictably.

Core behavior

Definition-time validation

Concrete subclasses are checked as soon as their class statement executes. Intermediate implementations can opt out until a concrete subclass is ready:

class BaseRepository(Repository, abstract=True):
    def save(self, item: str) -> None:
        pass


class MemoryRepository(BaseRepository):
    def find(self, item_id: int) -> str:
        return "row"

    @property
    def name(self) -> str:
        return "memory"

Abstract implementations cannot be instantiated.

Default methods

Interface methods normally declare requirements and therefore must have an empty body (..., pass, or a docstring-only body). Mark intentional implementations with @default:

from interface_contract import Interface, default


class Named(Interface):
    @property
    def name(self) -> str: ...

    @default
    def display_name(self) -> str:
        return self.name.title()

Structural interfaces

Set structural=True when inheritance is not under your control:

class Closable(Interface, structural=True):
    def close(self) -> None: ...


class FileLike:
    def close(self) -> None:
        pass


assert isinstance(FileLike(), Closable)
assert issubclass(FileLike, Closable)

Unlike runtime-checkable protocols, the structural check also validates callable signatures and descriptor kinds.

Instance-field contracts

Field checking is opt-in, preserving compatibility with versions that ignored class annotations:

class UserRecord(Interface, check_attributes=True):
    name: str
    age: int


class User(UserRecord):
    def __init__(self, name: str, age: int) -> None:
        self.name = name
        self.age = age

Fields are checked immediately after __init__. Standard annotations receive a best-effort shallow runtime check; parameter contents such as every item inside list[str] are intentionally not traversed. ClassVar does not declare an instance field. Dataclass implementations are supported.

For objects that cannot inherit from an interface, use verify_instance or satisfies:

from interface_contract import satisfies, verify_instance

verify_instance(User("Ada", 37), UserRecord)  # returns the object or raises
assert satisfies(User("Ada", 37), UserRecord)

Adapters

The registry converts an existing type to a target interface and validates the result:

from interface_contract import AdapterRegistry

registry = AdapterRegistry()


@registry.register(dict, UserRecord)
def dict_to_user(data: dict[str, object]) -> User:
    return User(name=str(data["name"]), age=int(data["age"]))


user = registry.adapt({"name": "Ada", "age": 37}, UserRecord)

adapt, can_adapt, register_adapter, and unregister_adapter expose a process-wide default registry when a dedicated registry is unnecessary.

Optional annotation checks

Call signatures are always checked for parameter shape. To also compare available parameter and return annotations, enable check_annotations=True:

class Parser(Interface, check_annotations=True):
    def parse(self, value: str) -> int: ...

Runtime annotation comparison is deliberately conservative. It does not try to replace a static type checker.

Mypy integration

The package is typed and ships an optional mypy plugin. It lets mypy reject the instantiation of incomplete implementations before execution. No extra runtime dependency is installed.

[tool.mypy]
plugins = ["interface_contract.mypy_plugin"]
class Job(Interface):
    def execute(self, payload: str) -> int: ...


class Incomplete(Job):
    pass


Incomplete()  # mypy: Cannot instantiate abstract class "Incomplete"

The mypy plugin API is itself experimental; runtime validation remains the source of truth.

Supported members

  • regular and async methods
  • properties, including independent getter/setter/deleter requirements
  • static methods and class methods
  • generic interfaces
  • multiple and derived interfaces
  • custom metaclass composition
  • source-less environments such as REPL, exec, notebooks, frozen apps, and bytecode-only distributions

Useful inspection functions include members_of, attributes_of, missing_members, missing_attributes, signature_problem, verify, and structurally_implements.

Backward compatibility

The former import path remains fully supported:

from strict_interface import Interface

strict_interface.Interface and interface_contract.Interface are the same object. Existing source code does not need an import migration. The PyPI distribution name is interface-contract; the preferred new import is interface_contract.

Version 0.4.0 is additive except for the distribution rename. Runtime field checking only activates when check_attributes=True is explicitly selected.

Development

python -m pip install -e ".[dev]"
python -m pytest
python -m ruff check .
python -m mypy strict_interface interface_contract typing_tests/valid.py
python -m build
python -m twine check dist/*

See README.tr.md for Turkish documentation and CHANGELOG.md for release notes.

License

MIT

Release files for interface-contract 0.4.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for interface-contract 0.4.0
File Size Uploaded
interface_contract-0.4.0.tar.gz 26.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for interface-contract 0.4.0
File Interpreter ABI Platform
interface_contract-0.4.0-py3-none-any.whl Python 3 none any Details

Total release size:45.2 kB

Release files / interface_contract-0.4.0.tar.gz

Download URL interface_contract-0.4.0.tar.gz
Size 26.7 kB
Tags Source
SHA-256 checksum
How to use checksums
9d7adcbf86c8a767d4dfd749b016d7e0b8be59f8e0b89343509c0c31bc673a12
BLAKE2b-256 checksum
How to use checksums
3006b1da22e698eb172bd0dbf0117258e24e172bd805f2d86a6d695bc1fbbf33
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 30, 2026.

Transparency log

Release files / interface_contract-0.4.0-py3-none-any.whl

Download URL interface_contract-0.4.0-py3-none-any.whl
Size 18.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
bcda8b2dfb82ba0c1285e41cda6c13920b4f79e828e7dca6b54c32760672fa71
BLAKE2b-256 checksum
How to use checksums
1b9290aef6dd221e93788ee8768e0cfc84895128794bf030f685bc8f4d9b0285
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 30, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.4.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page