Skip to main content

A type-safe mediator pattern implementation for Python 3.12+

Project description

PyMediate logo

A type-safe request mediator for Python 3.12+

Tests Code Quality Python 3.12+ PyPI version MIT License


Features

  • Type Safety: Full runtime validation with mypy support
  • Zero Convention: No naming conventions - uses type inspection
  • Async/Await Support: First-class async handlers and mediators via pymediate.aio
  • DI Ready: Built-in dependency-injector integration
  • Dataclass Friendly: Works seamlessly with @dataclass and Request[T] inheritance
  • 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.aio instead of pymediate
  • Handler's __call__ method must be async def
  • Use await mediator.send(...) instead of mediator.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 are resolved per request, respecting DI container scopes (Transient, Scoped, Singleton). See the Pipeline Behaviors Guide for more examples.

Installation

# Core package
pip install pymediate

# With dependency injection support
pip install pymediate[di]

Documentation

📚 Full 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 sync alone only installs the default dev dependency group (ruff, mypy, poethepoet). Test dependencies (pytest and friends) live in the separate test group and won't be installed unless you pass --group test (or --all-groups) — otherwise poe test fails with Failed to spawn: pytest.

Requirements

  • Python 3.12+
  • Optional: dependency-injector>=4.41.0 for DI support

Contributing

Contributions are welcome! Check the docs 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

pymediate-0.1.1.tar.gz (33.1 kB view details)

Uploaded Source

Built Distribution

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

pymediate-0.1.1-py3-none-any.whl (43.2 kB view details)

Uploaded Python 3

File details

Details for the file pymediate-0.1.1.tar.gz.

File metadata

  • Download URL: pymediate-0.1.1.tar.gz
  • Upload date:
  • Size: 33.1 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

Hashes for pymediate-0.1.1.tar.gz
Algorithm Hash digest
SHA256 51d3e64936f6414c97b3b5f28659819b987aa2a2565c325c16018cffaba6d864
MD5 99468d158cbe7c9b17806f881dc06aea
BLAKE2b-256 719d2ab8d361f8a7ea780d40b8830ad02b3b0af0281111c95e017bc0e1d608c9

See more details on using hashes here.

File details

Details for the file pymediate-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: pymediate-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 43.2 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

Hashes for pymediate-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 67819dcd44c338f2e5e07c0a3b7010a2604bb5a0ea646b31e60507992f6c5909
MD5 3578357a40e32599387aa2031128e34f
BLAKE2b-256 53ae70ef7a035bb5f2510e5c8ee3f2c324ef140a5fbc0ee79df9dff9e10abe20

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