FastAPI-fastkit 1.3.0__py3-none-any.whl → 1.4.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.
Files changed (335) hide show
  1. fastapi_fastkit/__init__.py +1 -1
  2. fastapi_fastkit/backend/inspection/__init__.py +26 -0
  3. fastapi_fastkit/backend/inspection/checks.py +349 -0
  4. fastapi_fastkit/backend/inspection/consistency.py +125 -0
  5. fastapi_fastkit/backend/inspection/context.py +67 -0
  6. fastapi_fastkit/backend/inspection/core.py +347 -0
  7. fastapi_fastkit/backend/inspection/docker.py +185 -0
  8. fastapi_fastkit/backend/inspection/freshness.py +145 -0
  9. fastapi_fastkit/backend/inspection/fsutils.py +139 -0
  10. fastapi_fastkit/backend/inspection/lint.py +89 -0
  11. fastapi_fastkit/backend/inspection/report.py +58 -0
  12. fastapi_fastkit/backend/inspection/smoke.py +327 -0
  13. fastapi_fastkit/backend/inspection/strategies.py +336 -0
  14. fastapi_fastkit/backend/inspector.py +18 -1400
  15. fastapi_fastkit/backend/interactive/__init__.py +6 -0
  16. fastapi_fastkit/backend/interactive/config_builder.py +45 -63
  17. fastapi_fastkit/backend/interactive/prompts.py +155 -6
  18. fastapi_fastkit/backend/interactive/selectors.py +18 -2
  19. fastapi_fastkit/backend/main.py +887 -99
  20. fastapi_fastkit/backend/package_managers/base.py +172 -0
  21. fastapi_fastkit/backend/package_managers/factory.py +5 -4
  22. fastapi_fastkit/backend/package_managers/pdm_manager.py +29 -82
  23. fastapi_fastkit/backend/package_managers/pip_manager.py +31 -85
  24. fastapi_fastkit/backend/package_managers/poetry_manager.py +70 -110
  25. fastapi_fastkit/backend/package_managers/uv_manager.py +43 -112
  26. fastapi_fastkit/backend/project_builder/__init__.py +8 -1
  27. fastapi_fastkit/backend/project_builder/config_generator.py +806 -450
  28. fastapi_fastkit/backend/project_builder/config_schema.py +384 -0
  29. fastapi_fastkit/backend/project_builder/dependency_collector.py +105 -66
  30. fastapi_fastkit/backend/project_builder/preset_layout.py +254 -28
  31. fastapi_fastkit/backend/scaffolder.py +578 -0
  32. fastapi_fastkit/backend/transducer.py +54 -21
  33. fastapi_fastkit/cli.py +434 -311
  34. fastapi_fastkit/core/settings.py +232 -43
  35. fastapi_fastkit/fastapi_project_template/README.md +26 -10
  36. fastapi_fastkit/fastapi_project_template/fastapi-async-crud/README.md-tpl +3 -0
  37. fastapi_fastkit/fastapi_project_template/fastapi-async-crud/pyproject.toml-tpl +21 -22
  38. fastapi_fastkit/fastapi_project_template/fastapi-async-crud/requirements.txt-tpl +39 -36
  39. fastapi_fastkit/fastapi_project_template/fastapi-async-crud/src/api/api.py-tpl +3 -0
  40. fastapi_fastkit/fastapi_project_template/fastapi-async-crud/src/api/routes/items.py-tpl +1 -2
  41. fastapi_fastkit/fastapi_project_template/fastapi-async-crud/src/core/config.py-tpl +5 -6
  42. fastapi_fastkit/fastapi_project_template/fastapi-async-crud/src/crud/items.py-tpl +1 -2
  43. fastapi_fastkit/fastapi_project_template/fastapi-async-crud/src/main.py-tpl +9 -1
  44. fastapi_fastkit/fastapi_project_template/fastapi-async-crud/src/schemas/items.py-tpl +1 -2
  45. fastapi_fastkit/fastapi_project_template/fastapi-async-crud/tests/conftest.py-tpl +0 -1
  46. fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/.env-tpl +21 -0
  47. fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/.gitignore-tpl +33 -0
  48. fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/Dockerfile-tpl +18 -0
  49. fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/README.md-tpl +210 -0
  50. fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/alembic.ini-tpl +119 -0
  51. fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/pyproject.toml-tpl +86 -0
  52. fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/requirements.txt-tpl +23 -0
  53. fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/scripts/format.sh-tpl +5 -0
  54. fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/scripts/lint.sh-tpl +6 -0
  55. fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/scripts/run-server.sh-tpl +8 -0
  56. fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/scripts/test.sh-tpl +6 -0
  57. fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/__init__.py-tpl +0 -0
  58. fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/__init__.py-tpl +0 -0
  59. fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/alembic/README-tpl +1 -0
  60. fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/alembic/env.py-tpl +51 -0
  61. fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/alembic/script.py.mako-tpl +26 -0
  62. fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/alembic/versions/0001_initial_auth_schema.py-tpl +62 -0
  63. fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/api/__init__.py-tpl +0 -0
  64. fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/api/deps.py-tpl +91 -0
  65. fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/api/health.py-tpl +11 -0
  66. fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/api/router.py-tpl +17 -0
  67. fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/core/__init__.py-tpl +0 -0
  68. fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/core/config.py-tpl +64 -0
  69. fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/core/security.py-tpl +100 -0
  70. fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/db/__init__.py-tpl +0 -0
  71. fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/db/session.py-tpl +36 -0
  72. fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/domains/__init__.py-tpl +0 -0
  73. fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/domains/auth/__init__.py-tpl +0 -0
  74. fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/domains/auth/models.py-tpl +25 -0
  75. fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/domains/auth/repository.py-tpl +41 -0
  76. fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/domains/auth/router.py-tpl +82 -0
  77. fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/domains/auth/schemas.py-tpl +14 -0
  78. fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/domains/auth/service.py-tpl +65 -0
  79. fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/domains/users/__init__.py-tpl +0 -0
  80. fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/domains/users/models.py-tpl +35 -0
  81. fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/domains/users/repository.py-tpl +33 -0
  82. fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/domains/users/router.py-tpl +71 -0
  83. fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/domains/users/schemas.py-tpl +29 -0
  84. fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/domains/users/service.py-tpl +55 -0
  85. fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/main.py-tpl +69 -0
  86. fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/template-config.yml-tpl +16 -0
  87. fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/tests/__init__.py-tpl +0 -0
  88. fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/tests/conftest.py-tpl +73 -0
  89. fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/tests/test_auth.py-tpl +201 -0
  90. fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/tests/test_health.py-tpl +15 -0
  91. fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/tests/test_users.py-tpl +77 -0
  92. fastapi_fastkit/fastapi_project_template/fastapi-custom-response/pyproject.toml-tpl +21 -22
  93. fastapi_fastkit/fastapi_project_template/fastapi-custom-response/requirements.txt-tpl +39 -36
  94. fastapi_fastkit/fastapi_project_template/fastapi-custom-response/src/api/api.py-tpl +3 -0
  95. fastapi_fastkit/fastapi_project_template/fastapi-custom-response/src/api/routes/items.py-tpl +3 -3
  96. fastapi_fastkit/fastapi_project_template/fastapi-custom-response/src/core/config.py-tpl +5 -6
  97. fastapi_fastkit/fastapi_project_template/fastapi-custom-response/src/crud/items.py-tpl +1 -2
  98. fastapi_fastkit/fastapi_project_template/fastapi-custom-response/src/helper/pagination.py-tpl +8 -8
  99. fastapi_fastkit/fastapi_project_template/fastapi-custom-response/src/main.py-tpl +9 -0
  100. fastapi_fastkit/fastapi_project_template/fastapi-custom-response/src/schemas/base.py-tpl +1 -1
  101. fastapi_fastkit/fastapi_project_template/fastapi-custom-response/src/schemas/items.py-tpl +1 -2
  102. fastapi_fastkit/fastapi_project_template/fastapi-custom-response/tests/conftest.py-tpl +0 -1
  103. fastapi_fastkit/fastapi_project_template/fastapi-default/pyproject.toml-tpl +18 -19
  104. fastapi_fastkit/fastapi_project_template/fastapi-default/requirements.txt-tpl +37 -34
  105. fastapi_fastkit/fastapi_project_template/fastapi-default/src/api/api.py-tpl +3 -0
  106. fastapi_fastkit/fastapi_project_template/fastapi-default/src/api/routes/items.py-tpl +1 -2
  107. fastapi_fastkit/fastapi_project_template/fastapi-default/src/core/config.py-tpl +5 -6
  108. fastapi_fastkit/fastapi_project_template/fastapi-default/src/crud/items.py-tpl +1 -2
  109. fastapi_fastkit/fastapi_project_template/fastapi-default/src/main.py-tpl +9 -1
  110. fastapi_fastkit/fastapi_project_template/fastapi-default/src/schemas/items.py-tpl +1 -2
  111. fastapi_fastkit/fastapi_project_template/fastapi-default/tests/conftest.py-tpl +2 -3
  112. fastapi_fastkit/fastapi_project_template/fastapi-default/tests/test_items.py-tpl +0 -2
  113. fastapi_fastkit/fastapi_project_template/fastapi-dockerized/Dockerfile-tpl +1 -1
  114. fastapi_fastkit/fastapi_project_template/fastapi-dockerized/README.md-tpl +3 -0
  115. fastapi_fastkit/fastapi_project_template/fastapi-dockerized/pyproject.toml-tpl +21 -22
  116. fastapi_fastkit/fastapi_project_template/fastapi-dockerized/requirements.txt-tpl +41 -36
  117. fastapi_fastkit/fastapi_project_template/fastapi-dockerized/src/api/api.py-tpl +3 -0
  118. fastapi_fastkit/fastapi_project_template/fastapi-dockerized/src/api/routes/items.py-tpl +1 -2
  119. fastapi_fastkit/fastapi_project_template/fastapi-dockerized/src/core/config.py-tpl +5 -6
  120. fastapi_fastkit/fastapi_project_template/fastapi-dockerized/src/crud/items.py-tpl +1 -2
  121. fastapi_fastkit/fastapi_project_template/fastapi-dockerized/src/main.py-tpl +9 -1
  122. fastapi_fastkit/fastapi_project_template/fastapi-dockerized/src/schemas/items.py-tpl +1 -2
  123. fastapi_fastkit/fastapi_project_template/fastapi-dockerized/tests/conftest.py-tpl +2 -3
  124. fastapi_fastkit/fastapi_project_template/fastapi-dockerized/tests/test_items.py-tpl +0 -2
  125. fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/pyproject.toml-tpl +13 -14
  126. fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/requirements.txt-tpl +36 -10
  127. fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/src/app/api/router.py-tpl +3 -0
  128. fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/src/app/core/config.py-tpl +4 -4
  129. fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/src/app/db/memory.py-tpl +4 -3
  130. fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/src/app/domains/items/repository.py-tpl +4 -5
  131. fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/src/app/domains/items/router.py-tpl +2 -3
  132. fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/src/app/domains/items/service.py-tpl +2 -3
  133. fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/src/app/main.py-tpl +3 -0
  134. fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/tests/conftest.py-tpl +0 -1
  135. fastapi_fastkit/fastapi_project_template/fastapi-empty/pyproject.toml-tpl +14 -15
  136. fastapi_fastkit/fastapi_project_template/fastapi-empty/requirements.txt-tpl +36 -10
  137. fastapi_fastkit/fastapi_project_template/fastapi-empty/src/core/config.py-tpl +5 -6
  138. fastapi_fastkit/fastapi_project_template/fastapi-empty/src/main.py-tpl +3 -1
  139. fastapi_fastkit/fastapi_project_template/fastapi-empty/tests/conftest.py-tpl +0 -1
  140. fastapi_fastkit/fastapi_project_template/fastapi-empty/tests/test_main.py-tpl +0 -1
  141. fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/.env-tpl +15 -0
  142. fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/.gitignore-tpl +31 -0
  143. fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/Dockerfile-tpl +18 -0
  144. fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/README.md-tpl +193 -0
  145. fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/pyproject.toml-tpl +76 -0
  146. fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/requirements.txt-tpl +15 -0
  147. fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/scripts/chat.sh-tpl +10 -0
  148. fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/scripts/format.sh-tpl +5 -0
  149. fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/scripts/lint.sh-tpl +6 -0
  150. fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/scripts/run-server.sh-tpl +8 -0
  151. fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/scripts/test.sh-tpl +6 -0
  152. fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/src/__init__.py-tpl +0 -0
  153. fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/src/app/__init__.py-tpl +0 -0
  154. fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/src/app/api/__init__.py-tpl +0 -0
  155. fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/src/app/api/chat.py-tpl +62 -0
  156. fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/src/app/api/deps.py-tpl +23 -0
  157. fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/src/app/api/health.py-tpl +11 -0
  158. fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/src/app/api/router.py-tpl +13 -0
  159. fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/src/app/core/__init__.py-tpl +0 -0
  160. fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/src/app/core/config.py-tpl +67 -0
  161. fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/src/app/llm/__init__.py-tpl +0 -0
  162. fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/src/app/llm/agent.py-tpl +136 -0
  163. fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/src/app/llm/client.py-tpl +30 -0
  164. fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/src/app/llm/tools.py-tpl +131 -0
  165. fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/src/app/main.py-tpl +53 -0
  166. fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/src/app/memory/__init__.py-tpl +0 -0
  167. fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/src/app/memory/base.py-tpl +27 -0
  168. fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/src/app/memory/in_memory.py-tpl +32 -0
  169. fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/src/app/schemas/__init__.py-tpl +0 -0
  170. fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/src/app/schemas/chat.py-tpl +46 -0
  171. fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/template-config.yml-tpl +16 -0
  172. fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/tests/__init__.py-tpl +0 -0
  173. fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/tests/conftest.py-tpl +158 -0
  174. fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/tests/test_chat.py-tpl +160 -0
  175. fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/tests/test_health.py-tpl +12 -0
  176. fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/tests/test_memory.py-tpl +38 -0
  177. fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/tests/test_tools.py-tpl +37 -0
  178. fastapi_fastkit/fastapi_project_template/fastapi-mcp/README.md-tpl +1 -1
  179. fastapi_fastkit/fastapi_project_template/fastapi-mcp/pyproject.toml-tpl +25 -25
  180. fastapi_fastkit/fastapi_project_template/fastapi-mcp/requirements.txt-tpl +68 -39
  181. fastapi_fastkit/fastapi_project_template/fastapi-mcp/src/api/api.py-tpl +3 -0
  182. fastapi_fastkit/fastapi_project_template/fastapi-mcp/src/api/routes/auth.py-tpl +4 -5
  183. fastapi_fastkit/fastapi_project_template/fastapi-mcp/src/api/routes/items.py-tpl +3 -4
  184. fastapi_fastkit/fastapi_project_template/fastapi-mcp/src/auth/dependencies.py-tpl +4 -5
  185. fastapi_fastkit/fastapi_project_template/fastapi-mcp/src/core/config.py-tpl +4 -5
  186. fastapi_fastkit/fastapi_project_template/fastapi-mcp/src/main.py-tpl +4 -1
  187. fastapi_fastkit/fastapi_project_template/fastapi-mcp/src/{mcp → mcp_server}/router.py-tpl +3 -5
  188. fastapi_fastkit/fastapi_project_template/fastapi-mcp/src/schemas/items.py-tpl +6 -7
  189. fastapi_fastkit/fastapi_project_template/fastapi-mcp/tests/conftest.py-tpl +1 -1
  190. fastapi_fastkit/fastapi_project_template/fastapi-mcp/tests/test_auth.py-tpl +0 -1
  191. fastapi_fastkit/fastapi_project_template/fastapi-mcp/tests/test_items.py-tpl +0 -1
  192. fastapi_fastkit/fastapi_project_template/fastapi-mcp/tests/test_mcp.py-tpl +0 -1
  193. fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/Dockerfile-tpl +1 -1
  194. fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/pyproject.toml-tpl +22 -23
  195. fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/requirements.txt-tpl +44 -41
  196. fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/src/alembic/env.py-tpl +1 -3
  197. fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/src/alembic/versions/bedcdc35b64a_first_alembic.py-tpl +5 -6
  198. fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/src/api/api.py-tpl +2 -0
  199. fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/src/api/routes/items.py-tpl +2 -4
  200. fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/src/core/config.py-tpl +1 -2
  201. fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/src/core/db.py-tpl +1 -1
  202. fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/src/crud/items.py-tpl +4 -6
  203. fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/src/main.py-tpl +9 -0
  204. fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/src/schemas/items.py-tpl +4 -5
  205. fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/tests/conftest.py-tpl +1 -5
  206. fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/tests/test_items.py-tpl +0 -2
  207. fastapi_fastkit/fastapi_project_template/fastapi-single-module/.env-tpl +1 -0
  208. fastapi_fastkit/fastapi_project_template/fastapi-single-module/.gitignore-tpl +30 -0
  209. fastapi_fastkit/fastapi_project_template/fastapi-single-module/pyproject.toml-tpl +3 -4
  210. fastapi_fastkit/fastapi_project_template/fastapi-single-module/requirements.txt-tpl +26 -6
  211. fastapi_fastkit/fastapi_project_template/fastapi-single-module/src/main.py-tpl +3 -0
  212. fastapi_fastkit/fastapi_project_template/fastapi-single-module/tests/__init__.py-tpl +0 -0
  213. fastapi_fastkit/fastapi_project_template/fastapi-single-module/tests/conftest.py-tpl +0 -1
  214. fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/.env-tpl +7 -0
  215. fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/.gitignore-tpl +35 -0
  216. fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/Dockerfile-tpl +18 -0
  217. fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/README.md-tpl +166 -0
  218. fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/alembic.ini-tpl +44 -0
  219. fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/pyproject.toml-tpl +82 -0
  220. fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/requirements.txt-tpl +21 -0
  221. fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/scripts/format.sh-tpl +5 -0
  222. fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/scripts/lint.sh-tpl +6 -0
  223. fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/scripts/migrate.sh-tpl +7 -0
  224. fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/scripts/run-server.sh-tpl +8 -0
  225. fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/scripts/test.sh-tpl +6 -0
  226. fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/__init__.py-tpl +0 -0
  227. fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/app/__init__.py-tpl +0 -0
  228. fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/app/alembic/README-tpl +9 -0
  229. fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/app/alembic/env.py-tpl +72 -0
  230. fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/app/alembic/script.py.mako-tpl +27 -0
  231. fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/app/alembic/versions/0001_create_items_table.py-tpl +38 -0
  232. fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/app/api/__init__.py-tpl +0 -0
  233. fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/app/api/health.py-tpl +11 -0
  234. fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/app/api/router.py-tpl +15 -0
  235. fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/app/core/__init__.py-tpl +0 -0
  236. fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/app/core/config.py-tpl +62 -0
  237. fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/app/crud/__init__.py-tpl +0 -0
  238. fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/app/crud/base.py-tpl +62 -0
  239. fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/app/crud/pagination.py-tpl +21 -0
  240. fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/app/db/__init__.py-tpl +0 -0
  241. fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/app/db/base.py-tpl +12 -0
  242. fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/app/db/session.py-tpl +50 -0
  243. fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/app/domains/__init__.py-tpl +0 -0
  244. fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/app/domains/items/__init__.py-tpl +0 -0
  245. fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/app/domains/items/crud.py-tpl +26 -0
  246. fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/app/domains/items/models.py-tpl +70 -0
  247. fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/app/domains/items/router.py-tpl +69 -0
  248. fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/app/main.py-tpl +43 -0
  249. fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/template-config.yml-tpl +16 -0
  250. fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/tests/__init__.py-tpl +0 -0
  251. fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/tests/conftest.py-tpl +60 -0
  252. fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/tests/test_crud_base.py-tpl +43 -0
  253. fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/tests/test_health.py-tpl +12 -0
  254. fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/tests/test_items.py-tpl +110 -0
  255. fastapi_fastkit/fastapi_project_template/modules/api/routes/new_route.py-tpl +2 -2
  256. fastapi_fastkit/fragments/README.md +90 -0
  257. fastapi_fastkit/fragments/auth/fastapi_users.py.j2 +8 -0
  258. fastapi_fastkit/fragments/auth/jwt.py.j2 +44 -0
  259. fastapi_fastkit/fragments/auth/oauth2.py.j2 +105 -0
  260. fastapi_fastkit/fragments/auth/session.py.j2 +67 -0
  261. fastapi_fastkit/fragments/db/mongodb.py.j2 +19 -0
  262. fastapi_fastkit/fragments/db/redis.py.j2 +26 -0
  263. fastapi_fastkit/fragments/db/sqlalchemy.py.j2 +45 -0
  264. fastapi_fastkit/fragments/docker/docker_compose.j2 +84 -0
  265. fastapi_fastkit/fragments/docker/dockerfile.j2 +19 -0
  266. fastapi_fastkit/fragments/features/cache.py.j2 +26 -0
  267. fastapi_fastkit/fragments/features/pagination.py.j2 +31 -0
  268. fastapi_fastkit/fragments/features/tasks_celery.py.j2 +50 -0
  269. fastapi_fastkit/fragments/features/tasks_dramatiq.py.j2 +59 -0
  270. fastapi_fastkit/fragments/features/websocket.py.j2 +66 -0
  271. fastapi_fastkit/fragments/header.j2 +4 -0
  272. fastapi_fastkit/fragments/logging/structured.py.j2 +88 -0
  273. fastapi_fastkit/fragments/main/imports/async_tasks.j2 +1 -0
  274. fastapi_fastkit/fragments/main/imports/caching.j2 +5 -0
  275. fastapi_fastkit/fragments/main/imports/fastapi_users.j2 +1 -0
  276. fastapi_fastkit/fragments/main/imports/jwt.j2 +1 -0
  277. fastapi_fastkit/fragments/main/imports/loguru.j2 +1 -0
  278. fastapi_fastkit/fragments/main/imports/mongodb.j2 +1 -0
  279. fastapi_fastkit/fragments/main/imports/oauth2.j2 +4 -0
  280. fastapi_fastkit/fragments/main/imports/opentelemetry.j2 +6 -0
  281. fastapi_fastkit/fragments/main/imports/pagination.j2 +3 -0
  282. fastapi_fastkit/fragments/main/imports/prometheus.j2 +1 -0
  283. fastapi_fastkit/fragments/main/imports/rate_limiting.j2 +3 -0
  284. fastapi_fastkit/fragments/main/imports/session_auth.j2 +4 -0
  285. fastapi_fastkit/fragments/main/imports/sqlalchemy.j2 +2 -0
  286. fastapi_fastkit/fragments/main/imports/structured_logging.j2 +1 -0
  287. fastapi_fastkit/fragments/main/imports/websocket.j2 +1 -0
  288. fastapi_fastkit/fragments/main/lifespan/caching.j2 +7 -0
  289. fastapi_fastkit/fragments/main/lifespan/mongodb.j2 +2 -0
  290. fastapi_fastkit/fragments/main/lifespan/prometheus.j2 +2 -0
  291. fastapi_fastkit/fragments/main/lifespan/sqlalchemy.j2 +2 -0
  292. fastapi_fastkit/fragments/main/setup/async_tasks.j2 +1 -0
  293. fastapi_fastkit/fragments/main/setup/caching.j2 +1 -0
  294. fastapi_fastkit/fragments/main/setup/cors.j2 +8 -0
  295. fastapi_fastkit/fragments/main/setup/oauth2.j2 +3 -0
  296. fastapi_fastkit/fragments/main/setup/opentelemetry.j2 +19 -0
  297. fastapi_fastkit/fragments/main/setup/pagination.j2 +2 -0
  298. fastapi_fastkit/fragments/main/setup/rate_limiting.j2 +3 -0
  299. fastapi_fastkit/fragments/main/setup/session_auth.j2 +6 -0
  300. fastapi_fastkit/fragments/main/setup/structured_logging.j2 +2 -0
  301. fastapi_fastkit/fragments/main/setup/websocket.j2 +1 -0
  302. fastapi_fastkit/fragments/main.py.j2 +57 -0
  303. fastapi_fastkit/fragments/migrations/alembic_ini.j2 +42 -0
  304. fastapi_fastkit/fragments/migrations/env.py.j2 +76 -0
  305. fastapi_fastkit/fragments/migrations/initial_revision.py.j2 +27 -0
  306. fastapi_fastkit/fragments/migrations/migrate_sh.j2 +34 -0
  307. fastapi_fastkit/fragments/migrations/script.py.mako.j2 +26 -0
  308. fastapi_fastkit/fragments/tasks/celery_worker.py.j2 +49 -0
  309. fastapi_fastkit/fragments/tasks/dramatiq_worker.py.j2 +41 -0
  310. fastapi_fastkit/fragments/test/factories.py.j2 +68 -0
  311. fastapi_fastkit/fragments/test/pytest_ini.j2 +16 -0
  312. fastapi_fastkit/fragments/test/test_factories.py.j2 +27 -0
  313. fastapi_fastkit/fragments/test/test_main.py.j2 +46 -0
  314. fastapi_fastkit/fragments/tooling/devcontainer.j2 +26 -0
  315. fastapi_fastkit/fragments/tooling/github_actions.j2 +37 -0
  316. fastapi_fastkit/fragments/tooling/makefile.j2 +40 -0
  317. fastapi_fastkit/fragments/tooling/pre_commit.j2 +36 -0
  318. fastapi_fastkit/fragments/tooling/ruff_toml.j2 +16 -0
  319. fastapi_fastkit/utils/config_file.py +263 -0
  320. fastapi_fastkit/utils/main.py +38 -0
  321. {fastapi_fastkit-1.3.0.dist-info → fastapi_fastkit-1.4.0.dist-info}/METADATA +26 -2
  322. fastapi_fastkit-1.4.0.dist-info/RECORD +484 -0
  323. {fastapi_fastkit-1.3.0.dist-info → fastapi_fastkit-1.4.0.dist-info}/WHEEL +1 -1
  324. fastapi_fastkit/fastapi_project_template/fastapi-async-crud/setup.py-tpl +0 -25
  325. fastapi_fastkit/fastapi_project_template/fastapi-custom-response/setup.py-tpl +0 -27
  326. fastapi_fastkit/fastapi_project_template/fastapi-default/setup.py-tpl +0 -22
  327. fastapi_fastkit/fastapi_project_template/fastapi-dockerized/setup.py-tpl +0 -22
  328. fastapi_fastkit/fastapi_project_template/fastapi-empty/setup.py-tpl +0 -22
  329. fastapi_fastkit/fastapi_project_template/fastapi-mcp/setup.py-tpl +0 -51
  330. fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/setup.py-tpl +0 -25
  331. fastapi_fastkit/fastapi_project_template/fastapi-single-module/setup.py-tpl +0 -17
  332. fastapi_fastkit-1.3.0.dist-info/RECORD +0 -287
  333. /fastapi_fastkit/fastapi_project_template/fastapi-mcp/src/{mcp → mcp_server}/__init__.py-tpl +0 -0
  334. {fastapi_fastkit-1.3.0.dist-info → fastapi_fastkit-1.4.0.dist-info}/entry_points.txt +0 -0
  335. {fastapi_fastkit-1.3.0.dist-info → fastapi_fastkit-1.4.0.dist-info}/licenses/LICENSE +0 -0
