vega-framework 0.1.19__tar.gz → 0.1.22__tar.gz
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.
- {vega_framework-0.1.19 → vega_framework-0.1.22}/PKG-INFO +1 -1
- {vega_framework-0.1.19 → vega_framework-0.1.22}/pyproject.toml +7 -2
- {vega_framework-0.1.19 → vega_framework-0.1.22}/vega/cli/commands/generate.py +10 -11
- {vega_framework-0.1.19 → vega_framework-0.1.22}/vega/cli/commands/init.py +9 -1
- {vega_framework-0.1.19 → vega_framework-0.1.22}/vega/cli/scaffolds/fastapi.py +2 -2
- {vega_framework-0.1.19 → vega_framework-0.1.22}/vega/cli/templates/__init__.py +4 -0
- vega_framework-0.1.22/vega/cli/templates/cli/commands_init.py.j2 +12 -0
- {vega_framework-0.1.19 → vega_framework-0.1.22}/vega/cli/templates/components.py +10 -0
- {vega_framework-0.1.19 → vega_framework-0.1.22}/vega/cli/templates/project/main.py.j2 +16 -2
- {vega_framework-0.1.19 → vega_framework-0.1.22}/vega/cli/templates/project/main_fastapi.py.j2 +8 -1
- {vega_framework-0.1.19 → vega_framework-0.1.22}/vega/cli/templates/project/main_standard.py.j2 +8 -1
- vega_framework-0.1.22/vega/cli/templates/web/routes_init_autodiscovery.py.j2 +12 -0
- vega_framework-0.1.22/vega/discovery/__init__.py +5 -0
- vega_framework-0.1.22/vega/discovery/commands.py +104 -0
- vega_framework-0.1.22/vega/discovery/routes.py +129 -0
- {vega_framework-0.1.19 → vega_framework-0.1.22}/LICENSE +0 -0
- {vega_framework-0.1.19 → vega_framework-0.1.22}/README.md +0 -0
- {vega_framework-0.1.19 → vega_framework-0.1.22}/vega/__init__.py +0 -0
- {vega_framework-0.1.19 → vega_framework-0.1.22}/vega/cli/__init__.py +0 -0
- {vega_framework-0.1.19 → vega_framework-0.1.22}/vega/cli/commands/__init__.py +0 -0
- {vega_framework-0.1.19 → vega_framework-0.1.22}/vega/cli/commands/add.py +0 -0
- {vega_framework-0.1.19 → vega_framework-0.1.22}/vega/cli/commands/migrate.py +0 -0
- {vega_framework-0.1.19 → vega_framework-0.1.22}/vega/cli/commands/update.py +0 -0
- {vega_framework-0.1.19 → vega_framework-0.1.22}/vega/cli/main.py +0 -0
- {vega_framework-0.1.19 → vega_framework-0.1.22}/vega/cli/scaffolds/__init__.py +0 -0
- {vega_framework-0.1.19 → vega_framework-0.1.22}/vega/cli/scaffolds/sqlalchemy.py +0 -0
- {vega_framework-0.1.19 → vega_framework-0.1.22}/vega/cli/templates/cli/command.py.j2 +0 -0
- {vega_framework-0.1.19 → vega_framework-0.1.22}/vega/cli/templates/cli/command_simple.py.j2 +0 -0
- {vega_framework-0.1.19 → vega_framework-0.1.22}/vega/cli/templates/domain/entity.py.j2 +0 -0
- {vega_framework-0.1.19 → vega_framework-0.1.22}/vega/cli/templates/domain/interactor.py.j2 +0 -0
- {vega_framework-0.1.19 → vega_framework-0.1.22}/vega/cli/templates/domain/mediator.py.j2 +0 -0
- {vega_framework-0.1.19 → vega_framework-0.1.22}/vega/cli/templates/domain/repository_interface.py.j2 +0 -0
- {vega_framework-0.1.19 → vega_framework-0.1.22}/vega/cli/templates/domain/service_interface.py.j2 +0 -0
- {vega_framework-0.1.19 → vega_framework-0.1.22}/vega/cli/templates/infrastructure/model.py.j2 +0 -0
- {vega_framework-0.1.19 → vega_framework-0.1.22}/vega/cli/templates/infrastructure/repository_impl.py.j2 +0 -0
- {vega_framework-0.1.19 → vega_framework-0.1.22}/vega/cli/templates/infrastructure/service_impl.py.j2 +0 -0
- {vega_framework-0.1.19 → vega_framework-0.1.22}/vega/cli/templates/loader.py +0 -0
- {vega_framework-0.1.19 → vega_framework-0.1.22}/vega/cli/templates/project/.env.example +0 -0
- {vega_framework-0.1.19 → vega_framework-0.1.22}/vega/cli/templates/project/.gitignore +0 -0
- {vega_framework-0.1.19 → vega_framework-0.1.22}/vega/cli/templates/project/ARCHITECTURE.md.j2 +0 -0
- {vega_framework-0.1.19 → vega_framework-0.1.22}/vega/cli/templates/project/README.md.j2 +0 -0
- {vega_framework-0.1.19 → vega_framework-0.1.22}/vega/cli/templates/project/config.py.j2 +0 -0
- {vega_framework-0.1.19 → vega_framework-0.1.22}/vega/cli/templates/project/pyproject.toml.j2 +0 -0
- {vega_framework-0.1.19 → vega_framework-0.1.22}/vega/cli/templates/project/settings.py.j2 +0 -0
- {vega_framework-0.1.19 → vega_framework-0.1.22}/vega/cli/templates/sqlalchemy/alembic.ini.j2 +0 -0
- {vega_framework-0.1.19 → vega_framework-0.1.22}/vega/cli/templates/sqlalchemy/database_manager.py.j2 +0 -0
- {vega_framework-0.1.19 → vega_framework-0.1.22}/vega/cli/templates/sqlalchemy/env.py.j2 +0 -0
- {vega_framework-0.1.19 → vega_framework-0.1.22}/vega/cli/templates/sqlalchemy/script.py.mako +0 -0
- {vega_framework-0.1.19 → vega_framework-0.1.22}/vega/cli/templates/web/__init__.py.j2 +0 -0
- {vega_framework-0.1.19 → vega_framework-0.1.22}/vega/cli/templates/web/app.py.j2 +0 -0
- {vega_framework-0.1.19 → vega_framework-0.1.22}/vega/cli/templates/web/health_route.py.j2 +0 -0
- {vega_framework-0.1.19 → vega_framework-0.1.22}/vega/cli/templates/web/main.py.j2 +0 -0
- {vega_framework-0.1.19 → vega_framework-0.1.22}/vega/cli/templates/web/middleware.py.j2 +0 -0
- {vega_framework-0.1.19 → vega_framework-0.1.22}/vega/cli/templates/web/models_init.py.j2 +0 -0
- {vega_framework-0.1.19 → vega_framework-0.1.22}/vega/cli/templates/web/router.py.j2 +0 -0
- {vega_framework-0.1.19 → vega_framework-0.1.22}/vega/cli/templates/web/routes_init.py.j2 +0 -0
- {vega_framework-0.1.19 → vega_framework-0.1.22}/vega/cli/templates/web/user_models.py.j2 +0 -0
- {vega_framework-0.1.19 → vega_framework-0.1.22}/vega/cli/templates/web/users_route.py.j2 +0 -0
- {vega_framework-0.1.19 → vega_framework-0.1.22}/vega/cli/utils/__init__.py +0 -0
- {vega_framework-0.1.19 → vega_framework-0.1.22}/vega/cli/utils/async_support.py +0 -0
- {vega_framework-0.1.19 → vega_framework-0.1.22}/vega/cli/utils/messages.py +0 -0
- {vega_framework-0.1.19 → vega_framework-0.1.22}/vega/cli/utils/naming.py +0 -0
- {vega_framework-0.1.19 → vega_framework-0.1.22}/vega/cli/utils/validators.py +0 -0
- {vega_framework-0.1.19 → vega_framework-0.1.22}/vega/di/__init__.py +0 -0
- {vega_framework-0.1.19 → vega_framework-0.1.22}/vega/di/container.py +0 -0
- {vega_framework-0.1.19 → vega_framework-0.1.22}/vega/di/decorators.py +0 -0
- {vega_framework-0.1.19 → vega_framework-0.1.22}/vega/di/errors.py +0 -0
- {vega_framework-0.1.19 → vega_framework-0.1.22}/vega/di/scope.py +0 -0
- {vega_framework-0.1.19 → vega_framework-0.1.22}/vega/patterns/__init__.py +0 -0
- {vega_framework-0.1.19 → vega_framework-0.1.22}/vega/patterns/interactor.py +0 -0
- {vega_framework-0.1.19 → vega_framework-0.1.22}/vega/patterns/mediator.py +0 -0
- {vega_framework-0.1.19 → vega_framework-0.1.22}/vega/patterns/repository.py +0 -0
- {vega_framework-0.1.19 → vega_framework-0.1.22}/vega/patterns/service.py +0 -0
- {vega_framework-0.1.19 → vega_framework-0.1.22}/vega/settings/__init__.py +0 -0
- {vega_framework-0.1.19 → vega_framework-0.1.22}/vega/settings/base.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
[tool.poetry]
|
2
2
|
name = "vega-framework"
|
3
|
-
version = "0.1.
|
3
|
+
version = "0.1.22"
|
4
4
|
description = "Enterprise-ready Python framework that enforces Clean Architecture for building maintainable and scalable applications."
|
5
5
|
authors = ["Roberto Ferro"]
|
6
6
|
license = "MIT"
|
@@ -28,7 +28,12 @@ classifiers = [
|
|
28
28
|
"Topic :: Software Development :: Libraries :: Python Modules",
|
29
29
|
]
|
30
30
|
packages = [{ include = "vega", from = "." }]
|
31
|
-
include = [
|
31
|
+
include = [
|
32
|
+
"vega/cli/templates/project/*",
|
33
|
+
"vega/cli/templates/web/*",
|
34
|
+
"vega/cli/templates/cli/*",
|
35
|
+
"vega/discovery/*",
|
36
|
+
]
|
32
37
|
|
33
38
|
[tool.poetry.dependencies]
|
34
39
|
python = "^3.10"
|
@@ -424,10 +424,11 @@ def _generate_router(project_root: Path, project_name: str, name: str) -> None:
|
|
424
424
|
routes_path = web_path / "routes"
|
425
425
|
routes_path.mkdir(exist_ok=True)
|
426
426
|
|
427
|
-
# Check if __init__.py exists
|
427
|
+
# Check if __init__.py exists, create with auto-discovery if not
|
428
428
|
init_file = routes_path / "__init__.py"
|
429
429
|
if not init_file.exists():
|
430
|
-
|
430
|
+
from vega.cli.templates import render_fastapi_routes_init_autodiscovery
|
431
|
+
init_file.write_text(render_fastapi_routes_init_autodiscovery())
|
431
432
|
click.echo(f"+ Created {click.style(str(init_file.relative_to(project_root)), fg='green')}")
|
432
433
|
|
433
434
|
# Generate router file
|
@@ -442,14 +443,12 @@ def _generate_router(project_root: Path, project_name: str, name: str) -> None:
|
|
442
443
|
|
443
444
|
click.echo(f"+ Created {click.style(str(router_file.relative_to(project_root)), fg='green')}")
|
444
445
|
|
445
|
-
# Register the router in routes/__init__.py
|
446
|
-
_register_router_in_init(project_root, resource_file, resource_name)
|
447
|
-
|
448
446
|
# Instructions for next steps
|
449
447
|
click.echo(f"\nNext steps:")
|
450
448
|
click.echo(f" 1. Create Pydantic models in presentation/web/models/{resource_file}_models.py")
|
451
449
|
click.echo(f" 2. Implement domain interactors for {resource_name} operations")
|
452
450
|
click.echo(f" 3. Replace in-memory storage with actual use cases")
|
451
|
+
click.echo(click.style(f" (Router auto-discovered from web/routes/)", fg='bright_black'))
|
453
452
|
|
454
453
|
|
455
454
|
def _generate_middleware(project_root: Path, project_name: str, class_name: str, file_name: str) -> None:
|
@@ -688,11 +687,12 @@ def _generate_command(project_root: Path, project_name: str, name: str, is_async
|
|
688
687
|
# Create commands directory if it doesn't exist
|
689
688
|
commands_path = cli_path / "commands"
|
690
689
|
commands_path.mkdir(exist_ok=True)
|
691
|
-
|
692
|
-
# Check if __init__.py exists
|
690
|
+
|
691
|
+
# Check if __init__.py exists, create with auto-discovery if not
|
693
692
|
init_file = commands_path / "__init__.py"
|
694
693
|
if not init_file.exists():
|
695
|
-
|
694
|
+
from vega.cli.templates import render_cli_commands_init
|
695
|
+
init_file.write_text(render_cli_commands_init())
|
696
696
|
click.echo(f"+ Created {click.style(str(init_file.relative_to(project_root)), fg='green')}")
|
697
697
|
|
698
698
|
# Convert name to snake_case for command and file
|
@@ -803,8 +803,7 @@ def _generate_command(project_root: Path, project_name: str, name: str, is_async
|
|
803
803
|
# Instructions for next steps
|
804
804
|
click.echo(f"\nNext steps:")
|
805
805
|
click.echo(f" 1. Implement your command logic in {command_file.relative_to(project_root)}")
|
806
|
-
click.echo(f" 2.
|
807
|
-
click.echo(click.style(f" from
|
808
|
-
click.echo(click.style(f" cli.add_command({file_name})", fg='cyan'))
|
806
|
+
click.echo(f" 2. Run your command: python main.py {command_name}")
|
807
|
+
click.echo(click.style(f" (Commands are auto-discovered from cli/commands/)", fg='bright_black'))
|
809
808
|
if with_interactor:
|
810
809
|
click.echo(f" 3. Create interactor: vega generate interactor {interactor_name}")
|
@@ -47,7 +47,15 @@ def init_project(project_name: str, template: str, parent_path: str):
|
|
47
47
|
for directory in directories:
|
48
48
|
dir_path = project_path / directory
|
49
49
|
dir_path.mkdir(parents=True, exist_ok=True)
|
50
|
-
|
50
|
+
|
51
|
+
# Use auto-discovery template for cli/commands
|
52
|
+
if "cli" in directory and "commands" in directory:
|
53
|
+
from vega.cli.templates import render_cli_commands_init
|
54
|
+
content = render_cli_commands_init()
|
55
|
+
(dir_path / "__init__.py").write_text(content)
|
56
|
+
else:
|
57
|
+
(dir_path / "__init__.py").write_text("")
|
58
|
+
|
51
59
|
click.echo(f" + Created {directory}/")
|
52
60
|
|
53
61
|
# Create __init__.py files
|
@@ -9,7 +9,7 @@ from vega.cli.templates import (
|
|
9
9
|
render_fastapi_app,
|
10
10
|
render_fastapi_health_route,
|
11
11
|
render_fastapi_main,
|
12
|
-
|
12
|
+
render_fastapi_routes_init_autodiscovery,
|
13
13
|
render_fastapi_user_route,
|
14
14
|
render_pydantic_models_init,
|
15
15
|
render_pydantic_user_models,
|
@@ -37,7 +37,7 @@ def create_fastapi_scaffold(
|
|
37
37
|
(web_dir / "__init__.py", render_web_package_init()),
|
38
38
|
(web_dir / "app.py", render_fastapi_app(project_name)),
|
39
39
|
(web_dir / "main.py", render_fastapi_main(project_name)),
|
40
|
-
(routes_dir / "__init__.py",
|
40
|
+
(routes_dir / "__init__.py", render_fastapi_routes_init_autodiscovery()),
|
41
41
|
(routes_dir / "health.py", render_fastapi_health_route()),
|
42
42
|
(routes_dir / "users.py", render_fastapi_user_route()),
|
43
43
|
(models_dir / "__init__.py", render_pydantic_models_init()),
|
@@ -26,6 +26,8 @@ from .components import (
|
|
26
26
|
render_sqlalchemy_model,
|
27
27
|
render_cli_command,
|
28
28
|
render_cli_command_simple,
|
29
|
+
render_cli_commands_init,
|
30
|
+
render_fastapi_routes_init_autodiscovery,
|
29
31
|
)
|
30
32
|
from .loader import render_template
|
31
33
|
|
@@ -57,5 +59,7 @@ __all__ = [
|
|
57
59
|
"render_sqlalchemy_model",
|
58
60
|
"render_cli_command",
|
59
61
|
"render_cli_command_simple",
|
62
|
+
"render_cli_commands_init",
|
63
|
+
"render_fastapi_routes_init_autodiscovery",
|
60
64
|
"render_template",
|
61
65
|
]
|
@@ -0,0 +1,12 @@
|
|
1
|
+
"""Custom CLI commands auto-discovery"""
|
2
|
+
from vega.discovery import discover_commands
|
3
|
+
|
4
|
+
|
5
|
+
def get_commands():
|
6
|
+
"""
|
7
|
+
Auto-discover Click commands in this directory.
|
8
|
+
|
9
|
+
Returns:
|
10
|
+
list: List of Click command objects found in this directory
|
11
|
+
"""
|
12
|
+
return discover_commands(__package__)
|
@@ -237,3 +237,13 @@ def render_cli_command_simple(
|
|
237
237
|
params_signature=params_signature,
|
238
238
|
params_list=params_list,
|
239
239
|
)
|
240
|
+
|
241
|
+
|
242
|
+
def render_cli_commands_init() -> str:
|
243
|
+
"""Return the template for cli/commands/__init__.py with auto-discovery"""
|
244
|
+
return render_template("commands_init.py.j2", subfolder="cli")
|
245
|
+
|
246
|
+
|
247
|
+
def render_fastapi_routes_init_autodiscovery() -> str:
|
248
|
+
"""Return the template for web/routes/__init__.py with auto-discovery"""
|
249
|
+
return render_template("routes_init_autodiscovery.py.j2", subfolder="web")
|
@@ -35,7 +35,14 @@ def hello():
|
|
35
35
|
click.echo("Add your CLI commands in presentation/cli/commands/")
|
36
36
|
|
37
37
|
|
38
|
-
#
|
38
|
+
# Auto-register custom commands from presentation/cli/commands/
|
39
|
+
try:
|
40
|
+
from presentation.cli.commands import get_commands
|
41
|
+
for cmd in get_commands():
|
42
|
+
cli.add_command(cmd)
|
43
|
+
except ImportError:
|
44
|
+
# cli/commands/ not created yet
|
45
|
+
pass
|
39
46
|
|
40
47
|
|
41
48
|
if __name__ == "__main__":
|
@@ -88,7 +95,14 @@ def greet(name: str):
|
|
88
95
|
# )
|
89
96
|
|
90
97
|
|
91
|
-
#
|
98
|
+
# Auto-register custom commands from presentation/cli/commands/
|
99
|
+
try:
|
100
|
+
from presentation.cli.commands import get_commands
|
101
|
+
for cmd in get_commands():
|
102
|
+
cli.add_command(cmd)
|
103
|
+
except ImportError:
|
104
|
+
# cli/commands/ not created yet
|
105
|
+
pass
|
92
106
|
|
93
107
|
|
94
108
|
if __name__ == "__main__":
|
{vega_framework-0.1.19 → vega_framework-0.1.22}/vega/cli/templates/project/main_fastapi.py.j2
RENAMED
@@ -62,7 +62,14 @@ async def create_user(name: str, email: str):
|
|
62
62
|
click.echo("Note: Replace this with your actual CreateUser interactor")
|
63
63
|
|
64
64
|
|
65
|
-
#
|
65
|
+
# Auto-register custom commands from presentation/cli/commands/
|
66
|
+
try:
|
67
|
+
from presentation.cli.commands import get_commands
|
68
|
+
for cmd in get_commands():
|
69
|
+
cli.add_command(cmd)
|
70
|
+
except ImportError:
|
71
|
+
# cli/commands/ not created yet
|
72
|
+
pass
|
66
73
|
|
67
74
|
|
68
75
|
if __name__ == "__main__":
|
{vega_framework-0.1.19 → vega_framework-0.1.22}/vega/cli/templates/project/main_standard.py.j2
RENAMED
@@ -70,7 +70,14 @@ async def create_user(name: str, email: str):
|
|
70
70
|
# )
|
71
71
|
|
72
72
|
|
73
|
-
#
|
73
|
+
# Auto-register custom commands from presentation/cli/commands/
|
74
|
+
try:
|
75
|
+
from presentation.cli.commands import get_commands
|
76
|
+
for cmd in get_commands():
|
77
|
+
cli.add_command(cmd)
|
78
|
+
except ImportError:
|
79
|
+
# cli/commands/ not created yet
|
80
|
+
pass
|
74
81
|
|
75
82
|
|
76
83
|
if __name__ == "__main__":
|
@@ -0,0 +1,12 @@
|
|
1
|
+
"""API routers auto-discovery"""
|
2
|
+
from vega.discovery import discover_routers
|
3
|
+
|
4
|
+
|
5
|
+
def get_api_router():
|
6
|
+
"""
|
7
|
+
Auto-discover and register FastAPI routers in this directory.
|
8
|
+
|
9
|
+
Returns:
|
10
|
+
APIRouter: Main API router with all discovered routers included
|
11
|
+
"""
|
12
|
+
return discover_routers(__package__)
|
@@ -0,0 +1,104 @@
|
|
1
|
+
"""Click CLI commands auto-discovery utilities"""
|
2
|
+
import importlib
|
3
|
+
import inspect
|
4
|
+
import logging
|
5
|
+
from pathlib import Path
|
6
|
+
from typing import List
|
7
|
+
|
8
|
+
try:
|
9
|
+
import click
|
10
|
+
except ImportError:
|
11
|
+
click = None
|
12
|
+
|
13
|
+
logger = logging.getLogger(__name__)
|
14
|
+
|
15
|
+
|
16
|
+
def discover_commands(
|
17
|
+
base_package: str,
|
18
|
+
commands_subpackage: str = "presentation.cli.commands"
|
19
|
+
) -> List["click.Command"]:
|
20
|
+
"""
|
21
|
+
Auto-discover Click commands from a package.
|
22
|
+
|
23
|
+
This function scans a package directory for Python modules containing
|
24
|
+
Click Command instances and returns them as a list.
|
25
|
+
|
26
|
+
Args:
|
27
|
+
base_package: Base package name (use __package__ from calling module)
|
28
|
+
commands_subpackage: Subpackage path containing commands (default: "presentation.cli.commands")
|
29
|
+
|
30
|
+
Returns:
|
31
|
+
List[click.Command]: List of discovered Click commands
|
32
|
+
|
33
|
+
Example:
|
34
|
+
# In your project's presentation/cli/commands/__init__.py
|
35
|
+
from vega.discovery import discover_commands
|
36
|
+
|
37
|
+
def get_commands():
|
38
|
+
return discover_commands(__package__)
|
39
|
+
|
40
|
+
# Or with custom configuration
|
41
|
+
def get_commands():
|
42
|
+
return discover_commands(
|
43
|
+
__package__,
|
44
|
+
commands_subpackage="cli.custom_commands"
|
45
|
+
)
|
46
|
+
|
47
|
+
Note:
|
48
|
+
Each command module can export multiple Click Command instances.
|
49
|
+
All public (non-underscore prefixed) Command instances will be discovered.
|
50
|
+
"""
|
51
|
+
if click is None:
|
52
|
+
raise ImportError(
|
53
|
+
"Click is not installed. Install it with: pip install click"
|
54
|
+
)
|
55
|
+
|
56
|
+
commands = []
|
57
|
+
|
58
|
+
# Resolve the commands package path
|
59
|
+
try:
|
60
|
+
# Determine the package to scan
|
61
|
+
if base_package.endswith(commands_subpackage):
|
62
|
+
commands_package = base_package
|
63
|
+
else:
|
64
|
+
# Extract base from fully qualified package name
|
65
|
+
parts = base_package.split('.')
|
66
|
+
# Find the root package (usually the project name)
|
67
|
+
root_package = parts[0]
|
68
|
+
commands_package = f"{root_package}.{commands_subpackage}"
|
69
|
+
|
70
|
+
# Import the commands package to get its path
|
71
|
+
commands_module = importlib.import_module(commands_package)
|
72
|
+
commands_dir = Path(commands_module.__file__).parent
|
73
|
+
|
74
|
+
logger.debug(f"Discovering commands in: {commands_dir}")
|
75
|
+
|
76
|
+
# Scan for command modules
|
77
|
+
discovered_count = 0
|
78
|
+
for file in commands_dir.glob("*.py"):
|
79
|
+
if file.stem == "__init__":
|
80
|
+
continue
|
81
|
+
|
82
|
+
module_name = f"{commands_package}.{file.stem}"
|
83
|
+
|
84
|
+
try:
|
85
|
+
module = importlib.import_module(module_name)
|
86
|
+
|
87
|
+
# Find all Click Command instances
|
88
|
+
for name, obj in inspect.getmembers(module):
|
89
|
+
if isinstance(obj, click.Command) and not name.startswith("_"):
|
90
|
+
commands.append(obj)
|
91
|
+
discovered_count += 1
|
92
|
+
logger.info(f"Registered command: {name} from {module_name}")
|
93
|
+
|
94
|
+
except Exception as e:
|
95
|
+
logger.warning(f"Failed to import {module_name}: {e}")
|
96
|
+
continue
|
97
|
+
|
98
|
+
logger.info(f"Auto-discovery complete: {discovered_count} command(s) registered")
|
99
|
+
|
100
|
+
except ImportError as e:
|
101
|
+
logger.error(f"Failed to import commands package '{commands_package}': {e}")
|
102
|
+
raise
|
103
|
+
|
104
|
+
return commands
|
@@ -0,0 +1,129 @@
|
|
1
|
+
"""FastAPI router auto-discovery utilities"""
|
2
|
+
import importlib
|
3
|
+
import inspect
|
4
|
+
import logging
|
5
|
+
from pathlib import Path
|
6
|
+
from typing import Optional
|
7
|
+
|
8
|
+
try:
|
9
|
+
from fastapi import APIRouter
|
10
|
+
except ImportError:
|
11
|
+
APIRouter = None
|
12
|
+
|
13
|
+
logger = logging.getLogger(__name__)
|
14
|
+
|
15
|
+
|
16
|
+
def discover_routers(
|
17
|
+
base_package: str,
|
18
|
+
routes_subpackage: str = "presentation.web.routes",
|
19
|
+
api_prefix: str = "/api",
|
20
|
+
auto_tags: bool = True,
|
21
|
+
auto_prefix: bool = True
|
22
|
+
) -> "APIRouter":
|
23
|
+
"""
|
24
|
+
Auto-discover and register FastAPI routers from a package.
|
25
|
+
|
26
|
+
This function scans a package directory for Python modules containing
|
27
|
+
APIRouter instances named 'router' and automatically registers them
|
28
|
+
with the main router.
|
29
|
+
|
30
|
+
Args:
|
31
|
+
base_package: Base package name (use __package__ from calling module)
|
32
|
+
routes_subpackage: Subpackage path containing routes (default: "presentation.web.routes")
|
33
|
+
api_prefix: Prefix for the main API router (default: "/api")
|
34
|
+
auto_tags: Automatically generate tags from module name (default: True)
|
35
|
+
auto_prefix: Automatically generate prefix from module name (default: True)
|
36
|
+
|
37
|
+
Returns:
|
38
|
+
APIRouter: Main router with all discovered routers included
|
39
|
+
|
40
|
+
Example:
|
41
|
+
# In your project's presentation/web/routes/__init__.py
|
42
|
+
from vega.discovery import discover_routers
|
43
|
+
|
44
|
+
router = discover_routers(__package__)
|
45
|
+
|
46
|
+
# Or with custom configuration
|
47
|
+
router = discover_routers(
|
48
|
+
__package__,
|
49
|
+
routes_subpackage="api.routes",
|
50
|
+
api_prefix="/v1"
|
51
|
+
)
|
52
|
+
|
53
|
+
Note:
|
54
|
+
Each route module should export an APIRouter instance named 'router'.
|
55
|
+
The module filename will be used for tags and prefix generation if enabled.
|
56
|
+
"""
|
57
|
+
if APIRouter is None:
|
58
|
+
raise ImportError(
|
59
|
+
"FastAPI is not installed. Install it with: pip install fastapi"
|
60
|
+
)
|
61
|
+
|
62
|
+
main_router = APIRouter(prefix=api_prefix)
|
63
|
+
|
64
|
+
# Resolve the routes package path
|
65
|
+
try:
|
66
|
+
# Determine the package to scan
|
67
|
+
if base_package.endswith(routes_subpackage):
|
68
|
+
routes_package = base_package
|
69
|
+
else:
|
70
|
+
# Extract base from fully qualified package name
|
71
|
+
parts = base_package.split('.')
|
72
|
+
# Find the root package (usually the project name)
|
73
|
+
root_package = parts[0]
|
74
|
+
routes_package = f"{root_package}.{routes_subpackage}"
|
75
|
+
|
76
|
+
# Import the routes package to get its path
|
77
|
+
routes_module = importlib.import_module(routes_package)
|
78
|
+
routes_dir = Path(routes_module.__file__).parent
|
79
|
+
|
80
|
+
logger.debug(f"Discovering routers in: {routes_dir}")
|
81
|
+
|
82
|
+
# Scan for router modules
|
83
|
+
discovered_count = 0
|
84
|
+
for file in routes_dir.glob("*.py"):
|
85
|
+
if file.stem == "__init__":
|
86
|
+
continue
|
87
|
+
|
88
|
+
module_name = f"{routes_package}.{file.stem}"
|
89
|
+
|
90
|
+
try:
|
91
|
+
module = importlib.import_module(module_name)
|
92
|
+
|
93
|
+
# Find APIRouter instance named 'router'
|
94
|
+
router = getattr(module, 'router', None)
|
95
|
+
|
96
|
+
if isinstance(router, APIRouter):
|
97
|
+
# Generate tags and prefix from module name
|
98
|
+
if auto_tags:
|
99
|
+
tag = file.stem.replace("_", " ").title()
|
100
|
+
tags = [tag]
|
101
|
+
else:
|
102
|
+
tags = None
|
103
|
+
|
104
|
+
if auto_prefix:
|
105
|
+
prefix = f"/{file.stem.replace('_', '-')}"
|
106
|
+
else:
|
107
|
+
prefix = None
|
108
|
+
|
109
|
+
main_router.include_router(
|
110
|
+
router,
|
111
|
+
tags=tags,
|
112
|
+
prefix=prefix
|
113
|
+
)
|
114
|
+
discovered_count += 1
|
115
|
+
logger.info(f"Registered router: {module_name} (tags={tags}, prefix={prefix})")
|
116
|
+
else:
|
117
|
+
logger.debug(f"No 'router' found in {module_name}")
|
118
|
+
|
119
|
+
except Exception as e:
|
120
|
+
logger.warning(f"Failed to import {module_name}: {e}")
|
121
|
+
continue
|
122
|
+
|
123
|
+
logger.info(f"Auto-discovery complete: {discovered_count} router(s) registered")
|
124
|
+
|
125
|
+
except ImportError as e:
|
126
|
+
logger.error(f"Failed to import routes package '{routes_package}': {e}")
|
127
|
+
raise
|
128
|
+
|
129
|
+
return main_router
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{vega_framework-0.1.19 → vega_framework-0.1.22}/vega/cli/templates/domain/repository_interface.py.j2
RENAMED
File without changes
|
{vega_framework-0.1.19 → vega_framework-0.1.22}/vega/cli/templates/domain/service_interface.py.j2
RENAMED
File without changes
|
{vega_framework-0.1.19 → vega_framework-0.1.22}/vega/cli/templates/infrastructure/model.py.j2
RENAMED
File without changes
|
File without changes
|
{vega_framework-0.1.19 → vega_framework-0.1.22}/vega/cli/templates/infrastructure/service_impl.py.j2
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{vega_framework-0.1.19 → vega_framework-0.1.22}/vega/cli/templates/project/ARCHITECTURE.md.j2
RENAMED
File without changes
|
File without changes
|
File without changes
|
{vega_framework-0.1.19 → vega_framework-0.1.22}/vega/cli/templates/project/pyproject.toml.j2
RENAMED
File without changes
|
File without changes
|
{vega_framework-0.1.19 → vega_framework-0.1.22}/vega/cli/templates/sqlalchemy/alembic.ini.j2
RENAMED
File without changes
|
{vega_framework-0.1.19 → vega_framework-0.1.22}/vega/cli/templates/sqlalchemy/database_manager.py.j2
RENAMED
File without changes
|
File without changes
|
{vega_framework-0.1.19 → vega_framework-0.1.22}/vega/cli/templates/sqlalchemy/script.py.mako
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|