Ports-and-adapters bootstrap decorators, autoload utilities, and standalone authz helpers.
Project description
iisi-app-core
iisi-app-core is a small Python library for two things:
- bootstrapping ports-and-adapters applications with decorators, autoload, and
punq - lightweight role-based authorization with an explicit
Principal
Requirements
- Python
>=3.14,<4
Installation
pip install iisi-app-core
Bootstrap Quickstart
1. Mark ports and implementations
from dataclasses import dataclass
from iisi_app_core import (
ContainerBuilder,
application,
driven_adapter,
driven_port,
driving_adapter,
driving_port,
)
@driven_port
class UserRepository:
def get(self, user_id: str) -> dict[str, str]:
raise NotImplementedError
@driven_adapter(port=UserRepository)
class DynamoUserRepository(UserRepository):
def get(self, user_id: str) -> dict[str, str]:
return {"id": user_id, "name": "Ada"}
@driving_port
class GetUserProfilePort:
def get_user_profile(self, user_id: str) -> dict[str, str]:
raise NotImplementedError
@dataclass(frozen=True, slots=True)
@application(port=GetUserProfilePort)
class GetUserProfile(GetUserProfilePort):
repo: UserRepository
def get_user_profile(self, user_id: str) -> dict[str, str]:
return self.repo.get(user_id)
@dataclass(frozen=True, slots=True)
@driving_adapter
class GetUserProfileHandler:
app: GetUserProfilePort
def handle(self, user_id: str) -> dict[str, str]:
return self.app.get_user_profile(user_id)
ContainerBuilder.build() validates the boundaries:
- driving adapters may depend only on
@driving_portports - applications may depend only on
@driven_portports - depending on a decorated implementation directly raises
PortsAndAdaptersViolationError
2. Autoload modules
import app.users
from iisi_app_core import autoload
modules = autoload(app.users)
autoload(...) expects an imported package object and recursively imports all Python modules under it, excluding __init__.py.
3. Build the container
from iisi_app_core import ComponentRegistry, ContainerBuilder
registry = ComponentRegistry.from_modules(modules)
container = ContainerBuilder(registry).build()
handler = container.resolve(GetUserProfileHandler)
result = handler.handle("123")
Both the declared port and the concrete implementation are registered in punq.
4. Optional FastAPI + Mangum bootstrap
Install the framework extra when you want a web bootstrap path:
pip install "iisi-app-core[fastapi]"
from typing import Annotated
import app.users
from fastapi import APIRouter, FastAPI
from iisi_app_core.integrations.fastapi import FastApiMangumBuilder, from_container
def create_app(ctx) -> FastAPI:
from app.users.ports import GetUserProfilePort
router = APIRouter()
@router.get("/users/{user_id}")
def get_user_profile(
user_id: str,
use_case: Annotated[GetUserProfilePort, from_container(GetUserProfilePort)],
) -> dict[str, str]:
return use_case.get_user_profile(user_id)
app = FastAPI()
app.include_router(router)
return app
built = (
FastApiMangumBuilder()
.autoload(app.users)
.app(create_app)
.enable_mangum(lifespan="off")
.build()
)
app = built.app
handler = built.handler
FastApiMangumBuilder reuses the existing autoload(...) and ContainerBuilder.build() logic, then exposes the built container, registry, and modules through app.state.
Standalone Authz
authz is explicit and does not use request-local global state.
from iisi_app_core import Principal, Role, require_role
@require_role(Role.READER)
def view_profile(*, principal: Principal, user_id: str) -> str:
return f"{principal.name}:{user_id}"
principal = Principal.of(name="alice", role=Role.READER, tenant_id="tenant")
result = view_profile(principal=principal, user_id="123")
If your callable does not receive principal= directly, provide principal_getter=...:
from iisi_app_core import Role, require_role
@require_role(Role.ADMIN, principal_getter=lambda args, kwargs: args[0].principal)
def run(command) -> str:
return "ok"
Available authz helpers:
Principal,Roleauthorize,ensure_at_least,at_least,has_rolerequire_roleDomainValidationError,ForbiddenError
Public API
Top-level exports are intentionally small:
- bootstrap:
autoload,ModuleAutoloadError, decorators, metadata types,ComponentRegistry,ContainerBuilder - authz:
Principal,Role,require_role, helper functions,DomainValidationError,ForbiddenError
Everything else from earlier versions is out of scope for this library.
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 iisi_app_core-0.2.2.tar.gz.
File metadata
- Download URL: iisi_app_core-0.2.2.tar.gz
- Upload date:
- Size: 17.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ea27e9adac796b29198871ccec694e2d9eac32a606e603ec61ac73984bd47837
|
|
| MD5 |
040ba74858628f721b8ed4c577893032
|
|
| BLAKE2b-256 |
6ab3b7c6d7bcddea9d0bb1a7a67a6664cb291335ee15e6edb01189f891b0fefa
|
File details
Details for the file iisi_app_core-0.2.2-py3-none-any.whl.
File metadata
- Download URL: iisi_app_core-0.2.2-py3-none-any.whl
- Upload date:
- Size: 13.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0912dcf3c6937a523290b683b3b77f9d6e795d1de66558feede651e2bf9dca90
|
|
| MD5 |
6726bd63f7347f8ebeb40f9bc38854d0
|
|
| BLAKE2b-256 |
903312518f1a1c20ca09056beb9ac99bddeed0d7fea8f9917ad5be16363aeaac
|