myfy module protocol, lifecycle phases, and extension patterns. Use when creating new modules, working with configure/extend/finalize methods, module dependencies, or using WebModule, DataModule, FrontendModule, TasksModule, UserModule, CliModule, AuthModule, or RateLimitModule.
Modules are the building blocks of myfy applications. Each module follows a protocol with lifecycle hooks.
myfy provides these built-in modules:
| Module | Package | Purpose | |--------|---------|---------| | WebModule | myfy.web | HTTP routing, ASGI, FastAPI-like decorators | | DataModule | myfy.data | SQLAlchemy async, migrations, sessions | | FrontendModule | myfy.frontend | Jinja2, Tailwind 4, DaisyUI 5, Vite | | TasksModule | myfy.tasks | Background jobs, SQL-based task queue | | UserModule | myfy.user | Auth, OAuth, user management | | CliModule | myfy.commands | Custom CLI commands | | AuthModule | myfy.web.auth | Type-based authentication, protected routes | | RateLimitModule | myfy.web.ratelimit | Rate limiting per IP or user |
from myfy.core import BaseModule, Container, SINGLETON
class MyModule(BaseModule):
"""Custom module following the Module protocol."""
def __init__(self):
super().__init__(name="my-module")
@property
def requires(self) -> list[type]:
"""Module types this module depends on."""
return [] # e.g., [WebModule, DataModule]
@property
def provides(self) -> list[type]:
"""Extension protocols this module implements."""
return [] # e.g., [IWebExtension]
def configure(self, container: Container) -> None:
"""Phase 1: Register providers in DI container."""
container.register(
type_=MyService,
factory=lambda: MyService(),
scope=SINGLETON,
)
def extend(self, container: Container) -> None:
"""Phase 2: Modify other modules' registrations (optional)."""
pass
def finalize(self, container: Container) -> None:
"""Phase 3: Configure singletons after compilation (optional)."""
# Safe to get singletons here - container is compiled
service = container.get(MyService)
service.setup()
async def start(self) -> None:
"""Phase 4: Start runtime services (connect to DB, etc.)."""
pass
async def stop(self) -> None:
"""Phase 5: Cleanup resources gracefully."""
pass
Declare dependencies via the requires property:
from myfy.web import WebModule
from myfy.data import DataModule
class MyModule(BaseModule):
@property
def requires(self) -> list[type]:
# This module needs WebModule and DataModule to be registered
return [WebModule, DataModule]
The application validates all required modules are present and initializes them in topological order.
Modules typically register their own settings:
from myfy.core.config import load_settings
from myfy.core.di.types import ProviderKey
class MyModule(BaseModule):
def __init__(self, settings: MySettings | None = None):
super().__init__("my-module")
self._settings = settings
def configure(self, container: Container) -> None:
# Check if settings already registered (avoid double registration)
key = ProviderKey(MySettings)
if key not in container._providers:
if self._settings is None:
self._settings = load_settings(MySettings)
container.register(
type_=MySettings,
factory=lambda: self._settings,
scope=SINGLETON,
)
Define extension interfaces using provides:
from myfy.web.extensions import IWebExtension
class MyModule(BaseModule):
@property
def provides(self) -> list[type]:
return [IWebExtension]
def finalize(self, container: Container) -> None:
# Access ASGIApp after compilation
asgi_app = container.get(ASGIApp)
asgi_app.app.mount("/my-path", MyASGIApp())
"""
MyFeature module for myfy.
Provides feature X with Y capabilities.
"""
from __future__ import annotations
import logging
from typing import TYPE_CHECKING
from myfy.core import BaseModule, SINGLETON
from myfy.core.config import load_settings
from myfy.core.di.types import ProviderKey
from .config import MyFeatureSettings
from .service import MyFeatureService
if TYPE_CHECKING:
from myfy.core.di import Container
logger = logging.getLogger(__name__)
class MyFeatureModule(BaseModule):
"""
MyFeature module.
Features:
- Feature capability 1
- Feature capability 2
"""
def __init__(self, settings: MyFeatureSettings | None = None):
super().__init__("my-feature")
self._settings = settings
@property
def requires(self) -> list[type]:
return []
@property
def provides(self) -> list[type]:
return []
def configure(self, container: Container) -> None:
logger.debug("Configuring MyFeatureModule...")
# Register settings
key = ProviderKey(MyFeatureSettings)
if key not in container._providers:
if self._settings is None:
self._settings = load_settings(MyFeatureSettings)
container.register(
type_=MyFeatureSettings,
factory=lambda: self._settings,
scope=SINGLETON,
)
# Register services
container.register(
type_=MyFeatureService,
factory=lambda settings=self._settings: MyFeatureService(settings),
scope=SINGLETON,
)
logger.debug("MyFeatureModule configured")
async def start(self) -> None:
logger.info("MyFeature module started")
async def stop(self) -> None:
logger.info("MyFeature module stopped")
# Module instance for entry point discovery
my_feature_module = MyFeatureModule()
npx skills add psincraian/module-development下载完整 Skill 目录,包含 SKILL.md 及所有相关文件
Search for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer