A package to define interface in Python
Project description
interface-py
interface-py is a lightweight Python package for defining interfaces and concrete implementations with enforced contracts.
It ensures that concrete classes implement all required methods, properties, and fields, including validation for empty or invalid property getters.
Features
- Define interfaces using the
@interfacedecorator. - Enforce that concrete classes implement all interface methods, fields, and properties.
- Detects and enforces rules for empty, explicit, and invalid getters/setters.
- Support for fields with four declaration styles:
- With annotation only →
x: int - With annotation +
...→y: float = ... - Without annotation +
...→z = ... - With direct type assignment →
DataModel = dictor any other type/class
- With annotation only →
- Ensures that property getters defined explicitly in interfaces always raise an error.
- Allows defining properties in interfaces with empty bodies (
...,pass, or docstring-only`). - Enforce getter, setter, and deleter implementation rules for properties.
- Supports multi-level interface hierarchies with automatic contract aggregation.
- Prevents runtime errors from missing implementations.
- Works alongside Python's built-in ABCs.
Installation
pip install interface-py
Usage
Defining an Interface
from interface_py import interface
@interface
class HumanInterface:
# field definitions
name: str
age: int = ...
nickname = ...
DataModel = dict # direct type assignment
def speak(self): ...
@property
def rank(self): ...
@rank.setter
def rank(self, value): ...
- Property definitions in interfaces may have an empty body (
...,pass, or docstring-only`). - However, explicit getters (e.g.
@rank.getter) are not allowed in interfaces, even if their body is empty.
🦆 Duck Typing (Structural Compatibility Checks)
interface-py provides structural compatibility checks via a separate submodule:
interface_py.duck_typing
This module lets you check whether a class or instance matches an interface contract structurally,
even if it doesn’t explicitly inherit from it.
Example
from interface_py import interface
from interface_py.duck_typing import is_duck_subclass, is_duck_instance
@interface
class FlyableInterface:
def fly(self): ...
class Bird:
def fly(self):
print("Flying high!")
class Plane:
def fly(self):
print("Taking off!")
# Structural (duck) compatibility check
assert is_duck_subclass(Bird, FlyableInterface)
assert is_duck_instance(Plane(), FlyableInterface)
✅ In this example:
- Neither
BirdnorPlaneexplicitly inherit fromFlyableInterface. - However, both provide the required
fly()method. - Therefore, they are structurally compatible with the interface.
(“If it flies like a bird, it’s a bird.”)
Notes
- These checks are opt-in and non-intrusive —
They don’t affect Python’s built-inissubclass()orisinstance()behavior.
Instead, you explicitly callis_duck_subclass()oris_duck_instance(). - Works with:
- Methods
- Properties
- Fields
- Static methods
- Class methods
- This design avoids modifying the MRO or inheritance hierarchy.
- Future versions may extract this logic into a standalone package for broader reuse.
Import Path
from interface_py.duck_typing import (
is_duck_subclass,
is_duck_instance,
)
Multi-level Interface Example
from interface_py import interface, concrete
@interface
class MilitaryHumanInterface(HumanInterface):
def march(self): ...
@concrete
class Soldier(MilitaryHumanInterface):
name: str = "John"
age: int = 25
nickname = "Eagle"
def speak(self):
print("Reporting for duty!")
def march(self):
print("Marching!")
@property
def rank(self):
return self._rank
@rank.setter
def rank(self, value):
self._rank = value
MilitaryHumanInterfaceextendsHumanInterface.Soldierimplements all required methods, fields, and properties from both interfaces automatically.- Multi-level inheritance automatically merges all parent interface contracts.
Field Enforcement Examples
from interface_py import interface, concrete
@interface
class ExampleInterface:
x: int # only annotation
y: float = ... # annotation with ellipsis
z = ... # plain ellipsis
DataModel = dict # direct type assignment
# ✅ Correct implementation
@concrete
class GoodImpl(ExampleInterface):
x: int = 10
y: float = 3.14
z = "hello"
DataModel = dict
# ❌ Incorrect implementation
@concrete
class BadImpl(ExampleInterface):
x: str = "oops" # wrong type (expected int)
# y missing → TypeError
z = ... # not allowed to keep ellipsis
DataModel = list # wrong type assignment
Method Enforcement Rules
In interfaces, all methods must have an empty body.
Empty bodies are recognized as:
pass...- docstring-only functions (for example, a function that only contains a string literal as its body)
In concrete classes, all methods must have a non-empty body.
Methods defined with only pass, ..., or docstring-only are considered unimplemented and raise a TypeError.
Property Enforcement Rules
-
A property can be declared in an interface as:
@property def data(self): ...
-
The above is valid and treated as a contract placeholder.
-
However, the following is invalid and raises an error:
@property def data(self): pass @data.getter def data(self): ...
-
Concrete classes must implement the getter, and if defined in the interface, also the setter and deleter.
Validation
- Instantiating a concrete class that does not implement all interface methods/fields/properties raises a
TypeError. - Ensures consistent interface contracts across your project.
- The decorator
@interfaceautomatically enforces the interface behavior without requiring any base class. - The decorator
@concreteensures that at least one parent of the concrete class is an interface.
Why Use interface-py?
- Provides contract enforcement in dynamically typed Python.
- Detects incomplete or empty implementations early.
- Helps structure large codebases with clear interface and implementation separation.
- Avoids runtime errors from missing or placeholder methods.
- Enforces correctness while remaining Pythonic.
License
MIT License
Project details
Release history Release notifications | RSS feed
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 interface_py-1.5.0.tar.gz.
File metadata
- Download URL: interface_py-1.5.0.tar.gz
- Upload date:
- Size: 10.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7e882043a54f38ae1ef6a812d5654492b8962ad9cdb33cd7f89d43d19f0d6289
|
|
| MD5 |
016164e4cfe558bfd978c97fb64e9b3f
|
|
| BLAKE2b-256 |
2b810a268d9fefacb0e9766fe25b9e47730dc21ac9f6ef7b6e9692535c0c2cb6
|
File details
Details for the file interface_py-1.5.0-py3-none-any.whl.
File metadata
- Download URL: interface_py-1.5.0-py3-none-any.whl
- Upload date:
- Size: 9.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ca1ad8935d903fcb5b250fd63b83a10a3c35951fca3507bb1074ed48afd29f24
|
|
| MD5 |
4d79f7c728373950e1008df893b1fbd4
|
|
| BLAKE2b-256 |
e7bfa371faab32665f81814fcbe89800433cdc7954dbcf54221d05b87d0a7108
|