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.0.tar.gz (33.0 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.0-py3-none-any.whl (43.2 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: pymediate-0.1.0.tar.gz
  • Upload date:
  • Size: 33.0 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.0.tar.gz
Algorithm Hash digest
SHA256 0725e459925c6126ebf44c317ebc496262ba8b28cb44d39ae682532b30753c22
MD5 2830dc729e37d933f053df1e6408a69b
BLAKE2b-256 25b4df37942ea5de67cfaf21bdb3eddcf2f82c33331b7ed61ddced0b709b7c63

See more details on using hashes here.

File details

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

File metadata

  • Download URL: pymediate-0.1.0-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.0-py3-none-any.whl
Algorithm Hash digest
SHA256 2d3567361d3f53fe2b05a21d22836784a5d7c5499a8cd918c1369ed375ef5ebc
MD5 67fd0d233e0a2c7612a6b9b8e4a2de0e
BLAKE2b-256 8954eb3e4f8c13431ef52f6fdd5076558e2c8956c9da15ed782fa5b1ed2006b9

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