fastapi-toolsets 0.5.0__py3-none-any.whl → 0.6.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fastapi_toolsets/__init__.py +1 -1
- fastapi_toolsets/cli/__init__.py +1 -2
- fastapi_toolsets/cli/app.py +17 -12
- fastapi_toolsets/cli/commands/fixtures.py +16 -35
- fastapi_toolsets/cli/config.py +71 -56
- fastapi_toolsets/cli/pyproject.py +43 -0
- fastapi_toolsets/dependencies/__init__.py +5 -0
- fastapi_toolsets/dependencies/factory.py +139 -0
- {fastapi_toolsets-0.5.0.dist-info → fastapi_toolsets-0.6.0.dist-info}/METADATA +2 -2
- {fastapi_toolsets-0.5.0.dist-info → fastapi_toolsets-0.6.0.dist-info}/RECORD +13 -10
- {fastapi_toolsets-0.5.0.dist-info → fastapi_toolsets-0.6.0.dist-info}/WHEEL +1 -1
- {fastapi_toolsets-0.5.0.dist-info → fastapi_toolsets-0.6.0.dist-info}/entry_points.txt +0 -0
- {fastapi_toolsets-0.5.0.dist-info → fastapi_toolsets-0.6.0.dist-info}/licenses/LICENSE +0 -0
fastapi_toolsets/__init__.py
CHANGED
fastapi_toolsets/cli/__init__.py
CHANGED
fastapi_toolsets/cli/app.py
CHANGED
|
@@ -2,17 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
import typer
|
|
4
4
|
|
|
5
|
-
from .config import
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
5
|
+
from .config import get_custom_cli
|
|
6
|
+
from .pyproject import load_pyproject
|
|
7
|
+
|
|
8
|
+
# Use custom CLI if configured, otherwise create default one
|
|
9
|
+
_custom_cli = get_custom_cli()
|
|
10
|
+
|
|
11
|
+
if _custom_cli is not None:
|
|
12
|
+
cli = _custom_cli
|
|
13
|
+
else:
|
|
14
|
+
cli = typer.Typer(
|
|
15
|
+
name="manager",
|
|
16
|
+
help="CLI utilities for FastAPI projects.",
|
|
17
|
+
no_args_is_help=True,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
_config = load_pyproject()
|
|
21
|
+
if _config.get("fixtures") and _config.get("db_context"):
|
|
16
22
|
from .commands.fixtures import fixture_cli
|
|
17
23
|
|
|
18
24
|
cli.add_typer(fixture_cli, name="fixtures")
|
|
@@ -22,4 +28,3 @@ if _config.fixtures:
|
|
|
22
28
|
def main(ctx: typer.Context) -> None:
|
|
23
29
|
"""FastAPI utilities CLI."""
|
|
24
30
|
ctx.ensure_object(dict)
|
|
25
|
-
ctx.obj["config"] = _config
|
|
@@ -7,7 +7,7 @@ from rich.console import Console
|
|
|
7
7
|
from rich.table import Table
|
|
8
8
|
|
|
9
9
|
from ...fixtures import Context, LoadStrategy, load_fixtures_by_context
|
|
10
|
-
from ..config import
|
|
10
|
+
from ..config import get_db_context, get_fixtures_registry
|
|
11
11
|
from ..utils import async_command
|
|
12
12
|
|
|
13
13
|
fixture_cli = typer.Typer(
|
|
@@ -18,27 +18,21 @@ fixture_cli = typer.Typer(
|
|
|
18
18
|
console = Console()
|
|
19
19
|
|
|
20
20
|
|
|
21
|
-
def _get_config(ctx: typer.Context) -> CliConfig:
|
|
22
|
-
"""Get CLI config from context."""
|
|
23
|
-
return ctx.obj["config"]
|
|
24
|
-
|
|
25
|
-
|
|
26
21
|
@fixture_cli.command("list")
|
|
27
22
|
def list_fixtures(
|
|
28
23
|
ctx: typer.Context,
|
|
29
24
|
context: Annotated[
|
|
30
|
-
|
|
25
|
+
Context | None,
|
|
31
26
|
typer.Option(
|
|
32
27
|
"--context",
|
|
33
28
|
"-c",
|
|
34
|
-
help="Filter by context
|
|
29
|
+
help="Filter by context.",
|
|
35
30
|
),
|
|
36
31
|
] = None,
|
|
37
32
|
) -> None:
|
|
38
33
|
"""List all registered fixtures."""
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
fixtures = registry.get_by_context(context) if context else registry.get_all()
|
|
34
|
+
registry = get_fixtures_registry()
|
|
35
|
+
fixtures = registry.get_by_context(context.value) if context else registry.get_all()
|
|
42
36
|
|
|
43
37
|
if not fixtures:
|
|
44
38
|
print("No fixtures found.")
|
|
@@ -60,17 +54,13 @@ def list_fixtures(
|
|
|
60
54
|
async def load(
|
|
61
55
|
ctx: typer.Context,
|
|
62
56
|
contexts: Annotated[
|
|
63
|
-
list[
|
|
64
|
-
typer.Argument(
|
|
65
|
-
help="Contexts to load (base, production, development, testing)."
|
|
66
|
-
),
|
|
57
|
+
list[Context] | None,
|
|
58
|
+
typer.Argument(help="Contexts to load."),
|
|
67
59
|
] = None,
|
|
68
60
|
strategy: Annotated[
|
|
69
|
-
|
|
70
|
-
typer.Option(
|
|
71
|
-
|
|
72
|
-
),
|
|
73
|
-
] = "merge",
|
|
61
|
+
LoadStrategy,
|
|
62
|
+
typer.Option("--strategy", "-s", help="Load strategy."),
|
|
63
|
+
] = LoadStrategy.MERGE,
|
|
74
64
|
dry_run: Annotated[
|
|
75
65
|
bool,
|
|
76
66
|
typer.Option(
|
|
@@ -79,19 +69,10 @@ async def load(
|
|
|
79
69
|
] = False,
|
|
80
70
|
) -> None:
|
|
81
71
|
"""Load fixtures into the database."""
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
get_db_context = config.get_db_context()
|
|
85
|
-
|
|
86
|
-
context_list = contexts if contexts else [Context.BASE]
|
|
72
|
+
registry = get_fixtures_registry()
|
|
73
|
+
db_context = get_db_context()
|
|
87
74
|
|
|
88
|
-
|
|
89
|
-
load_strategy = LoadStrategy(strategy)
|
|
90
|
-
except ValueError:
|
|
91
|
-
typer.echo(
|
|
92
|
-
f"Invalid strategy: {strategy}. Use: merge, insert, skip_existing", err=True
|
|
93
|
-
)
|
|
94
|
-
raise typer.Exit(1)
|
|
75
|
+
context_list = [c.value for c in contexts] if contexts else [Context.BASE]
|
|
95
76
|
|
|
96
77
|
ordered = registry.resolve_context_dependencies(*context_list)
|
|
97
78
|
|
|
@@ -99,7 +80,7 @@ async def load(
|
|
|
99
80
|
print("No fixtures to load for the specified context(s).")
|
|
100
81
|
return
|
|
101
82
|
|
|
102
|
-
print(f"\nFixtures to load ({
|
|
83
|
+
print(f"\nFixtures to load ({strategy.value} strategy):")
|
|
103
84
|
for name in ordered:
|
|
104
85
|
fixture = registry.get(name)
|
|
105
86
|
instances = list(fixture.func())
|
|
@@ -110,9 +91,9 @@ async def load(
|
|
|
110
91
|
print("\n[Dry run - no changes made]")
|
|
111
92
|
return
|
|
112
93
|
|
|
113
|
-
async with
|
|
94
|
+
async with db_context() as session:
|
|
114
95
|
result = await load_fixtures_by_context(
|
|
115
|
-
session, registry, *context_list, strategy=
|
|
96
|
+
session, registry, *context_list, strategy=strategy
|
|
116
97
|
)
|
|
117
98
|
|
|
118
99
|
total = sum(len(items) for items in result.values())
|
fastapi_toolsets/cli/config.py
CHANGED
|
@@ -1,52 +1,28 @@
|
|
|
1
|
-
"""CLI configuration."""
|
|
1
|
+
"""CLI configuration and dynamic imports."""
|
|
2
2
|
|
|
3
3
|
import importlib
|
|
4
4
|
import sys
|
|
5
|
-
import tomllib
|
|
6
|
-
from dataclasses import dataclass
|
|
7
|
-
from pathlib import Path
|
|
8
5
|
|
|
9
6
|
import typer
|
|
10
7
|
|
|
8
|
+
from .pyproject import find_pyproject, load_pyproject
|
|
11
9
|
|
|
12
|
-
@dataclass
|
|
13
|
-
class CliConfig:
|
|
14
|
-
"""CLI configuration loaded from pyproject.toml."""
|
|
15
10
|
|
|
16
|
-
|
|
17
|
-
|
|
11
|
+
def _ensure_project_in_path():
|
|
12
|
+
"""Add project root to sys.path if not installed in editable mode."""
|
|
13
|
+
pyproject = find_pyproject()
|
|
14
|
+
if pyproject:
|
|
15
|
+
project_root = str(pyproject.parent)
|
|
16
|
+
if project_root not in sys.path:
|
|
17
|
+
sys.path.insert(0, project_root)
|
|
18
18
|
|
|
19
|
-
def get_fixtures_registry(self):
|
|
20
|
-
"""Import and return the fixtures registry."""
|
|
21
|
-
from ..fixtures import FixtureRegistry
|
|
22
19
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
"No fixtures registry configured. "
|
|
26
|
-
"Add 'fixtures' to [tool.fastapi-toolsets] in pyproject.toml."
|
|
27
|
-
)
|
|
20
|
+
def import_from_string(import_path: str):
|
|
21
|
+
"""Import an object from a string path like 'module.submodule:attribute'.
|
|
28
22
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
raise typer.BadParameter(
|
|
33
|
-
f"'fixtures' must be a FixtureRegistry instance, got {type(registry).__name__}"
|
|
34
|
-
)
|
|
35
|
-
|
|
36
|
-
return registry
|
|
37
|
-
|
|
38
|
-
def get_db_context(self):
|
|
39
|
-
"""Import and return the db_context function."""
|
|
40
|
-
if not self.db_context:
|
|
41
|
-
raise typer.BadParameter(
|
|
42
|
-
"No db_context configured. "
|
|
43
|
-
"Add 'db_context' to [tool.fastapi-toolsets] in pyproject.toml."
|
|
44
|
-
)
|
|
45
|
-
return _import_from_string(self.db_context)
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
def _import_from_string(import_path: str):
|
|
49
|
-
"""Import an object from a string path like 'module.submodule:attribute'."""
|
|
23
|
+
Raises:
|
|
24
|
+
typer.BadParameter: If the import path is invalid or import fails.
|
|
25
|
+
"""
|
|
50
26
|
if ":" not in import_path:
|
|
51
27
|
raise typer.BadParameter(
|
|
52
28
|
f"Invalid import path '{import_path}'. Expected format: 'module:attribute'"
|
|
@@ -54,10 +30,7 @@ def _import_from_string(import_path: str):
|
|
|
54
30
|
|
|
55
31
|
module_path, attr_name = import_path.rsplit(":", 1)
|
|
56
32
|
|
|
57
|
-
|
|
58
|
-
cwd = str(Path.cwd())
|
|
59
|
-
if cwd not in sys.path:
|
|
60
|
-
sys.path.insert(0, cwd)
|
|
33
|
+
_ensure_project_in_path()
|
|
61
34
|
|
|
62
35
|
try:
|
|
63
36
|
module = importlib.import_module(module_path)
|
|
@@ -72,21 +45,63 @@ def _import_from_string(import_path: str):
|
|
|
72
45
|
return getattr(module, attr_name)
|
|
73
46
|
|
|
74
47
|
|
|
75
|
-
def
|
|
76
|
-
"""
|
|
77
|
-
pyproject_path = Path.cwd() / "pyproject.toml"
|
|
48
|
+
def get_config_value(key: str, required: bool = False):
|
|
49
|
+
"""Get a configuration value from pyproject.toml.
|
|
78
50
|
|
|
79
|
-
|
|
80
|
-
|
|
51
|
+
Args:
|
|
52
|
+
key: The configuration key in [tool.fastapi-toolsets].
|
|
53
|
+
required: If True, raises an error when the key is missing.
|
|
81
54
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
55
|
+
Returns:
|
|
56
|
+
The configuration value, or None if not found and not required.
|
|
57
|
+
|
|
58
|
+
Raises:
|
|
59
|
+
typer.BadParameter: If required=True and the key is missing.
|
|
60
|
+
"""
|
|
61
|
+
config = load_pyproject()
|
|
62
|
+
value = config.get(key)
|
|
85
63
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
64
|
+
if required and value is None:
|
|
65
|
+
raise typer.BadParameter(
|
|
66
|
+
f"No '{key}' configured. "
|
|
67
|
+
f"Add '{key}' to [tool.fastapi-toolsets] in pyproject.toml."
|
|
90
68
|
)
|
|
91
|
-
|
|
92
|
-
|
|
69
|
+
|
|
70
|
+
return value
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def get_fixtures_registry():
|
|
74
|
+
"""Import and return the fixtures registry from config."""
|
|
75
|
+
from ..fixtures import FixtureRegistry
|
|
76
|
+
|
|
77
|
+
import_path = get_config_value("fixtures", required=True)
|
|
78
|
+
registry = import_from_string(import_path)
|
|
79
|
+
|
|
80
|
+
if not isinstance(registry, FixtureRegistry):
|
|
81
|
+
raise typer.BadParameter(
|
|
82
|
+
f"'fixtures' must be a FixtureRegistry instance, got {type(registry).__name__}"
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
return registry
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def get_db_context():
|
|
89
|
+
"""Import and return the db_context function from config."""
|
|
90
|
+
import_path = get_config_value("db_context", required=True)
|
|
91
|
+
return import_from_string(import_path)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def get_custom_cli() -> typer.Typer | None:
|
|
95
|
+
"""Import and return the custom CLI Typer instance from config."""
|
|
96
|
+
import_path = get_config_value("custom_cli")
|
|
97
|
+
if not import_path:
|
|
98
|
+
return None
|
|
99
|
+
|
|
100
|
+
custom = import_from_string(import_path)
|
|
101
|
+
|
|
102
|
+
if not isinstance(custom, typer.Typer):
|
|
103
|
+
raise typer.BadParameter(
|
|
104
|
+
f"'custom_cli' must be a Typer instance, got {type(custom).__name__}"
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
return custom
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""Pyproject.toml discovery and loading."""
|
|
2
|
+
|
|
3
|
+
import tomllib
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
TOOL_NAME = "fastapi-toolsets"
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def find_pyproject(start_path: Path | None = None) -> Path | None:
|
|
10
|
+
"""Find pyproject.toml by walking up the directory tree.
|
|
11
|
+
|
|
12
|
+
Similar to how pytest, black, and ruff discover their config files.
|
|
13
|
+
"""
|
|
14
|
+
path = (start_path or Path.cwd()).resolve()
|
|
15
|
+
|
|
16
|
+
for directory in [path, *path.parents]:
|
|
17
|
+
pyproject = directory / "pyproject.toml"
|
|
18
|
+
if pyproject.is_file():
|
|
19
|
+
return pyproject
|
|
20
|
+
|
|
21
|
+
return None
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def load_pyproject(path: Path | None = None) -> dict:
|
|
25
|
+
"""Load tool configuration from pyproject.toml.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
path: Explicit path to pyproject.toml. If None, searches up from cwd.
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
The [tool.fastapi-toolsets] section as a dict, or empty dict if not found.
|
|
32
|
+
"""
|
|
33
|
+
pyproject_path = path or find_pyproject()
|
|
34
|
+
|
|
35
|
+
if not pyproject_path:
|
|
36
|
+
return {}
|
|
37
|
+
|
|
38
|
+
try:
|
|
39
|
+
with open(pyproject_path, "rb") as f:
|
|
40
|
+
data = tomllib.load(f)
|
|
41
|
+
return data.get("tool", {}).get(TOOL_NAME, {})
|
|
42
|
+
except (OSError, tomllib.TOMLDecodeError):
|
|
43
|
+
return {}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"""Dependency factories for FastAPI routes."""
|
|
2
|
+
|
|
3
|
+
import inspect
|
|
4
|
+
from collections.abc import AsyncGenerator, Callable
|
|
5
|
+
from typing import Any, TypeVar, cast
|
|
6
|
+
|
|
7
|
+
from fastapi import Depends
|
|
8
|
+
from sqlalchemy.ext.asyncio import AsyncSession
|
|
9
|
+
from sqlalchemy.orm import DeclarativeBase
|
|
10
|
+
|
|
11
|
+
from ..crud import CrudFactory
|
|
12
|
+
|
|
13
|
+
ModelType = TypeVar("ModelType", bound=DeclarativeBase)
|
|
14
|
+
SessionDependency = Callable[[], AsyncGenerator[AsyncSession, None]]
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def PathDependency(
|
|
18
|
+
model: type[ModelType],
|
|
19
|
+
field: Any,
|
|
20
|
+
*,
|
|
21
|
+
session_dep: SessionDependency,
|
|
22
|
+
param_name: str | None = None,
|
|
23
|
+
) -> ModelType:
|
|
24
|
+
"""Create a dependency that fetches a DB object from a path parameter.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
model: SQLAlchemy model class
|
|
28
|
+
field: Model field to filter by (e.g., User.id)
|
|
29
|
+
session_dep: Session dependency function (e.g., get_db)
|
|
30
|
+
param_name: Path parameter name (defaults to model_field, e.g., user_id)
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
A Depends() instance that resolves to the model instance
|
|
34
|
+
|
|
35
|
+
Raises:
|
|
36
|
+
NotFoundError: If no matching record is found
|
|
37
|
+
|
|
38
|
+
Example:
|
|
39
|
+
UserDep = PathDependency(User, User.id, session_dep=get_db)
|
|
40
|
+
|
|
41
|
+
@router.get("/user/{id}")
|
|
42
|
+
async def get(
|
|
43
|
+
user: User = UserDep,
|
|
44
|
+
): ...
|
|
45
|
+
"""
|
|
46
|
+
crud = CrudFactory(model)
|
|
47
|
+
name = (
|
|
48
|
+
param_name
|
|
49
|
+
if param_name is not None
|
|
50
|
+
else "{}_{}".format(model.__name__.lower(), field.key)
|
|
51
|
+
)
|
|
52
|
+
python_type = field.type.python_type
|
|
53
|
+
|
|
54
|
+
async def dependency(
|
|
55
|
+
session: AsyncSession = Depends(session_dep), **kwargs: Any
|
|
56
|
+
) -> ModelType:
|
|
57
|
+
value = kwargs[name]
|
|
58
|
+
return await crud.get(session, filters=[field == value])
|
|
59
|
+
|
|
60
|
+
setattr(
|
|
61
|
+
dependency,
|
|
62
|
+
"__signature__",
|
|
63
|
+
inspect.Signature(
|
|
64
|
+
parameters=[
|
|
65
|
+
inspect.Parameter(
|
|
66
|
+
name, inspect.Parameter.KEYWORD_ONLY, annotation=python_type
|
|
67
|
+
),
|
|
68
|
+
inspect.Parameter(
|
|
69
|
+
"session",
|
|
70
|
+
inspect.Parameter.KEYWORD_ONLY,
|
|
71
|
+
annotation=AsyncSession,
|
|
72
|
+
default=Depends(session_dep),
|
|
73
|
+
),
|
|
74
|
+
]
|
|
75
|
+
),
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
return cast(ModelType, Depends(cast(Callable[..., ModelType], dependency)))
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def BodyDependency(
|
|
82
|
+
model: type[ModelType],
|
|
83
|
+
field: Any,
|
|
84
|
+
*,
|
|
85
|
+
session_dep: SessionDependency,
|
|
86
|
+
body_field: str,
|
|
87
|
+
) -> ModelType:
|
|
88
|
+
"""Create a dependency that fetches a DB object from a body field.
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
model: SQLAlchemy model class
|
|
92
|
+
field: Model field to filter by (e.g., User.id)
|
|
93
|
+
session_dep: Session dependency function (e.g., get_db)
|
|
94
|
+
body_field: Name of the field in the request body
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
A Depends() instance that resolves to the model instance
|
|
98
|
+
|
|
99
|
+
Raises:
|
|
100
|
+
NotFoundError: If no matching record is found
|
|
101
|
+
|
|
102
|
+
Example:
|
|
103
|
+
UserDep = BodyDependency(
|
|
104
|
+
User, User.ctfd_id, session_dep=get_db, body_field="user_id"
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
@router.post("/assign")
|
|
108
|
+
async def assign(
|
|
109
|
+
user: User = UserDep,
|
|
110
|
+
): ...
|
|
111
|
+
"""
|
|
112
|
+
crud = CrudFactory(model)
|
|
113
|
+
python_type = field.type.python_type
|
|
114
|
+
|
|
115
|
+
async def dependency(
|
|
116
|
+
session: AsyncSession = Depends(session_dep), **kwargs: Any
|
|
117
|
+
) -> ModelType:
|
|
118
|
+
value = kwargs[body_field]
|
|
119
|
+
return await crud.get(session, filters=[field == value])
|
|
120
|
+
|
|
121
|
+
setattr(
|
|
122
|
+
dependency,
|
|
123
|
+
"__signature__",
|
|
124
|
+
inspect.Signature(
|
|
125
|
+
parameters=[
|
|
126
|
+
inspect.Parameter(
|
|
127
|
+
body_field, inspect.Parameter.KEYWORD_ONLY, annotation=python_type
|
|
128
|
+
),
|
|
129
|
+
inspect.Parameter(
|
|
130
|
+
"session",
|
|
131
|
+
inspect.Parameter.KEYWORD_ONLY,
|
|
132
|
+
annotation=AsyncSession,
|
|
133
|
+
default=Depends(session_dep),
|
|
134
|
+
),
|
|
135
|
+
]
|
|
136
|
+
),
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
return cast(ModelType, Depends(cast(Callable[..., ModelType], dependency)))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fastapi-toolsets
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.0
|
|
4
4
|
Summary: Reusable tools for FastAPI: async CRUD, fixtures, CLI, and standardized responses for SQLAlchemy + PostgreSQL
|
|
5
5
|
Keywords: fastapi,sqlalchemy,postgresql
|
|
6
6
|
Author: d3vyce
|
|
@@ -77,7 +77,7 @@ uv add fastapi-toolsets
|
|
|
77
77
|
|
|
78
78
|
- **CRUD**: Generic async CRUD operations with `CrudFactory`
|
|
79
79
|
- **Fixtures**: Fixture system with dependency management, context support and pytest integration
|
|
80
|
-
- **CLI**: Django-like command-line interface
|
|
80
|
+
- **CLI**: Django-like command-line interface with fixture management and custom commands support
|
|
81
81
|
- **Standardized API Responses**: Consistent response format across your API
|
|
82
82
|
- **Exception Handling**: Structured error responses with automatic OpenAPI documentation
|
|
83
83
|
|
|
@@ -1,14 +1,17 @@
|
|
|
1
|
-
fastapi_toolsets/__init__.py,sha256=
|
|
2
|
-
fastapi_toolsets/cli/__init__.py,sha256=
|
|
3
|
-
fastapi_toolsets/cli/app.py,sha256=
|
|
1
|
+
fastapi_toolsets/__init__.py,sha256=9FN3OLnVkKUsOTCa5C14L7b867Y0vSZZXUcDdvsItqY,820
|
|
2
|
+
fastapi_toolsets/cli/__init__.py,sha256=tlmq_tIoqwWou2dk6Ur4fQUtb0Rl3MglTJEVHyUgfz4,95
|
|
3
|
+
fastapi_toolsets/cli/app.py,sha256=LcY38gRW1yIylOVre82IHCPH4Wcg6I5Z1LoeR71vgCk,700
|
|
4
4
|
fastapi_toolsets/cli/commands/__init__.py,sha256=BogehmsY6olwLdfIBzviuppXP1LLl9znnxtmji3eLwI,29
|
|
5
|
-
fastapi_toolsets/cli/commands/fixtures.py,sha256=
|
|
6
|
-
fastapi_toolsets/cli/config.py,sha256=
|
|
5
|
+
fastapi_toolsets/cli/commands/fixtures.py,sha256=3AqlTU3qyyxsOE7XGsDMHlsQ7QNYikLM58JTjHJQugo,2827
|
|
6
|
+
fastapi_toolsets/cli/config.py,sha256=ZFnmGzQOePiAVKSwgHcF_wnvCWSba119fVjc6mJZgXs,3060
|
|
7
|
+
fastapi_toolsets/cli/pyproject.py,sha256=9OKDjZvMEGfpiBq3dgtMZc0IemPisXme8h77-a6o1jA,1153
|
|
7
8
|
fastapi_toolsets/cli/utils.py,sha256=KsW-FW9tBCAjUd_qludXa1jy8ch8TpkT-_ltcO0DWVs,733
|
|
8
9
|
fastapi_toolsets/crud/__init__.py,sha256=XSl_2k5qfjr0ZFtS7JgNtiNJmZjYL8bDh9BE5qrDyw0,325
|
|
9
10
|
fastapi_toolsets/crud/factory.py,sha256=wBtmFNC7cqzeZfyJLKW-lj3Dd0zo15bZfeTNA1DvqNE,16663
|
|
10
11
|
fastapi_toolsets/crud/search.py,sha256=OAhbkuDan-elAWyAUz0msieZxYkE76fpJYwaA1BRTQ8,4635
|
|
11
12
|
fastapi_toolsets/db.py,sha256=YUj5CrxCnREg7AqpJLNrLR2RDIOCS7stQCNOSS3cRho,5619
|
|
13
|
+
fastapi_toolsets/dependencies/__init__.py,sha256=jw_EZDuuwNPTS3MKhf66Z24Kd53r76RKbHzhl-_UVhI,158
|
|
14
|
+
fastapi_toolsets/dependencies/factory.py,sha256=WL38Xc2BIeXXSlpuhHzy2gHS-rNFcUzjidE78SoItgk,3984
|
|
12
15
|
fastapi_toolsets/exceptions/__init__.py,sha256=vNP19B7aaVKnBpRXG_fdCZ0P_GlPDiZDYydPLxfdhD0,481
|
|
13
16
|
fastapi_toolsets/exceptions/exceptions.py,sha256=hu8_lvE9KmnYID9YgJqlzZMkCD0kASPrGAmN1hUe2bY,5086
|
|
14
17
|
fastapi_toolsets/exceptions/handler.py,sha256=IXfKiIr_LPo-11PRpOIrNRAXBkeQ5TdLcu3Gy-r6ChU,5916
|
|
@@ -21,8 +24,8 @@ fastapi_toolsets/pytest/__init__.py,sha256=0GnnFxWNfpaShBBYsfRGIgSygwC1eo8TydmXC
|
|
|
21
24
|
fastapi_toolsets/pytest/plugin.py,sha256=lbEiumS2zi7jARY6eYBUPAlfKCplbLFrZXcmp0-RkcA,6892
|
|
22
25
|
fastapi_toolsets/pytest/utils.py,sha256=VqkxtbpEU8w7-0xfcZG0m8Tpn3LtdnvAJMyqWS7WtIw,3447
|
|
23
26
|
fastapi_toolsets/schemas.py,sha256=LBzrq4s5VWYeQqlUfOEvWDtpFdO8scgY0LRypk9KUAE,2639
|
|
24
|
-
fastapi_toolsets-0.
|
|
25
|
-
fastapi_toolsets-0.
|
|
26
|
-
fastapi_toolsets-0.
|
|
27
|
-
fastapi_toolsets-0.
|
|
28
|
-
fastapi_toolsets-0.
|
|
27
|
+
fastapi_toolsets-0.6.0.dist-info/licenses/LICENSE,sha256=V2jCjI-VPB-veGY2Ktb0sU4vT_TldRciZ9lCE98bMoE,1063
|
|
28
|
+
fastapi_toolsets-0.6.0.dist-info/WHEEL,sha256=5DEXXimM34_d4Gx1AuF9ysMr1_maoEtGKjaILM3s4w4,80
|
|
29
|
+
fastapi_toolsets-0.6.0.dist-info/entry_points.txt,sha256=I-wL3xlhglq38WG6WDitgmE4Hstk9tmSUf4eIjz9ckk,54
|
|
30
|
+
fastapi_toolsets-0.6.0.dist-info/METADATA,sha256=sOE5365KN7O-ZsasgNDoWO9kj_pBu-RfuwkNlmlDWgs,4240
|
|
31
|
+
fastapi_toolsets-0.6.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|