@@ -3,11 +3,32 @@
3
3
  #
4
4
  # @author bnbong bbbong9@gmail.com
5
5
  # --------------------------------------------------------------------------
6
+ import os
6
7
  import subprocess
7
8
  from abc import ABC, abstractmethod
8
9
  from pathlib import Path
9
10
  from typing import Any, Dict, List, Optional
10
11
 
12
+ from fastapi_fastkit.core.exceptions import BackendExceptions
13
+ from fastapi_fastkit.core.settings import settings
14
+ from fastapi_fastkit.utils.logging import debug_log
15
+ from fastapi_fastkit.utils.main import console, handle_exception, print_error
16
+
17
+
18
+ def _with_stderr_tail(message: str, stderr: str, max_lines: int = 5) -> str:
19
+ """Append the last few non-empty stderr lines to an error message.
20
+
21
+ :param message: Base error message
22
+ :param stderr: Captured stderr of the failed command
23
+ :param max_lines: Maximum number of trailing stderr lines to include
24
+ :return: ``message`` unchanged when stderr carries nothing useful
25
+ """
26
+ lines = [line.strip() for line in (stderr or "").splitlines()]
27
+ lines = [line for line in lines if line]
28
+ if not lines:
29
+ return message
30
+ return f"{message}: " + " | ".join(lines[-max_lines:])
31
+
11
32
 
