A type-safe mediator pattern implementation for Python 3.12+
Project description
A type-safe request mediator for Python 3.12+
Features
- Type safe. Full runtime validation with mypy support.
- Async/await support. First-class async handlers and mediators via
pymediate.aio. - DI ready. Built-in
dependency-injectorintegration. - Well tested. Comprehensive test suite.
Quick example
from dataclasses import dataclass
from pymediate import Request, Handler, Mediator, Services
# Define response and request as pure dataclasses
@dataclass
class UserCreated:
user_id: int
username: str
@dataclass
class CreateUser(Request[UserCreated]):
username: str
email: str
# Handler automatically linked by type
class CreateUserHandler(Handler[CreateUser]):
def __call__(self, req: CreateUser) -> UserCreated:
return UserCreated(user_id=1, username=req.username)
# Set up and use
services = Services()
services.add(CreateUserHandler())
provider = services.provider()
mediator = Mediator(provider)
response = mediator.send(CreateUser(username="alice", email="alice@example.com"))
print(f"User {response.username} created with ID {response.user_id}")
Async support
PyMediate provides first-class async/await support through the pymediate.aio package:
import asyncio
from dataclasses import dataclass
from pymediate import Request, Services
from pymediate.aio import Handler, Mediator
@dataclass
class UserCreated:
user_id: int
username: str
@dataclass
class CreateUser(Request[UserCreated]):
username: str
email: str
class CreateUserHandler(Handler[CreateUser]):
async def __call__(self, req: CreateUser) -> UserCreated:
# Perform async operations
await asyncio.sleep(0.1) # Simulate async database call
return UserCreated(user_id=1, username=req.username)
async def main():
services = Services()
services.add(CreateUserHandler())
provider = services.provider()
mediator = Mediator(provider)
response = await mediator.send(CreateUser(username="alice", email="alice@example.com"))
print(f"User {response.username} created with ID {response.user_id}")
asyncio.run(main())
Key differences for async:
- Import from
pymediate.aioinstead ofpymediate. - The handler's
__call__method must beasync def. - Use
await mediator.send(...)instead ofmediator.send(...). - Supports concurrent request handling with
asyncio.gather().
Pipeline behaviors
PyMediate supports pipeline behaviors (middleware) that automatically wrap request processing for cross-cutting concerns like logging, validation, caching, and more:
from pymediate import Request, PipelineBehavior
# Universal behavior - applies to all requests
class LoggingBehavior(PipelineBehavior[Request]):
def __call__(self, request, next):
print(f"Handling: {type(request).__name__}")
response = next()
print(f"Completed: {type(request).__name__}")
return response
# Selective behavior - only applies to CreateUser requests
class ValidationBehavior(PipelineBehavior[CreateUser]):
def __call__(self, request, next):
# Validate before processing
if not request.username:
raise ValueError("Username is required")
return next()
# Register behaviors and handlers
services = Services()
services.add(LoggingBehavior()) # Applied to all requests
services.add(ValidationBehavior()) # Only applied to CreateUser
services.add(CreateUserHandler())
mediator = Mediator(services.provider())
# Behaviors automatically wrap matching requests
response = mediator.send(CreateUser(username="alice", email="alice@example.com"))
# Output:
# Handling: CreateUser
# Completed: CreateUser
Behaviors can be universal (PipelineBehavior[Request]) or selective (PipelineBehavior[SpecificRequest]), applying only to matching request types or mixins. They're resolved per request and work with any dependency-injector provider lifetime — Factory, Singleton, or a scoped variant like ContextLocalSingleton. See the Pipeline behaviors guide for more examples.
Installation
# Core package
pip install pymediate
# With dependency injection support
pip install pymediate[di]
Documentation
Development
Quick start
# Clone and install
git clone https://github.com/sina-al/pymediate.git
cd pymediate
uv sync --all-extras --group test
# Run tests
poe test
# Run all checks
poe check:all
# See all available tasks
poe
Available commands
PyMediate uses Poe the Poet for task running. Run poe to see all commands, or check tasks.toml.
Note:
uv syncalone only installs the defaultdevdependency group (ruff, mypy, poethepoet). Test dependencies (pytest and friends) live in the separatetestgroup and won't be installed unless you pass--group test(or--all-groups) — otherwisepoe testfails withFailed to spawn: pytest.
Requirements
- Python 3.12+.
- Optional:
dependency-injector>=4.41.0for DI support.
Versioning
PyMediate follows ZeroVer — the major version stays at 0 indefinitely,
with no planned 1.0. Expect the public API to keep evolving: a minor release (0.X.0) can
include breaking changes, while a patch release (0.1.X) is backward-compatible.
Contributing
Contributions are welcome. See CONTRIBUTING.md for guidelines.
License
MIT License — see LICENSE for details.
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
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 pymediate-0.1.3.tar.gz.
File metadata
- Download URL: pymediate-0.1.3.tar.gz
- Upload date:
- Size: 27.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.26 {"installer":{"name":"uv","version":"0.11.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
247a6570510967b438439eee4811333392093098b32b321f4257bd8b834670f8
|
|
| MD5 |
d073bbe7f221a86b9f81b63d64ed6fbd
|
|
| BLAKE2b-256 |
9af79264a16ddb40207b262e262ebc2508f873aeca432d6c74c8f2f71f1d4eb7
|
File details
Details for the file pymediate-0.1.3-py3-none-any.whl.
File metadata
- Download URL: pymediate-0.1.3-py3-none-any.whl
- Upload date:
- Size: 37.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.26 {"installer":{"name":"uv","version":"0.11.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0509e3c3c12eaa9f2b6f718ca4b3ee71993b4d24a1a303b9863112ec58591ff4
|
|
| MD5 |
4390e1f7b94b53704d7c152a04131d36
|
|
| BLAKE2b-256 |
68e4b72d614e1dfe3aacc6726fb4387302c8ab7d96a4e431b734a792cbd9c67d
|