FastAPI-fastkit 1.2.0__py3-none-any.whl → 1.2.1__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.
@@ -1 +1 @@
1
- __version__ = 'v1.2.0'
1
+ __version__ = 'v1.2.1'
@@ -185,6 +185,7 @@ def inject_project_metadata(
185
185
  description,
186
186
  )
187
187
  _process_config_file(core_modules.get("config", ""), project_name)
188
+ _process_main_file(core_modules.get("main", ""), project_name)
188
189
 
189
190
  print_success("Project metadata injected successfully")
190
191
 
@@ -235,6 +236,40 @@ def _process_setup_file(
235
236
  raise BackendExceptions(f"Failed to process setup.py: {e}")
236
237
 
237
238
 
239
+ def _process_main_file(main_py: str, project_name: str) -> None:
240
+ """
241
+ Replace project name placeholders in the template main.py file.
242
+
243
+ Some templates (e.g. fastapi-single-module) inline the project name directly
244
+ in main.py rather than reading it from a settings module, so the placeholder
245
+ must be substituted here as well.
246
+
247
+ :param main_py: Path to main.py file
248
+ :param project_name: Project name
249
+ """
250
+ if not main_py or not os.path.exists(main_py):
251
+ return
252
+
253
+ try:
254
+ with open(main_py, "r", encoding="utf-8") as f:
255
+ content = f.read()
256
+
257
+ if "<project_name>" not in content:
258
+ return
259
+
260
+ content = content.replace("<project_name>", project_name)
261
+
262
+ with open(main_py, "w", encoding="utf-8") as f:
263
+ f.write(content)
264
+
265
+ debug_log("Injected project name into main.py", "info")
266
+ print_info("Injected project name into main.py")
267
+
268
+ except (OSError, UnicodeDecodeError) as e:
269
+ debug_log(f"Error processing main.py: {e}", "error")
270
+ raise BackendExceptions(f"Failed to process main.py: {e}")
271
+
272
+
238
273
  def _process_config_file(config_py: str, project_name: str) -> None:
239
274
  """
240
275
  Process config file and inject project name.
@@ -4,7 +4,7 @@
4
4
  # @author bnbong bbbong9@gmail.com
5
5
  # --------------------------------------------------------------------------
6
6
  import subprocess
7
- from typing import List
7
+ from typing import List, Tuple
8
8
 
9
9
  from fastapi_fastkit.core.exceptions import BackendExceptions
10
10
  from fastapi_fastkit.utils.logging import debug_log, get_logger
@@ -19,6 +19,47 @@ from .base import BasePackageManager
19
19
 
20
20
  logger = get_logger(__name__)
21
21
 
22
+ # PEP 440 version specifier operators, longest-match first so "===" beats "==".
23
+ _PEP440_OPERATORS = ("===", "==", "!=", "<=", ">=", "~=", "<", ">")
24
+
25
+
26
+ def _parse_pip_requirement(
27
+ requirement: str,
28
+ ) -> Tuple[str, List[str], str, str]:
29
+ """Parse a pip/PEP 508 requirement string.
30
+
31
+ Returns ``(name, extras, version_spec, marker)`` where ``version_spec``
32
+ includes the operator (e.g. ``">=1.2.3"``) or is empty when unspecified,
33
+ and ``marker`` is the environment marker without the leading semicolon.
34
+ """
35
+ req = requirement.strip()
36
+
37
+ marker = ""
38
+ if ";" in req:
39
+ req, marker = req.split(";", 1)
40
+ req = req.strip()
41
+ marker = marker.strip()
42
+
43
+ version_spec = ""
44
+ split_at = len(req)
45
+ for op in _PEP440_OPERATORS:
46
+ idx = req.find(op)
47
+ if idx != -1 and idx < split_at:
48
+ split_at = idx
49
+ if split_at < len(req):
50
+ version_spec = req[split_at:].strip()
51
+ req = req[:split_at].strip()
52
+
53
+ extras: List[str] = []
54
+ if "[" in req and req.endswith("]"):
55
+ bare_name, _, extras_part = req.partition("[")
56
+ extras = [
57
+ extra.strip() for extra in extras_part[:-1].split(",") if extra.strip()
58
+ ]
59
+ req = bare_name.strip()
60
+
61
+ return req, extras, version_spec, marker
62
+
22
63
 
23
64
  class PoetryManager(BasePackageManager):
24
65
  """Poetry package manager implementation."""
@@ -182,15 +223,35 @@ build-backend = "poetry.core.masonry.api"
182
223
  pyproject_path = self.get_dependency_file_path()
183
224
 
184
225
  try:
185
- # Create dependencies section for Poetry
226
+ # Create dependencies section for Poetry. Parse each requirement as
227
+ # PEP 508 so non-``==`` specifiers (``>=``, ``~=``, ...), extras, and
228
+ # environment markers all round-trip to valid TOML.
186
229
  deps_section = ""
187
230
  for dep in dependencies:
188
- # Convert pip-style to poetry-style
189
- if "==" in dep:
190
- name, version = dep.split("==", 1)
191
- deps_section += f'{name} = "{version}"\n'
231
+ name_part, extras, version_spec, marker = _parse_pip_requirement(dep)
232
+ if not name_part:
233
+ continue
234
+
235
+ if not version_spec:
236
+ version_str = "*"
237
+ elif version_spec.startswith("=="):
238
+ # Poetry treats a bare version as a pin; keep the old
239
+ # formatting to avoid churn in generated files.
240
+ version_str = version_spec[2:].strip()
241
+ else:
242
+ version_str = version_spec
243
+
244
+ if extras or marker:
245
+ parts = [f'version = "{version_str}"']
246
+ if extras:
247
+ extras_str = ", ".join(f'"{extra}"' for extra in extras)
248
+ parts.append(f"extras = [{extras_str}]")
249
+ if marker:
250
+ escaped_marker = marker.replace('"', '\\"')
251
+ parts.append(f'markers = "{escaped_marker}"')
252
+ deps_section += f"{name_part} = {{{', '.join(parts)}}}\n"
192
253
  else:
193
- deps_section += f'{dep} = "*"\n'
254
+ deps_section += f'{name_part} = "{version_str}"\n'
194
255
 
195
256
  # Create basic pyproject.toml content for Poetry
196
257
  pyproject_content = f"""[tool.poetry]
@@ -488,7 +488,7 @@ class DynamicConfigGenerator:
488
488
  content.append("")
489
489
  content.append("# Run application")
490
490
  content.append(
491
- "CMD ['uvicorn', 'src.main:app', '--host', '0.0.0.0', '--port', '8000']"
491
+ 'CMD ["uvicorn", "src.main:app", "--host", "0.0.0.0", "--port", "8000"]'
492
492
  )
493
493
  content.append("")
494
494
 
fastapi_fastkit/cli.py CHANGED
@@ -43,9 +43,28 @@ from fastapi_fastkit.utils.main import (
43
43
  validate_email,
44
44
  )
45
45
 
46
+ from . import __version__
47
+
46
48
  console = utils_console
47
49
 
48
- from . import __version__
50
+
51
+ def _cleanup_failed_project(
52
+ project_dir: str, user_workspace: str, create_project_folder: bool
53
+ ) -> None:
54
+ """
55
+ Clean up a partially created project after an error.
56
+
57
+ Only deletes a freshly created project folder. When the project was deployed
58
+ in-place (create_project_folder=False), project_dir equals the user's workspace,
59
+ and removing it would destroy unrelated files, so no cleanup is performed.
60
+ """
61
+ if not create_project_folder:
62
+ return
63
+ if not project_dir or not os.path.exists(project_dir):
64
+ return
65
+ if os.path.abspath(project_dir) == os.path.abspath(user_workspace):
66
+ return
67
+ shutil.rmtree(project_dir, ignore_errors=True)
49
68
 
50
69
 
51
70
  @click.group()
@@ -483,8 +502,7 @@ def init(
483
502
  if testing_type != "None":
484
503
  test_config_content = generator.generate_test_config()
485
504
  if test_config_content:
486
- test_config_path = os.path.join(project_dir, "tests", "conftest.py")
487
- os.makedirs(os.path.dirname(test_config_path), exist_ok=True)
505
+ test_config_path = os.path.join(project_dir, "pytest.ini")
488
506
  with open(test_config_path, "w") as f:
489
507
  f.write(test_config_content)
490
508
 
@@ -492,9 +510,9 @@ def init(
492
510
  deployment = config.get("deployment", [])
493
511
  if deployment and deployment != ["None"]:
494
512
  generator.generate_docker_files()
495
- print_success(f"Generated Docker deployment files")
513
+ print_success("Generated Docker deployment files")
496
514
 
497
- print_success(f"Generated configuration files for selected stack")
515
+ print_success("Generated configuration files for selected stack")
498
516
 
499
517
  # Create virtual environment and install dependencies
500
518
  venv_path = create_venv_with_manager(project_dir, package_manager)
@@ -514,8 +532,9 @@ def init(
514
532
  logger = get_logger()
515
533
  logger.exception(f"Error during project creation in init: {str(e)}")
516
534
  print_error(f"Error during project creation: {str(e)}")
517
- if os.path.exists(project_dir):
518
- shutil.rmtree(project_dir, ignore_errors=True)
535
+ _cleanup_failed_project(
536
+ project_dir, settings.USER_WORKSPACE, create_project_folder
537
+ )
519
538
 
520
539
  return
521
540
 
@@ -662,8 +681,9 @@ def init(
662
681
  logger = get_logger()
663
682
  logger.exception(f"Error during project creation in init: {str(e)}")
664
683
  print_error(f"Error during project creation: {str(e)}")
665
- if os.path.exists(project_dir):
666
- shutil.rmtree(project_dir, ignore_errors=True)
684
+ _cleanup_failed_project(
685
+ project_dir, settings.USER_WORKSPACE, create_project_folder
686
+ )
667
687
 
668
688
 
669
689
  @fastkit_cli.command()
@@ -930,9 +950,9 @@ def runserver(
930
950
  logger.exception(f"FileNotFoundError when starting server: {e}")
931
951
  if venv_python:
932
952
  print_error(
933
- f"Failed to run Python from the virtual environment. Make sure uvicorn is installed in the project's virtual environment."
953
+ "Failed to run Python from the virtual environment. Make sure uvicorn is installed in the project's virtual environment."
934
954
  )
935
955
  else:
936
956
  print_error(
937
- f"uvicorn not found. Make sure it's installed in your system Python."
957
+ "uvicorn not found. Make sure it's installed in your system Python."
938
958
  )
@@ -102,7 +102,7 @@ class FastkitConfig:
102
102
  "MySQL": ["pymysql", "aiomysql", "sqlalchemy", "alembic"],
103
103
  "MongoDB": ["motor", "beanie"],
104
104
  "Redis": ["redis[hiredis]", "aioredis"],
105
- "SQLite": ["sqlalchemy", "alembic"],
105
+ "SQLite": ["sqlalchemy", "aiosqlite", "alembic"],
106
106
  "None": [],
107
107
  },
108
108
  "authentication": {
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: FastAPI-fastkit
3
- Version: 1.2.0
3
+ Version: 1.2.1
4
4
  Summary: Fast, easy-to-use starter kit for new users of Python and FastAPI
5
5
  Author-Email: bnbong <bbbong9@gmail.com>
6
6
  License: MIT
@@ -12,12 +12,13 @@ Requires-Python: >=3.12
12
12
  Requires-Dist: click>=8.1.7
13
13
  Requires-Dist: rich>=13.9.2
14
14
  Provides-Extra: dev
15
- Requires-Dist: pytest>=8.3.3; extra == "dev"
15
+ Requires-Dist: pytest>=9.0.3; extra == "dev"
16
16
  Requires-Dist: pytest-cov>=6.2.1; extra == "dev"
17
- Requires-Dist: black>=24.10.0; extra == "dev"
17
+ Requires-Dist: black>=26.3.1; extra == "dev"
18
18
  Requires-Dist: pre-commit>=4.0.1; extra == "dev"
19
19
  Requires-Dist: mypy>=1.12.0; extra == "dev"
20
20
  Requires-Dist: isort>=5.13.2; extra == "dev"
21
+ Requires-Dist: pygments>=2.20.0; extra == "dev"
21
22
  Description-Content-Type: text/markdown
22
23
 
23
24
  <p align="center">
@@ -36,6 +37,9 @@ Description-Content-Type: text/markdown
36
37
  <a href="https://codecov.io/gh/bnbong/FastAPI-fastkit" >
37
38
  <img src="https://codecov.io/gh/bnbong/FastAPI-fastkit/graph/badge.svg?token=WS0B6WWD8K"/>
38
39
  </a>
40
+ <a href="https://pepy.tech/project/fastapi-fastkit">
41
+ <img src="https://static.pepy.tech/personalized-badge/fastapi-fastkit?period=total&units=INTERNATIONAL_SYSTEM&left_color=BLACK&right_color=GREEN&left_text=downloads" alt="PyPI Downloads">
42
+ </a>
39
43
  </p>
40
44
 
41
45
  ---
@@ -47,6 +51,7 @@ This project was inspired by the `SpringBoot initializer` & Python Django's `dja
47
51
  ## Key Features
48
52
 
49
53
  - **⚡ Immediate FastAPI project creation** : Super-fast FastAPI workspace & project creation via CLI, inspired by `django-admin` feature of [Python Django](https://github.com/django/django)
54
+ - **✨ Interactive project builder**: Guided step-by-step feature selection for databases, authentication, caching, monitoring, and more with auto-generated code
50
55
  - **🎨 Prettier CLI output** : Beautiful CLI experience powered by [rich library](https://github.com/Textualize/rich)
51
56
  - **📋 Standards-based FastAPI project templates** : Templates follow Python standards and common FastAPI patterns.
52
57
  - **🔍 Automated template quality assurance** : Weekly automated testing ensures all templates remain functional and up-to-date
@@ -79,6 +84,28 @@ fastkit init [OPTIONS]
79
84
  - `--package-manager` [pip|uv|pdm|poetry]
80
85
  - Stack selection: `minimal` | `standard` | `full` (interactive)
81
86
 
87
+ ### Create a project with interactive mode
88
+ ```console
89
+ fastkit init --interactive
90
+ ```
91
+ - What it does: Guided step-by-step project setup with intelligent feature selection
92
+ - Features:
93
+ - **Database selection**: PostgreSQL, MySQL, MongoDB, Redis, SQLite
94
+ - **Authentication**: JWT, OAuth2, FastAPI-Users, Session-based
95
+ - **Background tasks**: Celery, Dramatiq
96
+ - **Caching**: Redis, fastapi-cache2
97
+ - **Monitoring**: Loguru, OpenTelemetry, Prometheus
98
+ - **Testing**: Basic (pytest), Coverage, Advanced (with faker, factory-boy)
99
+ - **Utilities**: CORS, Rate-Limiting, Pagination, WebSocket
100
+ - **Deployment**: Docker, docker-compose with auto-generated configs
101
+ - **Package manager**: pip, uv, pdm, poetry
102
+ - **Custom packages**: Add your own dependencies
103
+ - Auto-generates:
104
+ - `main.py` with selected features integrated
105
+ - Database and authentication configuration files
106
+ - Docker deployment files (Dockerfile, docker-compose.yml)
107
+ - Test configuration (pytest with coverage)
108
+
82
109
  ### Create a project from a template
83
110
  ```console
84
111
  fastkit startdemo [TEMPLATE] [OPTIONS]
@@ -1,8 +1,8 @@
1
- fastapi_fastkit-1.2.0.dist-info/METADATA,sha256=iZblXw6CUHthMi-Sr8UqGt7Pio4j-c91YGvVtTbWPKc,7670
2
- fastapi_fastkit-1.2.0.dist-info/WHEEL,sha256=tsUv_t7BDeJeRHaSrczbGeuK-TtDpGsWi_JfpzD255I,90
3
- fastapi_fastkit-1.2.0.dist-info/entry_points.txt,sha256=IONmgb7zWPnJWsCOcpF3u1yP6AWnPjrgWIv49mr1DZE,76
4
- fastapi_fastkit-1.2.0.dist-info/licenses/LICENSE,sha256=2a9cYM3Uy8DW-so6zpYaqUafYT1Dznd3OCWCFe5CKNA,1066
5
- fastapi_fastkit/__init__.py,sha256=3fYvbK3reCGZv6LR-d0_6tSw-leXYFt3SadePKQDefo,23
1
+ fastapi_fastkit-1.2.1.dist-info/METADATA,sha256=ss4JwBYGr5g56XsTNQM6KqnGQSSYcSj-cUweDQq7sjI,9118
2
+ fastapi_fastkit-1.2.1.dist-info/WHEEL,sha256=Z36eTX6lG3PITRleSd5hAZHCcz52yg3c0JQVxKBbLW0,90
3
+ fastapi_fastkit-1.2.1.dist-info/entry_points.txt,sha256=IONmgb7zWPnJWsCOcpF3u1yP6AWnPjrgWIv49mr1DZE,76
4
+ fastapi_fastkit-1.2.1.dist-info/licenses/LICENSE,sha256=2a9cYM3Uy8DW-so6zpYaqUafYT1Dznd3OCWCFe5CKNA,1066
5
+ fastapi_fastkit/__init__.py,sha256=n28YDgLpCC7wZf_uB1xeqKSCdyKmIwYrD_UCvLQnHPc,23
6
6
  fastapi_fastkit/__main__.py,sha256=-FS9yUe4IEgDbJjFC1xso-pFCxRzUJ447j6bYpsBTBM,271
7
7
  fastapi_fastkit/backend/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
8
  fastapi_fastkit/backend/inspector.py,sha256=kiQbFko8kHW65LgRMpiuaru__Tsn_3FOx5CwtKSGt9c,51029
@@ -11,22 +11,22 @@ fastapi_fastkit/backend/interactive/config_builder.py,sha256=KgLTVG9VBZVGA5b2IKC
11
11
  fastapi_fastkit/backend/interactive/prompts.py,sha256=bXwXk-PnKsss8AQecf5NxTFUOTU7YU1QrjJYfLHo8_w,15343
12
12
  fastapi_fastkit/backend/interactive/selectors.py,sha256=fa1nKhPok6iqi0lsC3fzrlHYomTcRtdHJFF19fe43R4,7377
13
13
  fastapi_fastkit/backend/interactive/validators.py,sha256=WaRWuE6H2qQ5UExmHq3NQ41spiCyhbyk7QlYVXcfoHw,4651
14
- fastapi_fastkit/backend/main.py,sha256=weHSZ5Kd5sxy7d39S3ELb5lR2Wl8Vf2pGHy4Imy7_5A,29761
14
+ fastapi_fastkit/backend/main.py,sha256=xEubuHrTzRGXZ2VwCMV7Wmyn0LSSthHLEt4F48rBDBs,30942
15
15
  fastapi_fastkit/backend/package_managers/__init__.py,sha256=awSaY-pInI9K4mk1aWqdyKk143AGrcPI4NCy5l_48vw,691
16
16
  fastapi_fastkit/backend/package_managers/base.py,sha256=6IZmIgEgySocVRKH36ffyEMDrnyDYGR3vye7tFjCSRA,4644
17
17
  fastapi_fastkit/backend/package_managers/factory.py,sha256=sfmsAO3WC1IbehQRrAuJx3pDUtfvz3OZEMzd0kkDiZM,5452
18
18
  fastapi_fastkit/backend/package_managers/pdm_manager.py,sha256=5UF2XtSJB5vjTeG4NoziZT8HgMbNfSdJeALGm6sIN6Y,9938
19
19
  fastapi_fastkit/backend/package_managers/pip_manager.py,sha256=hNzGIH3WxKUOS9FLcRBe9DANGz4iYaBKUSwZOR8KQm0,8061
20
- fastapi_fastkit/backend/package_managers/poetry_manager.py,sha256=1wdf090w-n5JhSNlR66drI3JXSqUtO3FuzAEqlKihVw,14242
20
+ fastapi_fastkit/backend/package_managers/poetry_manager.py,sha256=S4RhLHIKVpDq5knaC18vhDkfNjd129uWqXCIeBVE3lw,16596
21
21
  fastapi_fastkit/backend/package_managers/uv_manager.py,sha256=F9oXnKgjd-fgLmB6-H9lWZVnMcf_psXyQNCQ3O45BCc,12410
22
22
  fastapi_fastkit/backend/project_builder/__init__.py,sha256=Y_0G3duIEyTtlJMAN7VgK4ufCNxCNKXnkT0u7QCldCI,574
23
- fastapi_fastkit/backend/project_builder/config_generator.py,sha256=8nyW0ODNwUgMePbIpfhj9UIqAvCnTfDIlrq10Yc3TKE,23100
23
+ fastapi_fastkit/backend/project_builder/config_generator.py,sha256=3e7F8DcwvW25Rj2l6Cc9rPKEjpEOcznVYHQXPPFbIao,23100
24
24
  fastapi_fastkit/backend/project_builder/dependency_collector.py,sha256=stQ77W7LUfsiKyyl3ggynC90Pd0UYFnW1xTrb-y286w,6419
25
25
  fastapi_fastkit/backend/transducer.py,sha256=rIqwJCm5nsruw3qYZYSEoevqOXoKs38MLAmexAM3O7Y,7171
26
- fastapi_fastkit/cli.py,sha256=mOrhr3tHkRQ2M3HrYPyUJ06fnjLpN2PgOyPuUBC2asI,30923
26
+ fastapi_fastkit/cli.py,sha256=60FZTMT9ulTwMthJsM0-WMJHKvcZiCK9JMAor9XH5-k,31556
27
27
  fastapi_fastkit/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
28
28
  fastapi_fastkit/core/exceptions.py,sha256=W29lbDlKE_yKdS-m_QHaWdDB1j3yGge6mVx5mo_jLrU,972
29
- fastapi_fastkit/core/settings.py,sha256=WdMjQaWxhB6CKEQ8s9b34xwhbYAyvX5yetvdNs-N9a8,8811
29
+ fastapi_fastkit/core/settings.py,sha256=lSb4S9rf8zVSD1KFXGP6o0BjZUPnu4wzviNp2KVZkIY,8824
30
30
  fastapi_fastkit/fastapi_project_template/PROJECT_README_TEMPLATE.md,sha256=5lT_ZY1QItTDbhKYNY6KlOoGlbSTBSrs9DJthcle3D4,1529
31
31
  fastapi_fastkit/fastapi_project_template/README.md,sha256=7urtQfEjWkX8tSnG_f28_z_kgB8Gjy-W4NxqN8M-df8,2637
32
32
  fastapi_fastkit/fastapi_project_template/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -253,4 +253,4 @@ fastapi_fastkit/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
253
253
  fastapi_fastkit/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
254
254
  fastapi_fastkit/utils/logging.py,sha256=oU7BnWInxzl_Gk8kGQ0f6cKdNHZcOVVcSHY4I9MTmR8,6431
255
255
  fastapi_fastkit/utils/main.py,sha256=iL1_dM2L2LLfGH9QvKhiup9wiBISrpDkJbXPSn1MJyE,9298
256
- fastapi_fastkit-1.2.0.dist-info/RECORD,,
256
+ fastapi_fastkit-1.2.1.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: pdm-backend (2.4.6)
2
+ Generator: pdm-backend (2.4.8)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any