FastAPI-fastkit 1.0.2__py3-none-any.whl → 1.1.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_fastkit/__init__.py +1 -10
- fastapi_fastkit/backend/inspector.py +933 -83
- fastapi_fastkit/backend/main.py +640 -482
- fastapi_fastkit/backend/package_managers/__init__.py +22 -0
- fastapi_fastkit/backend/package_managers/base.py +150 -0
- fastapi_fastkit/backend/package_managers/factory.py +151 -0
- fastapi_fastkit/backend/package_managers/pdm_manager.py +275 -0
- fastapi_fastkit/backend/package_managers/pip_manager.py +205 -0
- fastapi_fastkit/backend/package_managers/poetry_manager.py +378 -0
- fastapi_fastkit/backend/package_managers/uv_manager.py +327 -0
- fastapi_fastkit/backend/transducer.py +210 -106
- fastapi_fastkit/cli.py +702 -664
- fastapi_fastkit/core/exceptions.py +14 -3
- fastapi_fastkit/core/settings.py +181 -155
- fastapi_fastkit/fastapi_project_template/README.md +90 -90
- fastapi_fastkit/fastapi_project_template/fastapi-async-crud/.env-tpl +1 -1
- fastapi_fastkit/fastapi_project_template/fastapi-async-crud/.gitignore-tpl +30 -30
- fastapi_fastkit/fastapi_project_template/fastapi-async-crud/README.md-tpl +107 -107
- fastapi_fastkit/fastapi_project_template/fastapi-async-crud/requirements.txt-tpl +36 -36
- fastapi_fastkit/fastapi_project_template/fastapi-async-crud/scripts/format.sh-tpl +5 -5
- fastapi_fastkit/fastapi_project_template/fastapi-async-crud/scripts/lint.sh-tpl +5 -5
- fastapi_fastkit/fastapi_project_template/fastapi-async-crud/scripts/run-server.sh-tpl +8 -8
- fastapi_fastkit/fastapi_project_template/fastapi-async-crud/scripts/test.sh-tpl +6 -6
- fastapi_fastkit/fastapi_project_template/fastapi-async-crud/setup.cfg-tpl +14 -14
- fastapi_fastkit/fastapi_project_template/fastapi-async-crud/setup.py-tpl +25 -25
- fastapi_fastkit/fastapi_project_template/fastapi-async-crud/src/api/api.py-tpl +9 -9
- fastapi_fastkit/fastapi_project_template/fastapi-async-crud/src/api/routes/items.py-tpl +57 -57
- fastapi_fastkit/fastapi_project_template/fastapi-async-crud/src/core/config.py-tpl +64 -64
- fastapi_fastkit/fastapi_project_template/fastapi-async-crud/src/crud/items.py-tpl +21 -21
- fastapi_fastkit/fastapi_project_template/fastapi-async-crud/src/main.py-tpl +26 -26
- fastapi_fastkit/fastapi_project_template/fastapi-async-crud/src/mocks/mock_items.json-tpl +8 -8
- fastapi_fastkit/fastapi_project_template/fastapi-async-crud/src/schemas/items.py-tpl +22 -22
- fastapi_fastkit/fastapi_project_template/fastapi-async-crud/tests/conftest.py-tpl +42 -42
- fastapi_fastkit/fastapi_project_template/fastapi-async-crud/tests/test_items.py-tpl +48 -48
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/.env-tpl +1 -1
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/.gitignore-tpl +191 -191
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/README.md-tpl +156 -156
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/requirements.txt-tpl +36 -36
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/scripts/format.sh-tpl +5 -5
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/scripts/lint.sh-tpl +5 -5
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/scripts/run-server.sh-tpl +8 -8
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/scripts/test.sh-tpl +6 -6
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/setup.cfg-tpl +12 -12
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/setup.py-tpl +27 -27
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/src/api/api.py-tpl +9 -9
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/src/api/routes/items.py-tpl +172 -172
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/src/core/config.py-tpl +64 -64
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/src/crud/items.py-tpl +21 -21
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/src/helper/exceptions.py-tpl +103 -103
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/src/helper/pagination.py-tpl +72 -72
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/src/main.py-tpl +39 -39
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/src/mocks/mock_items.json-tpl +8 -8
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/src/schemas/base.py-tpl +49 -49
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/src/schemas/items.py-tpl +22 -22
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/src/utils/documents.py-tpl +20 -20
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/tests/conftest.py-tpl +42 -42
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/tests/test_items.py-tpl +61 -61
- fastapi_fastkit/fastapi_project_template/fastapi-default/README.md-tpl +101 -101
- fastapi_fastkit/fastapi_project_template/fastapi-default/scripts/run-server.sh-tpl +8 -8
- fastapi_fastkit/fastapi_project_template/fastapi-default/setup.py-tpl +22 -22
- fastapi_fastkit/fastapi_project_template/fastapi-default/src/mocks/mock_items.json-tpl +8 -8
- fastapi_fastkit/fastapi_project_template/fastapi-default/tests/conftest.py-tpl +30 -30
- fastapi_fastkit/fastapi_project_template/fastapi-default/tests/test_items.py-tpl +44 -44
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/.env-tpl +1 -1
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/.gitignore-tpl +30 -30
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/Dockerfile-tpl +23 -23
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/README.md-tpl +119 -119
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/requirements.txt-tpl +36 -36
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/scripts/format.sh-tpl +5 -5
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/scripts/lint.sh-tpl +5 -5
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/scripts/run-server.sh-tpl +8 -8
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/scripts/test.sh-tpl +6 -6
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/setup.cfg-tpl +13 -13
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/setup.py-tpl +22 -22
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/src/api/api.py-tpl +6 -6
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/src/api/routes/items.py-tpl +54 -54
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/src/core/config.py-tpl +64 -64
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/src/crud/items.py-tpl +15 -15
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/src/main.py-tpl +26 -26
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/src/mocks/mock_items.json-tpl +8 -8
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/src/schemas/items.py-tpl +19 -19
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/tests/conftest.py-tpl +30 -30
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/tests/test_items.py-tpl +44 -44
- fastapi_fastkit/fastapi_project_template/fastapi-empty/.env-tpl +1 -1
- fastapi_fastkit/fastapi_project_template/fastapi-empty/.gitignore-tpl +30 -0
- fastapi_fastkit/fastapi_project_template/fastapi-empty/README.md-tpl +85 -0
- fastapi_fastkit/fastapi_project_template/fastapi-empty/requirements.txt-tpl +9 -0
- fastapi_fastkit/fastapi_project_template/fastapi-empty/setup.cfg-tpl +13 -0
- fastapi_fastkit/fastapi_project_template/fastapi-empty/setup.py-tpl +22 -22
- fastapi_fastkit/fastapi_project_template/fastapi-empty/src/core/config.py-tpl +75 -75
- fastapi_fastkit/fastapi_project_template/fastapi-empty/src/main.py-tpl +48 -36
- fastapi_fastkit/fastapi_project_template/fastapi-empty/tests/__init__.py-tpl +0 -0
- fastapi_fastkit/fastapi_project_template/fastapi-empty/tests/conftest.py-tpl +16 -0
- fastapi_fastkit/fastapi_project_template/fastapi-empty/tests/test_main.py-tpl +37 -0
- fastapi_fastkit/fastapi_project_template/fastapi-mcp/.env-tpl +29 -0
- fastapi_fastkit/fastapi_project_template/fastapi-mcp/README.md-tpl +249 -0
- fastapi_fastkit/fastapi_project_template/fastapi-mcp/requirements.txt-tpl +40 -0
- fastapi_fastkit/fastapi_project_template/fastapi-mcp/scripts/format.sh-tpl +12 -0
- fastapi_fastkit/fastapi_project_template/fastapi-mcp/scripts/lint.sh-tpl +18 -0
- fastapi_fastkit/fastapi_project_template/fastapi-mcp/scripts/run-server.sh-tpl +9 -0
- fastapi_fastkit/fastapi_project_template/fastapi-mcp/scripts/test.sh-tpl +9 -0
- fastapi_fastkit/fastapi_project_template/fastapi-mcp/setup.cfg-tpl +90 -0
- fastapi_fastkit/fastapi_project_template/fastapi-mcp/setup.py-tpl +51 -0
- fastapi_fastkit/fastapi_project_template/fastapi-mcp/src/__init__.py-tpl +4 -0
- fastapi_fastkit/fastapi_project_template/fastapi-mcp/src/api/__init__.py-tpl +3 -0
- fastapi_fastkit/fastapi_project_template/fastapi-mcp/src/api/api.py-tpl +14 -0
- fastapi_fastkit/fastapi_project_template/fastapi-mcp/src/api/routes/__init__.py-tpl +3 -0
- fastapi_fastkit/fastapi_project_template/fastapi-mcp/src/api/routes/auth.py-tpl +134 -0
- fastapi_fastkit/fastapi_project_template/fastapi-mcp/src/api/routes/items.py-tpl +189 -0
- fastapi_fastkit/fastapi_project_template/fastapi-mcp/src/auth/__init__.py-tpl +3 -0
- fastapi_fastkit/fastapi_project_template/fastapi-mcp/src/auth/dependencies.py-tpl +88 -0
- fastapi_fastkit/fastapi_project_template/fastapi-mcp/src/core/__init__.py-tpl +3 -0
- fastapi_fastkit/fastapi_project_template/fastapi-mcp/src/core/config.py-tpl +60 -0
- fastapi_fastkit/fastapi_project_template/fastapi-mcp/src/main.py-tpl +66 -0
- fastapi_fastkit/fastapi_project_template/fastapi-mcp/src/mcp/__init__.py-tpl +3 -0
- fastapi_fastkit/fastapi_project_template/fastapi-mcp/src/mcp/router.py-tpl +100 -0
- fastapi_fastkit/fastapi_project_template/fastapi-mcp/src/schemas/__init__.py-tpl +3 -0
- fastapi_fastkit/fastapi_project_template/fastapi-mcp/src/schemas/items.py-tpl +67 -0
- fastapi_fastkit/fastapi_project_template/fastapi-mcp/tests/__init__.py-tpl +3 -0
- fastapi_fastkit/fastapi_project_template/fastapi-mcp/tests/conftest.py-tpl +49 -0
- fastapi_fastkit/fastapi_project_template/fastapi-mcp/tests/test_auth.py-tpl +101 -0
- fastapi_fastkit/fastapi_project_template/fastapi-mcp/tests/test_items.py-tpl +96 -0
- fastapi_fastkit/fastapi_project_template/fastapi-mcp/tests/test_mcp.py-tpl +81 -0
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/.env-tpl +9 -9
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/.gitignore-tpl +30 -30
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/Dockerfile-tpl +23 -23
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/README.md-tpl +144 -144
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/alembic.ini-tpl +119 -119
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/docker-compose.yml-tpl +44 -44
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/requirements.txt-tpl +41 -41
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/scripts/format.sh-tpl +5 -5
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/scripts/lint.sh-tpl +5 -5
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/scripts/pre-start.sh-tpl +16 -16
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/scripts/run-server.sh-tpl +8 -8
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/scripts/test.sh-tpl +10 -10
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/setup.cfg-tpl +13 -13
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/setup.py-tpl +25 -25
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/src/alembic/README-tpl +1 -1
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/src/alembic/env.py-tpl +85 -85
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/src/alembic/script.py.mako-tpl +26 -26
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/src/alembic/versions/bedcdc35b64a_first_alembic.py-tpl +37 -37
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/src/api/api.py-tpl +11 -11
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/src/api/deps.py-tpl +19 -19
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/src/api/routes/items.py-tpl +109 -109
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/src/core/config.py-tpl +88 -88
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/src/core/db.py-tpl +53 -53
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/src/crud/items.py-tpl +102 -102
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/src/main.py-tpl +45 -45
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/src/schemas/items.py-tpl +54 -54
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/src/utils/backend_pre_start.py-tpl +39 -39
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/src/utils/init_data.py-tpl +23 -23
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/src/utils/tests_pre_start.py-tpl +39 -39
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/template-config.yml-tpl +24 -0
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/tests/conftest.py-tpl +63 -63
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/tests/test_items.py-tpl +44 -44
- fastapi_fastkit/fastapi_project_template/modules/api/routes/new_route.py-tpl +64 -64
- fastapi_fastkit/fastapi_project_template/modules/crud/new_route.py-tpl +29 -29
- fastapi_fastkit/fastapi_project_template/modules/schemas/new_route.py-tpl +23 -23
- fastapi_fastkit/utils/logging.py +64 -28
- fastapi_fastkit/utils/main.py +180 -140
- fastapi_fastkit-1.1.0.dist-info/METADATA +182 -0
- {fastapi_fastkit-1.0.2.dist-info → fastapi_fastkit-1.1.0.dist-info}/RECORD +165 -129
- {fastapi_fastkit-1.0.2.dist-info → fastapi_fastkit-1.1.0.dist-info}/WHEEL +1 -1
- {fastapi_fastkit-1.0.2.dist-info → fastapi_fastkit-1.1.0.dist-info}/licenses/LICENSE +21 -21
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/src/.DS_Store +0 -0
- fastapi_fastkit/logs/fastkit_debug_20250702_161423.log +0 -21
- fastapi_fastkit/logs/fastkit_debug_20250702_162706.log +0 -20
- fastapi_fastkit/logs/fastkit_debug_20250702_163010.log +0 -21
- fastapi_fastkit/logs/fastkit_debug_20250702_163526.log +0 -21
- fastapi_fastkit/logs/fastkit_debug_20250702_164053.log +0 -21
- fastapi_fastkit/logs/fastkit_debug_20250702_165220.log +0 -21
- fastapi_fastkit/logs/fastkit_debug_20250702_165647.log +0 -21
- fastapi_fastkit-1.0.2.dist-info/METADATA +0 -320
- {fastapi_fastkit-1.0.2.dist-info → fastapi_fastkit-1.1.0.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# --------------------------------------------------------------------------
|
|
2
|
+
# Package Managers Module - FastAPI-fastkit
|
|
3
|
+
# Provides abstraction layer for different package managers (pip, uv, pdm, poetry)
|
|
4
|
+
#
|
|
5
|
+
# @author bnbong bbbong9@gmail.com
|
|
6
|
+
# --------------------------------------------------------------------------
|
|
7
|
+
|
|
8
|
+
from .base import BasePackageManager
|
|
9
|
+
from .factory import PackageManagerFactory
|
|
10
|
+
from .pdm_manager import PdmManager
|
|
11
|
+
from .pip_manager import PipManager
|
|
12
|
+
from .poetry_manager import PoetryManager
|
|
13
|
+
from .uv_manager import UvManager
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"BasePackageManager",
|
|
17
|
+
"PackageManagerFactory",
|
|
18
|
+
"PipManager",
|
|
19
|
+
"PdmManager",
|
|
20
|
+
"UvManager",
|
|
21
|
+
"PoetryManager",
|
|
22
|
+
]
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# --------------------------------------------------------------------------
|
|
2
|
+
# Base Package Manager - Abstract class for package manager implementations
|
|
3
|
+
#
|
|
4
|
+
# @author bnbong bbbong9@gmail.com
|
|
5
|
+
# --------------------------------------------------------------------------
|
|
6
|
+
import subprocess
|
|
7
|
+
from abc import ABC, abstractmethod
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Any, Dict, List, Optional
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class BasePackageManager(ABC):
|
|
13
|
+
"""
|
|
14
|
+
Abstract base class for package managers.
|
|
15
|
+
|
|
16
|
+
All package manager implementations must inherit from this class
|
|
17
|
+
and implement the required abstract methods.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def __init__(self, project_dir: str):
|
|
21
|
+
"""
|
|
22
|
+
Initialize package manager for a specific project.
|
|
23
|
+
|
|
24
|
+
:param project_dir: Path to the project directory
|
|
25
|
+
"""
|
|
26
|
+
self.project_dir = Path(project_dir)
|
|
27
|
+
self.name = self.__class__.__name__.replace("Manager", "").lower()
|
|
28
|
+
|
|
29
|
+
@abstractmethod
|
|
30
|
+
def is_available(self) -> bool:
|
|
31
|
+
"""
|
|
32
|
+
Check if the package manager is available on the system.
|
|
33
|
+
|
|
34
|
+
:return: True if package manager is installed and available
|
|
35
|
+
"""
|
|
36
|
+
pass
|
|
37
|
+
|
|
38
|
+
@abstractmethod
|
|
39
|
+
def get_dependency_file_name(self) -> str:
|
|
40
|
+
"""
|
|
41
|
+
Get the name of the dependency file for this package manager.
|
|
42
|
+
|
|
43
|
+
:return: Dependency file name (e.g., 'requirements.txt', 'pyproject.toml')
|
|
44
|
+
"""
|
|
45
|
+
pass
|
|
46
|
+
|
|
47
|
+
@abstractmethod
|
|
48
|
+
def create_virtual_environment(self) -> str:
|
|
49
|
+
"""
|
|
50
|
+
Create a virtual environment for the project.
|
|
51
|
+
|
|
52
|
+
:return: Path to the created virtual environment
|
|
53
|
+
:raises: Exception if virtual environment creation fails
|
|
54
|
+
"""
|
|
55
|
+
pass
|
|
56
|
+
|
|
57
|
+
@abstractmethod
|
|
58
|
+
def install_dependencies(self, venv_path: str) -> None:
|
|
59
|
+
"""
|
|
60
|
+
Install dependencies using the package manager.
|
|
61
|
+
|
|
62
|
+
:param venv_path: Path to the virtual environment
|
|
63
|
+
:raises: Exception if dependency installation fails
|
|
64
|
+
"""
|
|
65
|
+
pass
|
|
66
|
+
|
|
67
|
+
@abstractmethod
|
|
68
|
+
def generate_dependency_file(
|
|
69
|
+
self,
|
|
70
|
+
dependencies: List[str],
|
|
71
|
+
project_name: str = "",
|
|
72
|
+
author: str = "",
|
|
73
|
+
author_email: str = "",
|
|
74
|
+
description: str = "",
|
|
75
|
+
) -> None:
|
|
76
|
+
"""
|
|
77
|
+
Generate a dependency file with the given dependencies and metadata.
|
|
78
|
+
|
|
79
|
+
:param dependencies: List of dependency specifications
|
|
80
|
+
:param project_name: Name of the project
|
|
81
|
+
:param author: Author name
|
|
82
|
+
:param author_email: Author email
|
|
83
|
+
:param description: Project description
|
|
84
|
+
"""
|
|
85
|
+
pass
|
|
86
|
+
|
|
87
|
+
@abstractmethod
|
|
88
|
+
def add_dependency(self, dependency: str, dev: bool = False) -> None:
|
|
89
|
+
"""
|
|
90
|
+
Add a new dependency to the project.
|
|
91
|
+
|
|
92
|
+
:param dependency: Dependency specification
|
|
93
|
+
:param dev: Whether this is a development dependency
|
|
94
|
+
"""
|
|
95
|
+
pass
|
|
96
|
+
|
|
97
|
+
def get_executable_path(
|
|
98
|
+
self, executable_name: str, venv_path: Optional[str] = None
|
|
99
|
+
) -> str:
|
|
100
|
+
"""
|
|
101
|
+
Get the full path to an executable, considering virtual environment.
|
|
102
|
+
|
|
103
|
+
:param executable_name: Name of the executable
|
|
104
|
+
:param venv_path: Path to virtual environment (optional)
|
|
105
|
+
:return: Full path to the executable
|
|
106
|
+
"""
|
|
107
|
+
import os
|
|
108
|
+
|
|
109
|
+
if venv_path:
|
|
110
|
+
if os.name == "nt": # Windows
|
|
111
|
+
return os.path.join(venv_path, "Scripts", f"{executable_name}.exe")
|
|
112
|
+
else: # Unix-based
|
|
113
|
+
return os.path.join(venv_path, "bin", executable_name)
|
|
114
|
+
else:
|
|
115
|
+
return executable_name
|
|
116
|
+
|
|
117
|
+
def run_command(
|
|
118
|
+
self, command: List[str], **kwargs: Any
|
|
119
|
+
) -> subprocess.CompletedProcess[str]:
|
|
120
|
+
"""
|
|
121
|
+
Run a command with proper error handling.
|
|
122
|
+
|
|
123
|
+
:param command: Command to run as list of strings
|
|
124
|
+
:param kwargs: Additional keyword arguments for subprocess.run
|
|
125
|
+
:return: CompletedProcess instance
|
|
126
|
+
:raises: subprocess.CalledProcessError on failure
|
|
127
|
+
"""
|
|
128
|
+
default_kwargs: Dict[str, Any] = {
|
|
129
|
+
"check": True,
|
|
130
|
+
"capture_output": True,
|
|
131
|
+
"text": True,
|
|
132
|
+
"cwd": str(self.project_dir),
|
|
133
|
+
}
|
|
134
|
+
default_kwargs.update(kwargs)
|
|
135
|
+
|
|
136
|
+
return subprocess.run(command, **default_kwargs)
|
|
137
|
+
|
|
138
|
+
def get_dependency_file_path(self) -> Path:
|
|
139
|
+
"""
|
|
140
|
+
Get the full path to the dependency file.
|
|
141
|
+
|
|
142
|
+
:return: Path to the dependency file
|
|
143
|
+
"""
|
|
144
|
+
return self.project_dir / self.get_dependency_file_name()
|
|
145
|
+
|
|
146
|
+
def __str__(self) -> str:
|
|
147
|
+
return f"{self.__class__.__name__}({self.project_dir})"
|
|
148
|
+
|
|
149
|
+
def __repr__(self) -> str:
|
|
150
|
+
return self.__str__()
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# --------------------------------------------------------------------------
|
|
2
|
+
# Package Manager Factory - Creates appropriate package manager instances
|
|
3
|
+
#
|
|
4
|
+
# @author bnbong bbbong9@gmail.com
|
|
5
|
+
# --------------------------------------------------------------------------
|
|
6
|
+
from typing import Dict, List, Optional, Type
|
|
7
|
+
|
|
8
|
+
from fastapi_fastkit.core.exceptions import BackendExceptions
|
|
9
|
+
from fastapi_fastkit.utils.logging import debug_log, get_logger
|
|
10
|
+
|
|
11
|
+
from .base import BasePackageManager
|
|
12
|
+
from .pdm_manager import PdmManager
|
|
13
|
+
from .pip_manager import PipManager
|
|
14
|
+
from .poetry_manager import PoetryManager
|
|
15
|
+
from .uv_manager import UvManager
|
|
16
|
+
|
|
17
|
+
logger = get_logger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class PackageManagerFactory:
|
|
21
|
+
"""Factory for creating package manager instances."""
|
|
22
|
+
|
|
23
|
+
# Registry of available package managers
|
|
24
|
+
_managers: Dict[str, Type[BasePackageManager]] = {
|
|
25
|
+
"pip": PipManager,
|
|
26
|
+
"pdm": PdmManager,
|
|
27
|
+
"uv": UvManager,
|
|
28
|
+
"poetry": PoetryManager,
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
@classmethod
|
|
32
|
+
def create_manager(
|
|
33
|
+
self, manager_type: str, project_dir: str, auto_detect: bool = False
|
|
34
|
+
) -> BasePackageManager:
|
|
35
|
+
"""
|
|
36
|
+
Create a package manager instance.
|
|
37
|
+
|
|
38
|
+
:param manager_type: Type of package manager ('pip', 'uv', 'pdm', 'poetry')
|
|
39
|
+
:param project_dir: Path to the project directory
|
|
40
|
+
:param auto_detect: If True, auto-detect available package manager when requested type is not available
|
|
41
|
+
:return: Package manager instance
|
|
42
|
+
:raises: BackendExceptions if manager type is not supported or not available
|
|
43
|
+
"""
|
|
44
|
+
manager_type = manager_type.lower()
|
|
45
|
+
|
|
46
|
+
# Check if requested manager type is supported
|
|
47
|
+
if manager_type not in self._managers:
|
|
48
|
+
available_managers = list(self._managers.keys())
|
|
49
|
+
error_msg = f"Unsupported package manager: {manager_type}. Available: {available_managers}"
|
|
50
|
+
debug_log(error_msg, "error")
|
|
51
|
+
raise BackendExceptions(error_msg)
|
|
52
|
+
|
|
53
|
+
# Create the manager instance
|
|
54
|
+
manager_class = self._managers[manager_type]
|
|
55
|
+
manager = manager_class(project_dir)
|
|
56
|
+
|
|
57
|
+
# Check if the manager is available on the system
|
|
58
|
+
if not manager.is_available():
|
|
59
|
+
if auto_detect:
|
|
60
|
+
debug_log(
|
|
61
|
+
f"{manager_type} is not available, trying to auto-detect alternative",
|
|
62
|
+
"warning",
|
|
63
|
+
)
|
|
64
|
+
return self._auto_detect_manager(project_dir, exclude=[manager_type])
|
|
65
|
+
else:
|
|
66
|
+
error_msg = (
|
|
67
|
+
f"Package manager '{manager_type}' is not available on the system"
|
|
68
|
+
)
|
|
69
|
+
debug_log(error_msg, "error")
|
|
70
|
+
raise BackendExceptions(error_msg)
|
|
71
|
+
|
|
72
|
+
debug_log(f"Created {manager_type} package manager for {project_dir}", "info")
|
|
73
|
+
return manager
|
|
74
|
+
|
|
75
|
+
@classmethod
|
|
76
|
+
def _auto_detect_manager(
|
|
77
|
+
self, project_dir: str, exclude: Optional[List[str]] = None
|
|
78
|
+
) -> BasePackageManager:
|
|
79
|
+
"""
|
|
80
|
+
Auto-detect the best available package manager.
|
|
81
|
+
|
|
82
|
+
:param project_dir: Path to the project directory
|
|
83
|
+
:param exclude: List of manager types to exclude from detection
|
|
84
|
+
:return: Package manager instance
|
|
85
|
+
:raises: BackendExceptions if no package manager is available
|
|
86
|
+
"""
|
|
87
|
+
exclude = exclude or []
|
|
88
|
+
|
|
89
|
+
# Priority order for auto-detection
|
|
90
|
+
detection_order = ["uv", "pdm", "poetry", "pip"]
|
|
91
|
+
|
|
92
|
+
for manager_type in detection_order:
|
|
93
|
+
if manager_type in exclude:
|
|
94
|
+
continue
|
|
95
|
+
|
|
96
|
+
if manager_type not in self._managers:
|
|
97
|
+
continue
|
|
98
|
+
|
|
99
|
+
manager_class = self._managers[manager_type]
|
|
100
|
+
manager = manager_class(project_dir)
|
|
101
|
+
|
|
102
|
+
if manager.is_available():
|
|
103
|
+
debug_log(f"Auto-detected package manager: {manager_type}", "info")
|
|
104
|
+
return manager
|
|
105
|
+
|
|
106
|
+
error_msg = "No package manager is available on the system"
|
|
107
|
+
debug_log(error_msg, "error")
|
|
108
|
+
raise BackendExceptions(error_msg)
|
|
109
|
+
|
|
110
|
+
@classmethod
|
|
111
|
+
def get_available_managers(self) -> list[str]:
|
|
112
|
+
"""
|
|
113
|
+
Get list of available package managers on the current system.
|
|
114
|
+
|
|
115
|
+
:return: List of available package manager names
|
|
116
|
+
"""
|
|
117
|
+
available = []
|
|
118
|
+
|
|
119
|
+
for manager_type, manager_class in self._managers.items():
|
|
120
|
+
# Create a temporary instance to check availability
|
|
121
|
+
# Using current directory as a dummy project path
|
|
122
|
+
temp_manager = manager_class(".")
|
|
123
|
+
if temp_manager.is_available():
|
|
124
|
+
available.append(manager_type)
|
|
125
|
+
|
|
126
|
+
return available
|
|
127
|
+
|
|
128
|
+
@classmethod
|
|
129
|
+
def register_manager(
|
|
130
|
+
self, name: str, manager_class: Type[BasePackageManager]
|
|
131
|
+
) -> None:
|
|
132
|
+
"""
|
|
133
|
+
Register a new package manager type.
|
|
134
|
+
|
|
135
|
+
:param name: Name of the package manager
|
|
136
|
+
:param manager_class: Package manager class
|
|
137
|
+
"""
|
|
138
|
+
if not issubclass(manager_class, BasePackageManager):
|
|
139
|
+
raise ValueError(f"Manager class must inherit from BasePackageManager")
|
|
140
|
+
|
|
141
|
+
self._managers[name.lower()] = manager_class
|
|
142
|
+
debug_log(f"Registered package manager: {name}", "info")
|
|
143
|
+
|
|
144
|
+
@classmethod
|
|
145
|
+
def get_supported_managers(self) -> list[str]:
|
|
146
|
+
"""
|
|
147
|
+
Get list of all supported package manager types.
|
|
148
|
+
|
|
149
|
+
:return: List of supported package manager names
|
|
150
|
+
"""
|
|
151
|
+
return list(self._managers.keys())
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
# --------------------------------------------------------------------------
|
|
2
|
+
# PDM Package Manager Implementation
|
|
3
|
+
#
|
|
4
|
+
# @author bnbong bbbong9@gmail.com
|
|
5
|
+
# --------------------------------------------------------------------------
|
|
6
|
+
import os
|
|
7
|
+
import subprocess
|
|
8
|
+
import sys
|
|
9
|
+
from typing import List
|
|
10
|
+
|
|
11
|
+
from fastapi_fastkit import console
|
|
12
|
+
from fastapi_fastkit.core.exceptions import BackendExceptions
|
|
13
|
+
from fastapi_fastkit.utils.logging import debug_log, get_logger
|
|
14
|
+
from fastapi_fastkit.utils.main import handle_exception, print_error, print_success
|
|
15
|
+
|
|
16
|
+
from .base import BasePackageManager
|
|
17
|
+
|
|
18
|
+
logger = get_logger(__name__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class PdmManager(BasePackageManager):
|
|
22
|
+
"""PDM package manager implementation."""
|
|
23
|
+
|
|
24
|
+
def is_available(self) -> bool:
|
|
25
|
+
"""Check if PDM is available on the system."""
|
|
26
|
+
try:
|
|
27
|
+
subprocess.run(
|
|
28
|
+
["pdm", "--version"],
|
|
29
|
+
check=True,
|
|
30
|
+
capture_output=True,
|
|
31
|
+
text=True,
|
|
32
|
+
)
|
|
33
|
+
return True
|
|
34
|
+
except (subprocess.CalledProcessError, FileNotFoundError):
|
|
35
|
+
return False
|
|
36
|
+
|
|
37
|
+
def get_dependency_file_name(self) -> str:
|
|
38
|
+
"""Get the dependency file name for PDM."""
|
|
39
|
+
return "pyproject.toml"
|
|
40
|
+
|
|
41
|
+
def create_virtual_environment(self) -> str:
|
|
42
|
+
"""
|
|
43
|
+
Create a virtual environment using PDM.
|
|
44
|
+
|
|
45
|
+
:return: Path to the virtual environment
|
|
46
|
+
:raises: BackendExceptions if virtual environment creation fails
|
|
47
|
+
"""
|
|
48
|
+
venv_path = str(self.project_dir / ".venv")
|
|
49
|
+
|
|
50
|
+
try:
|
|
51
|
+
with console.status("[bold green]Creating virtual environment with PDM..."):
|
|
52
|
+
# PDM can create virtual environment in specific location
|
|
53
|
+
subprocess.run(
|
|
54
|
+
["pdm", "venv", "create", "--name", ".venv", sys.executable],
|
|
55
|
+
cwd=str(self.project_dir),
|
|
56
|
+
check=True,
|
|
57
|
+
capture_output=True,
|
|
58
|
+
text=True,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
debug_log(f"Virtual environment created at {venv_path}", "info")
|
|
62
|
+
print_success("Virtual environment created successfully with PDM")
|
|
63
|
+
return venv_path
|
|
64
|
+
|
|
65
|
+
except subprocess.CalledProcessError as e:
|
|
66
|
+
debug_log(f"Error creating virtual environment: {e.stderr}", "error")
|
|
67
|
+
handle_exception(e, f"Error creating virtual environment: {str(e)}")
|
|
68
|
+
raise BackendExceptions("Failed to create venv with PDM")
|
|
69
|
+
except OSError as e:
|
|
70
|
+
debug_log(f"System error creating virtual environment: {e}", "error")
|
|
71
|
+
handle_exception(e, f"Error creating virtual environment: {str(e)}")
|
|
72
|
+
raise BackendExceptions(f"Failed to create venv with PDM: {str(e)}")
|
|
73
|
+
|
|
74
|
+
def install_dependencies(self, venv_path: str) -> None:
|
|
75
|
+
"""
|
|
76
|
+
Install dependencies using PDM.
|
|
77
|
+
|
|
78
|
+
:param venv_path: Path to the virtual environment
|
|
79
|
+
:raises: BackendExceptions if dependency installation fails
|
|
80
|
+
"""
|
|
81
|
+
try:
|
|
82
|
+
if not os.path.exists(venv_path):
|
|
83
|
+
debug_log(
|
|
84
|
+
"Virtual environment does not exist. Creating it first.", "warning"
|
|
85
|
+
)
|
|
86
|
+
print_error("Virtual environment does not exist. Creating it first.")
|
|
87
|
+
venv_path = self.create_virtual_environment()
|
|
88
|
+
if not venv_path:
|
|
89
|
+
raise BackendExceptions("Failed to create venv")
|
|
90
|
+
|
|
91
|
+
pyproject_path = self.get_dependency_file_path()
|
|
92
|
+
if not pyproject_path.exists():
|
|
93
|
+
debug_log(f"pyproject.toml file not found at {pyproject_path}", "error")
|
|
94
|
+
print_error(f"pyproject.toml file not found at {pyproject_path}")
|
|
95
|
+
raise BackendExceptions("pyproject.toml file not found")
|
|
96
|
+
|
|
97
|
+
# Install dependencies using PDM
|
|
98
|
+
with console.status("[bold green]Installing dependencies with PDM..."):
|
|
99
|
+
subprocess.run(
|
|
100
|
+
["pdm", "install"],
|
|
101
|
+
cwd=str(self.project_dir),
|
|
102
|
+
check=True,
|
|
103
|
+
capture_output=True,
|
|
104
|
+
text=True,
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
debug_log("Dependencies installed successfully with PDM", "info")
|
|
108
|
+
print_success("Dependencies installed successfully with PDM")
|
|
109
|
+
|
|
110
|
+
except subprocess.CalledProcessError as e:
|
|
111
|
+
debug_log(f"Error during dependency installation: {e.stderr}", "error")
|
|
112
|
+
handle_exception(e, f"Error during dependency installation: {str(e)}")
|
|
113
|
+
if hasattr(e, "stderr"):
|
|
114
|
+
print_error(f"Error details: {e.stderr}")
|
|
115
|
+
raise BackendExceptions("Failed to install dependencies with PDM")
|
|
116
|
+
except OSError as e:
|
|
117
|
+
debug_log(f"System error during dependency installation: {e}", "error")
|
|
118
|
+
handle_exception(e, f"Error during dependency installation: {str(e)}")
|
|
119
|
+
raise BackendExceptions(
|
|
120
|
+
f"Failed to install dependencies with PDM: {str(e)}"
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
def generate_dependency_file(
|
|
124
|
+
self,
|
|
125
|
+
dependencies: List[str],
|
|
126
|
+
project_name: str = "",
|
|
127
|
+
author: str = "",
|
|
128
|
+
author_email: str = "",
|
|
129
|
+
description: str = "",
|
|
130
|
+
) -> None:
|
|
131
|
+
"""
|
|
132
|
+
Generate a pyproject.toml file with the given dependencies and metadata.
|
|
133
|
+
|
|
134
|
+
:param dependencies: List of dependency specifications
|
|
135
|
+
:param project_name: Name of the project
|
|
136
|
+
:param author: Author name
|
|
137
|
+
:param author_email: Author email
|
|
138
|
+
:param description: Project description
|
|
139
|
+
"""
|
|
140
|
+
pyproject_path = self.get_dependency_file_path()
|
|
141
|
+
|
|
142
|
+
try:
|
|
143
|
+
# Create dependencies list as TOML format
|
|
144
|
+
deps_toml = "[\n"
|
|
145
|
+
for dep in dependencies:
|
|
146
|
+
deps_toml += f' "{dep}",\n'
|
|
147
|
+
deps_toml += "]"
|
|
148
|
+
|
|
149
|
+
# Create basic pyproject.toml content as string
|
|
150
|
+
pyproject_content = f"""[project]
|
|
151
|
+
name = "{project_name or 'fastapi-project'}"
|
|
152
|
+
version = "0.1.0"
|
|
153
|
+
description = "{description or 'A FastAPI project'}"
|
|
154
|
+
authors = [
|
|
155
|
+
{{name = "{author or 'Author'}", email = "{author_email or 'author@example.com'}"}},
|
|
156
|
+
]
|
|
157
|
+
dependencies = {deps_toml}
|
|
158
|
+
requires-python = ">=3.8"
|
|
159
|
+
readme = "README.md"
|
|
160
|
+
license = {{text = "MIT"}}
|
|
161
|
+
|
|
162
|
+
[build-system]
|
|
163
|
+
requires = ["pdm-backend"]
|
|
164
|
+
build-backend = "pdm.backend"
|
|
165
|
+
|
|
166
|
+
[tool.hatch.build.targets.wheel]
|
|
167
|
+
packages = ["src"]
|
|
168
|
+
|
|
169
|
+
[tool.pdm]
|
|
170
|
+
"""
|
|
171
|
+
|
|
172
|
+
with open(pyproject_path, "w", encoding="utf-8") as f:
|
|
173
|
+
f.write(pyproject_content)
|
|
174
|
+
|
|
175
|
+
debug_log(
|
|
176
|
+
f"Generated {pyproject_path} with {len(dependencies)} dependencies",
|
|
177
|
+
"info",
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
except (OSError, UnicodeEncodeError) as e:
|
|
181
|
+
debug_log(f"Error generating pyproject.toml: {e}", "error")
|
|
182
|
+
raise BackendExceptions(f"Failed to generate pyproject.toml: {str(e)}")
|
|
183
|
+
|
|
184
|
+
def add_dependency(self, dependency: str, dev: bool = False) -> None:
|
|
185
|
+
"""
|
|
186
|
+
Add a new dependency using PDM.
|
|
187
|
+
|
|
188
|
+
:param dependency: Dependency specification
|
|
189
|
+
:param dev: Whether this is a development dependency
|
|
190
|
+
"""
|
|
191
|
+
try:
|
|
192
|
+
# Use PDM's add command to add dependency
|
|
193
|
+
cmd = ["pdm", "add"]
|
|
194
|
+
if dev:
|
|
195
|
+
cmd.append("--dev")
|
|
196
|
+
cmd.append(dependency)
|
|
197
|
+
|
|
198
|
+
subprocess.run(
|
|
199
|
+
cmd,
|
|
200
|
+
cwd=str(self.project_dir),
|
|
201
|
+
check=True,
|
|
202
|
+
capture_output=True,
|
|
203
|
+
text=True,
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
debug_log(
|
|
207
|
+
f"Added {'dev ' if dev else ''}dependency '{dependency}' with PDM",
|
|
208
|
+
"info",
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
except subprocess.CalledProcessError as e:
|
|
212
|
+
debug_log(f"Error adding dependency with PDM: {e}", "error")
|
|
213
|
+
raise BackendExceptions(f"Failed to add dependency with PDM: {str(e)}")
|
|
214
|
+
except OSError as e:
|
|
215
|
+
debug_log(f"System error adding dependency with PDM: {e}", "error")
|
|
216
|
+
raise BackendExceptions(f"Failed to add dependency with PDM: {str(e)}")
|
|
217
|
+
|
|
218
|
+
def initialize_project(
|
|
219
|
+
self, project_name: str, author: str, author_email: str, description: str
|
|
220
|
+
) -> None:
|
|
221
|
+
"""
|
|
222
|
+
Initialize a new PDM project with metadata.
|
|
223
|
+
|
|
224
|
+
:param project_name: Name of the project
|
|
225
|
+
:param author: Author name
|
|
226
|
+
:param author_email: Author email
|
|
227
|
+
:param description: Project description
|
|
228
|
+
"""
|
|
229
|
+
try:
|
|
230
|
+
# Use PDM init command for interactive setup
|
|
231
|
+
subprocess.run(
|
|
232
|
+
["pdm", "init", "--non-interactive"],
|
|
233
|
+
cwd=str(self.project_dir),
|
|
234
|
+
check=True,
|
|
235
|
+
capture_output=True,
|
|
236
|
+
text=True,
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
# Create or update pyproject.toml with provided metadata
|
|
240
|
+
pyproject_path = self.get_dependency_file_path()
|
|
241
|
+
|
|
242
|
+
# Create basic pyproject.toml with project metadata
|
|
243
|
+
pyproject_content = f"""[project]
|
|
244
|
+
name = "{project_name}"
|
|
245
|
+
version = "0.1.0"
|
|
246
|
+
description = "{description}"
|
|
247
|
+
authors = [
|
|
248
|
+
{{name = "{author}", email = "{author_email}"}},
|
|
249
|
+
]
|
|
250
|
+
dependencies = []
|
|
251
|
+
requires-python = ">=3.8"
|
|
252
|
+
readme = "README.md"
|
|
253
|
+
license = {{text = "MIT"}}
|
|
254
|
+
|
|
255
|
+
[build-system]
|
|
256
|
+
requires = ["pdm-backend"]
|
|
257
|
+
build-backend = "pdm.backend"
|
|
258
|
+
|
|
259
|
+
[tool.hatch.build.targets.wheel]
|
|
260
|
+
packages = ["src"]
|
|
261
|
+
|
|
262
|
+
[tool.pdm]
|
|
263
|
+
"""
|
|
264
|
+
|
|
265
|
+
with open(pyproject_path, "w", encoding="utf-8") as f:
|
|
266
|
+
f.write(pyproject_content)
|
|
267
|
+
|
|
268
|
+
debug_log(f"Initialized PDM project: {project_name}", "info")
|
|
269
|
+
|
|
270
|
+
except subprocess.CalledProcessError as e:
|
|
271
|
+
debug_log(f"Error initializing PDM project: {e}", "error")
|
|
272
|
+
raise BackendExceptions(f"Failed to initialize PDM project: {str(e)}")
|
|
273
|
+
except (OSError, UnicodeEncodeError) as e:
|
|
274
|
+
debug_log(f"Error updating project metadata: {e}", "error")
|
|
275
|
+
raise BackendExceptions(f"Failed to update project metadata: {str(e)}")
|