12
33
  class BasePackageManager(ABC):
13
34
  """
@@ -130,11 +151,162 @@ class BasePackageManager(ABC):
130
151
  "capture_output": True,
131
152
  "text": True,
132
153
  "cwd": str(self.project_dir),
154
+ "timeout": settings.get_subprocess_timeout(),
133
155
  }
134
156
  default_kwargs.update(kwargs)
135
157
 
136
158
  return subprocess.run(command, **default_kwargs)
137
159
 
160
+ def _check_command_available(self, command: List[str]) -> bool:
161
+ """
162
+ Check whether a command can be executed on the current system.
163
+
164
+ :param command: Version probe command (e.g. ``["uv", "--version"]``)
165
+ :return: True if the command executed successfully
166
+ """
167
+ try:
168
+ subprocess.run(
169
+ command,
170
+ check=True,
171
+ capture_output=True,
172
+ text=True,
173
+ timeout=settings.get_subprocess_timeout("check"),
174
+ )
175
+ return True
176
+ except (
177
+ subprocess.CalledProcessError,
178
+ subprocess.TimeoutExpired,
179
+ FileNotFoundError,
180
+ OSError,
181
+ ):
182
+ return False
183
+
184
+ def _run_checked(
185
+ self,
186
+ command: List[str],
187
+ status_msg: str,
188
+ error_prefix: str,
189
+ cwd: Optional[str] = None,
190
+ timeout: Optional[int] = None,
191
+ summarize_output: bool = False,
192
+ ) -> subprocess.CompletedProcess[str]:
193
+ """
194
+ Run a command with a status spinner and uniform error handling.
195
+
196
+ :param command: Command to run as list of strings
197
+ :param status_msg: Message shown while the command runs
198
+ :param error_prefix: Message used for the raised ``BackendExceptions``
199
+ :param cwd: Working directory (defaults to the project directory)
200
+ :param timeout: Timeout in seconds (defaults to the general timeout)
201
+ :param summarize_output: Print the tail of the command output on success
202
+ :return: CompletedProcess instance
203
+ :raises BackendExceptions: if the command fails, times out or cannot run
204
+ """
205
+ timeout = timeout if timeout is not None else settings.get_subprocess_timeout()
206
+
207
+ try:
208
+ with console.status(f"[bold green]{status_msg}"):
209
+ result: subprocess.CompletedProcess[str] = subprocess.run(
210
+ command,
211
+ cwd=cwd if cwd is not None else str(self.project_dir),
212
+ check=True,
213
+ capture_output=True,
214
+ text=True,
215
+ timeout=timeout,
216
+ )
217
+
218
+ if summarize_output:
219
+ self._print_output_summary(result)
220
+
221
+ return result
222
+
223
+ except subprocess.TimeoutExpired as e:
224
+ message = (
225
+ f"{error_prefix}: command timed out after {timeout}s "
226
+ f"({' '.join(command)}). "
227
+ f"Set {settings.SUBPROCESS_TIMEOUT_ENV_VAR} to raise the limit."
228
+ )
229
+ debug_log(message, "error")
230
+ handle_exception(e, message)
231
+ raise BackendExceptions(message)
232
+ except subprocess.CalledProcessError as e:
233
+ stderr = e.stderr if isinstance(e.stderr, str) else ""
234
+ debug_log(f"{error_prefix}: {stderr or e}", "error")
235
+ handle_exception(e, f"{error_prefix}: {str(e)}")
236
+ if stderr:
237
+ print_error(f"Error details: {stderr}")
238
+ # Carry the tail of stderr into the exception message: the raised
239
+ # BackendExceptions is what callers (and the CLI) surface, and a
240
+ # bare prefix leaves "why did it fail?" unanswerable from a log.
241
+ raise BackendExceptions(_with_stderr_tail(error_prefix, stderr))
242
+ except OSError as e:
243
+ debug_log(f"{error_prefix}: {e}", "error")
244
+ handle_exception(e, f"{error_prefix}: {str(e)}")
245
+ raise BackendExceptions(f"{error_prefix}: {str(e)}")
246
+
247
+ @staticmethod
248
+ def _print_output_summary(
249
+ result: subprocess.CompletedProcess[str], max_lines: int = 5
250
+ ) -> None:
251
+ """
252
+ Print the tail of a completed command's output.
253
+
254
+ Keeps the user informed about long running installs without dumping the
255
+ whole log. Silently does nothing when there is no textual output.
256
+
257
+ :param result: Completed process whose output should be summarized
258
+ :param max_lines: Maximum number of trailing lines to print
259
+ """
260
+ chunks = [
261
+ chunk
262
+ for chunk in (
263
+ getattr(result, "stdout", None),
264
+ getattr(result, "stderr", None),
265
+ )
266
+ if isinstance(chunk, str)
267
+ ]
268
+ lines = [line.strip() for chunk in chunks for line in chunk.splitlines()]
269
+ lines = [line for line in lines if line]
270
+ if not lines:
271
+ return
272
+
273
+ for line in lines[-max_lines:]:
274
+ console.print(f"[dim] {line}[/dim]")
275
+
276
+ def _ensure_venv(self, venv_path: str) -> str:
277
+ """
278
+ Make sure a virtual environment exists, creating it when missing.
279
+
280
+ :param venv_path: Path to the virtual environment
281
+ :return: Path to an existing virtual environment
282
+ :raises BackendExceptions: if the virtual environment cannot be created
283
+ """
284
+ if os.path.exists(venv_path):
285
+ return venv_path
286
+
287
+ debug_log("Virtual environment does not exist. Creating it first.", "warning")
288
+ print_error("Virtual environment does not exist. Creating it first.")
289
+ created = self.create_virtual_environment()
290
+ if not created:
291
+ raise BackendExceptions("Failed to create venv")
292
+ return created
293
+
294
+ def _require_dependency_file(self, error_message: Optional[str] = None) -> Path:
295
+ """
296
+ Return the dependency file path, failing when it is missing.
297
+
298
+ :param error_message: Message used for the raised exception
299
+ :return: Path to the dependency file
300
+ :raises BackendExceptions: if the dependency file does not exist
301
+ """
302
+ dependency_path = self.get_dependency_file_path()
303
+ if not dependency_path.exists():
304
+ file_name = self.get_dependency_file_name()
305
+ debug_log(f"{file_name} file not found at {dependency_path}", "error")
306
+ print_error(f"{file_name} file not found at {dependency_path}")
307
+ raise BackendExceptions(error_message or f"{file_name} file not found")
308
+ return dependency_path
309
+
138
310
  def get_dependency_file_path(self) -> Path:
139
311
  """
