mirror of
https://github.com/d3vyce/fastapi-toolsets.git
synced 2026-03-02 01:10:47 +01:00
feat: allow custom CLI (#28)
This commit is contained in:
@@ -1,52 +1,28 @@
|
||||
"""CLI configuration."""
|
||||
"""CLI configuration and dynamic imports."""
|
||||
|
||||
import importlib
|
||||
import sys
|
||||
import tomllib
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
|
||||
import typer
|
||||
|
||||
|
||||
@dataclass
|
||||
class CliConfig:
|
||||
"""CLI configuration loaded from pyproject.toml."""
|
||||
|
||||
fixtures: str | None = None
|
||||
db_context: str | None = None
|
||||
|
||||
def get_fixtures_registry(self):
|
||||
"""Import and return the fixtures registry."""
|
||||
from ..fixtures import FixtureRegistry
|
||||
|
||||
if not self.fixtures:
|
||||
raise typer.BadParameter(
|
||||
"No fixtures registry configured. "
|
||||
"Add 'fixtures' to [tool.fastapi-toolsets] in pyproject.toml."
|
||||
)
|
||||
|
||||
registry = _import_from_string(self.fixtures)
|
||||
|
||||
if not isinstance(registry, FixtureRegistry):
|
||||
raise typer.BadParameter(
|
||||
f"'fixtures' must be a FixtureRegistry instance, got {type(registry).__name__}"
|
||||
)
|
||||
|
||||
return registry
|
||||
|
||||
def get_db_context(self):
|
||||
"""Import and return the db_context function."""
|
||||
if not self.db_context:
|
||||
raise typer.BadParameter(
|
||||
"No db_context configured. "
|
||||
"Add 'db_context' to [tool.fastapi-toolsets] in pyproject.toml."
|
||||
)
|
||||
return _import_from_string(self.db_context)
|
||||
from .pyproject import find_pyproject, load_pyproject
|
||||
|
||||
|
||||
def _import_from_string(import_path: str):
|
||||
"""Import an object from a string path like 'module.submodule:attribute'."""
|
||||
def _ensure_project_in_path():
|
||||
"""Add project root to sys.path if not installed in editable mode."""
|
||||
pyproject = find_pyproject()
|
||||
if pyproject:
|
||||
project_root = str(pyproject.parent)
|
||||
if project_root not in sys.path:
|
||||
sys.path.insert(0, project_root)
|
||||
|
||||
|
||||
def import_from_string(import_path: str):
|
||||
"""Import an object from a string path like 'module.submodule:attribute'.
|
||||
|
||||
Raises:
|
||||
typer.BadParameter: If the import path is invalid or import fails.
|
||||
"""
|
||||
if ":" not in import_path:
|
||||
raise typer.BadParameter(
|
||||
f"Invalid import path '{import_path}'. Expected format: 'module:attribute'"
|
||||
@@ -54,10 +30,7 @@ def _import_from_string(import_path: str):
|
||||
|
||||
module_path, attr_name = import_path.rsplit(":", 1)
|
||||
|
||||
# Add cwd to sys.path for local imports
|
||||
cwd = str(Path.cwd())
|
||||
if cwd not in sys.path:
|
||||
sys.path.insert(0, cwd)
|
||||
_ensure_project_in_path()
|
||||
|
||||
try:
|
||||
module = importlib.import_module(module_path)
|
||||
@@ -72,21 +45,63 @@ def _import_from_string(import_path: str):
|
||||
return getattr(module, attr_name)
|
||||
|
||||
|
||||
def load_config() -> CliConfig:
|
||||
"""Load CLI configuration from pyproject.toml."""
|
||||
pyproject_path = Path.cwd() / "pyproject.toml"
|
||||
def get_config_value(key: str, required: bool = False):
|
||||
"""Get a configuration value from pyproject.toml.
|
||||
|
||||
if not pyproject_path.exists():
|
||||
return CliConfig()
|
||||
Args:
|
||||
key: The configuration key in [tool.fastapi-toolsets].
|
||||
required: If True, raises an error when the key is missing.
|
||||
|
||||
try:
|
||||
with open(pyproject_path, "rb") as f:
|
||||
data = tomllib.load(f)
|
||||
Returns:
|
||||
The configuration value, or None if not found and not required.
|
||||
|
||||
tool_config = data.get("tool", {}).get("fastapi-toolsets", {})
|
||||
return CliConfig(
|
||||
fixtures=tool_config.get("fixtures"),
|
||||
db_context=tool_config.get("db_context"),
|
||||
Raises:
|
||||
typer.BadParameter: If required=True and the key is missing.
|
||||
"""
|
||||
config = load_pyproject()
|
||||
value = config.get(key)
|
||||
|
||||
if required and value is None:
|
||||
raise typer.BadParameter(
|
||||
f"No '{key}' configured. "
|
||||
f"Add '{key}' to [tool.fastapi-toolsets] in pyproject.toml."
|
||||
)
|
||||
except Exception:
|
||||
return CliConfig()
|
||||
|
||||
return value
|
||||
|
||||
|
||||
def get_fixtures_registry():
|
||||
"""Import and return the fixtures registry from config."""
|
||||
from ..fixtures import FixtureRegistry
|
||||
|
||||
import_path = get_config_value("fixtures", required=True)
|
||||
registry = import_from_string(import_path)
|
||||
|
||||
if not isinstance(registry, FixtureRegistry):
|
||||
raise typer.BadParameter(
|
||||
f"'fixtures' must be a FixtureRegistry instance, got {type(registry).__name__}"
|
||||
)
|
||||
|
||||
return registry
|
||||
|
||||
|
||||
def get_db_context():
|
||||
"""Import and return the db_context function from config."""
|
||||
import_path = get_config_value("db_context", required=True)
|
||||
return import_from_string(import_path)
|
||||
|
||||
|
||||
def get_custom_cli() -> typer.Typer | None:
|
||||
"""Import and return the custom CLI Typer instance from config."""
|
||||
import_path = get_config_value("custom_cli")
|
||||
if not import_path:
|
||||
return None
|
||||
|
||||
custom = import_from_string(import_path)
|
||||
|
||||
if not isinstance(custom, typer.Typer):
|
||||
raise typer.BadParameter(
|
||||
f"'custom_cli' must be a Typer instance, got {type(custom).__name__}"
|
||||
)
|
||||
|
||||
return custom
|
||||
|
||||
Reference in New Issue
Block a user