140
312
  Get the full path to the dependency file.
@@ -20,6 +20,10 @@ logger = get_logger(__name__)
20
20
  class PackageManagerFactory:
21
21
  """Factory for creating package manager instances."""
22
22
 
23
+ # Auto-detection priority. ``uv`` wins whenever it is installed and ``pip``
24
+ # is only the last-resort fallback; explicit selection bypasses this order.
25
+ DETECTION_ORDER: List[str] = ["uv", "pdm", "poetry", "pip"]
26
+
23
27
  # Registry of available package managers
24
28
  _managers: Dict[str, Type[BasePackageManager]] = {
25
29
  "pip": PipManager,
@@ -86,10 +90,7 @@ class PackageManagerFactory:
86
90
  """
87
91
  exclude = exclude or []
88
92
 
89
- # Priority order for auto-detection
90
- detection_order = ["uv", "pdm", "poetry", "pip"]
91
-
92
- for manager_type in detection_order:
93
+ for manager_type in self.DETECTION_ORDER:
93
94
  if manager_type in exclude:
94
95
  continue
95
96
 
@@ -3,19 +3,14 @@
3
3
  #
4
4
  # @author bnbong bbbong9@gmail.com
5
5
  # --------------------------------------------------------------------------
6
- import os
7
6
  import subprocess
8
7
  import sys
9
8
  from typing import List
10
9
 
11
10
  from fastapi_fastkit.core.exceptions import BackendExceptions
11
+ from fastapi_fastkit.core.settings import settings
12
12
  from fastapi_fastkit.utils.logging import debug_log, get_logger
13
- from fastapi_fastkit.utils.main import (
14
- console,
15
- handle_exception,
16
- print_error,
17
- print_success,
18
- )
13
+ from fastapi_fastkit.utils.main import print_success
19
14
 
20
15
  from .base import BasePackageManager
21
16
 
@@ -27,16 +22,7 @@ class PdmManager(BasePackageManager):
27
22
 
28
23
  def is_available(self) -> bool:
29
24
  """Check if PDM is available on the system."""
30
- try:
31
- subprocess.run(
32
- ["pdm", "--version"],
33
- check=True,
34
- capture_output=True,
35
- text=True,
36
- )
37
- return True
38
- except (subprocess.CalledProcessError, FileNotFoundError):
39
- return False
25
+ return self._check_command_available(["pdm", "--version"])
40
26
 
41
27
  def get_dependency_file_name(self) -> str:
42
28
  """Get the dependency file name for PDM."""
@@ -51,29 +37,16 @@ class PdmManager(BasePackageManager):
51
37
  """
52
38
  venv_path = str(self.project_dir / ".venv")
53
39
 
54
- try:
55
- with console.status("[bold green]Creating virtual environment with PDM..."):
56
- # PDM can create virtual environment in specific location
57
- subprocess.run(
58
- ["pdm", "venv", "create", "--name", ".venv", sys.executable],
59
- cwd=str(self.project_dir),
60
- check=True,
61
- capture_output=True,
62
- text=True,
63
- )
64
-
65
- debug_log(f"Virtual environment created at {venv_path}", "info")
66
- print_success("Virtual environment created successfully with PDM")
67
- return venv_path
68
-
69
- except subprocess.CalledProcessError as e:
70
- debug_log(f"Error creating virtual environment: {e.stderr}", "error")
71
- handle_exception(e, f"Error creating virtual environment: {str(e)}")
72
- raise BackendExceptions("Failed to create venv with PDM")
73
- except OSError as e:
74
- debug_log(f"System error creating virtual environment: {e}", "error")
75
- handle_exception(e, f"Error creating virtual environment: {str(e)}")
76
- raise BackendExceptions(f"Failed to create venv with PDM: {str(e)}")
40
+ self._run_checked(
41
+ ["pdm", "venv", "create", "--name", ".venv", sys.executable],
42
+ status_msg="Creating virtual environment with PDM...",
43
+ error_prefix="Failed to create venv with PDM",
44
+ timeout=settings.get_subprocess_timeout("venv"),
45
+ )
46
+
47
+ debug_log(f"Virtual environment created at {venv_path}", "info")
48
+ print_success("Virtual environment created successfully with PDM")
49
+ return venv_path
77
50
 
78
51
  def install_dependencies(self, venv_path: str) -> None:
79
52
  """
@@ -82,47 +55,19 @@ class PdmManager(BasePackageManager):
82
55
  :param venv_path: Path to the virtual environment
83
56
  :raises: BackendExceptions if dependency installation fails
84
57
  """
85
- try:
86
- if not os.path.exists(venv_path):
87
- debug_log(
88
- "Virtual environment does not exist. Creating it first.", "warning"
89
- )
90
- print_error("Virtual environment does not exist. Creating it first.")
91
- venv_path = self.create_virtual_environment()
92
- if not venv_path:
93
- raise BackendExceptions("Failed to create venv")
58
+ venv_path = self._ensure_venv(venv_path)
59
+ self._require_dependency_file()
94
60
 
95
- pyproject_path = self.get_dependency_file_path()
96
- if not pyproject_path.exists():
97
- debug_log(f"pyproject.toml file not found at {pyproject_path}", "error")
98
- print_error(f"pyproject.toml file not found at {pyproject_path}")
99
- raise BackendExceptions("pyproject.toml file not found")
100
-
101
- # Install dependencies using PDM
102
- with console.status("[bold green]Installing dependencies with PDM..."):
103
- subprocess.run(
104
- ["pdm", "install"],
105
- cwd=str(self.project_dir),
106
- check=True,
107
- capture_output=True,
108
- text=True,
109
- )
110
-
111
- debug_log("Dependencies installed successfully with PDM", "info")
112
- print_success("Dependencies installed successfully with PDM")
113
-
114
- except subprocess.CalledProcessError as e:
115
- debug_log(f"Error during dependency installation: {e.stderr}", "error")
116
- handle_exception(e, f"Error during dependency installation: {str(e)}")
117
- if hasattr(e, "stderr"):
118
- print_error(f"Error details: {e.stderr}")
119
- raise BackendExceptions("Failed to install dependencies with PDM")
120
- except OSError as e:
121
- debug_log(f"System error during dependency installation: {e}", "error")
122
- handle_exception(e, f"Error during dependency installation: {str(e)}")
123
- raise BackendExceptions(
124
- f"Failed to install dependencies with PDM: {str(e)}"
125
- )
61
+ self._run_checked(
62
+ ["pdm", "install"],
63
+ status_msg="Installing dependencies with PDM...",
64
+ error_prefix="Failed to install dependencies with PDM",
65
+ timeout=settings.get_subprocess_timeout("install"),
66
+ summarize_output=True,
67
+ )
68
+
69
+ debug_log("Dependencies installed successfully with PDM", "info")
70
+ print_success("Dependencies installed successfully with PDM")
126
71
 
127
72
  def generate_dependency_file(
128
73
  self,
@@ -205,6 +150,7 @@ packages = ["src"]
205
150
  check=True,
206
151
  capture_output=True,
207
152
  text=True,
153
+ timeout=settings.get_subprocess_timeout("install"),
208
154
  )
209
155
 
210
156
  debug_log(
@@ -212,7 +158,7 @@ packages = ["src"]
212
158
  "info",
213
159
  )
214
160
 
215
- except subprocess.CalledProcessError as e:
161
+ except (subprocess.CalledProcessError, subprocess.TimeoutExpired) as e:
216
162
  debug_log(f"Error adding dependency with PDM: {e}", "error")
217
163
  raise BackendExceptions(f"Failed to add dependency with PDM: {str(e)}")
218
164
  except OSError as e:
@@ -238,6 +184,7 @@ packages = ["src"]
238
184
  check=True,
239
185
  capture_output=True,
240
186
  text=True,
187
+ timeout=settings.get_subprocess_timeout(),
241
188
  )
242
189
 
243
190
  # Create or update pyproject.toml with provided metadata
@@ -271,7 +218,7 @@ packages = ["src"]
271
218
 
272
219
  debug_log(f"Initialized PDM project: {project_name}", "info")
273
220
 
274
- except subprocess.CalledProcessError as e:
221
+ except (subprocess.CalledProcessError, subprocess.TimeoutExpired) as e:
275
222
  debug_log(f"Error initializing PDM project: {e}", "error")
276
223
  raise BackendExceptions(f"Failed to initialize PDM project: {str(e)}")
277
224
  except (OSError, UnicodeEncodeError) as e:
@@ -3,19 +3,13 @@
3
3
  #
4
4
  # @author bnbong bbbong9@gmail.com
5
5
  # --------------------------------------------------------------------------
6
- import os
7
- import subprocess
8
6
  import sys
9
7
  from typing import List
10
8
 
11
9
  from fastapi_fastkit.core.exceptions import BackendExceptions
10
+ from fastapi_fastkit.core.settings import settings
12
11
  from fastapi_fastkit.utils.logging import debug_log, get_logger
13
- from fastapi_fastkit.utils.main import (
14
- console,
15
- handle_exception,
16
- print_error,
17
- print_success,
18
- )
12
+ from fastapi_fastkit.utils.main import print_success
19
13
 
20
14
  from .base import BasePackageManager
21
15
 
@@ -27,16 +21,7 @@ class PipManager(BasePackageManager):
27
21
 
28
22
  def is_available(self) -> bool:
29
23
  """Check if pip is available on the system."""
30
- try:
31
- subprocess.run(
32
- [sys.executable, "-m", "pip", "--version"],
33
- check=True,
34
- capture_output=True,
35
- text=True,
36
- )
37
- return True
38
- except (subprocess.CalledProcessError, FileNotFoundError):
39
- return False
24
+ return self._check_command_available([sys.executable, "-m", "pip", "--version"])
40
25
 
41
26
  def get_dependency_file_name(self) -> str:
42
27
  """Get the dependency file name for pip."""
@@ -51,87 +36,48 @@ class PipManager(BasePackageManager):
51
36
  """
52
37
  venv_path = str(self.project_dir / ".venv")
53
38
 
54
- try:
55
- with console.status("[bold green]Creating virtual environment..."):
56
- subprocess.run(
57
- [sys.executable, "-m", "venv", venv_path],
58
- check=True,
59
- capture_output=True,
60
- text=True,
61
- )
62
-
63
- debug_log(f"Virtual environment created at {venv_path}", "info")
64
- print_success("Virtual environment created successfully")
65
- return venv_path
39
+ self._run_checked(
40
+ [sys.executable, "-m", "venv", venv_path],
41
+ status_msg="Creating virtual environment...",
42
+ error_prefix="Failed to create venv",
43
+ timeout=settings.get_subprocess_timeout("venv"),
44
+ )
66
45
 
67
- except subprocess.CalledProcessError as e:
68
- debug_log(f"Error creating virtual environment: {e.stderr}", "error")
69
- handle_exception(e, f"Error creating virtual environment: {str(e)}")
70
- raise BackendExceptions("Failed to create venv")
71
- except OSError as e:
72
- debug_log(f"System error creating virtual environment: {e}", "error")
73
- handle_exception(e, f"Error creating virtual environment: {str(e)}")
74
- raise BackendExceptions(f"Failed to create venv: {str(e)}")
46
+ debug_log(f"Virtual environment created at {venv_path}", "info")
47
+ print_success("Virtual environment created successfully")
48
+ return venv_path
75
49
 
76
- def install_dependencies(self, venv_path: str) -> None:
50
+ def install_dependencies(self, venv_path: str, upgrade_pip: bool = False) -> None:
77
51
  """
78
52
  Install dependencies using pip in the virtual environment.
79
53
 
80
54
  :param venv_path: Path to the virtual environment
55
+ :param upgrade_pip: Upgrade pip inside the venv before installing
81
56
  :raises: BackendExceptions if dependency installation fails
82
57
  """
83
- try:
84
- if not os.path.exists(venv_path):
85
- debug_log(
86
- "Virtual environment does not exist. Creating it first.", "warning"
87
- )
88
- print_error("Virtual environment does not exist. Creating it first.")
89
- venv_path = self.create_virtual_environment()
90
- if not venv_path:
91
- raise BackendExceptions("Failed to create venv")
92
-
93
- requirements_path = self.get_dependency_file_path()
94
- if not requirements_path.exists():
95
- debug_log(
96
- f"Requirements file not found at {requirements_path}", "error"
97
- )
98
- print_error(f"Requirements file not found at {requirements_path}")
99
- raise BackendExceptions("Requirements file not found")
58
+ venv_path = self._ensure_venv(venv_path)
59
+ requirements_path = self._require_dependency_file("Requirements file not found")
100
60
 
101
- # Get pip path
102
- pip_path = self.get_executable_path("pip", venv_path)
61
+ pip_path = self.get_executable_path("pip", venv_path)
103
62
 
104
- # Upgrade pip first
105
- subprocess.run(
63
+ if upgrade_pip:
64
+ self._run_checked(
106
65
  [pip_path, "install", "--upgrade", "pip"],
107
- check=True,
108
- capture_output=True,
109
- text=True,
66
+ status_msg="Upgrading pip...",
67
+ error_prefix="Failed to upgrade pip",
68
+ timeout=settings.get_subprocess_timeout("install"),
110
69
  )
111
70
 
112
- # Install dependencies
113
- with console.status("[bold green]Installing dependencies..."):
114
- subprocess.run(
115
- [pip_path, "install", "-r", str(requirements_path.name)],
116
- cwd=str(self.project_dir),
117
- check=True,
118
- capture_output=True,
119
- text=True,
120
- )
71
+ self._run_checked(
72
+ [pip_path, "install", "-r", str(requirements_path.name)],
73
+ status_msg="Installing dependencies...",
74
+ error_prefix="Failed to install dependencies",
75
+ timeout=settings.get_subprocess_timeout("install"),
76
+ summarize_output=True,
77
+ )
121
78
 
122
- debug_log("Dependencies installed successfully", "info")
123
- print_success("Dependencies installed successfully")
124
-
125
- except subprocess.CalledProcessError as e:
126
- debug_log(f"Error during dependency installation: {e.stderr}", "error")
127
- handle_exception(e, f"Error during dependency installation: {str(e)}")
128
- if hasattr(e, "stderr"):
129
- print_error(f"Error details: {e.stderr}")
130
- raise BackendExceptions("Failed to install dependencies")
131
- except OSError as e:
132
- debug_log(f"System error during dependency installation: {e}", "error")
133
- handle_exception(e, f"Error during dependency installation: {str(e)}")
134
- raise BackendExceptions(f"Failed to install dependencies: {str(e)}")
79
+ debug_log("Dependencies installed successfully", "info")
80
+ print_success("Dependencies installed successfully")
135
81
 
136
82
  def generate_dependency_file(
137
83
  self,