FastAPI-fastkit 1.3.0__py3-none-any.whl → 1.4.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.
- fastapi_fastkit/__init__.py +1 -1
- fastapi_fastkit/backend/inspection/__init__.py +26 -0
- fastapi_fastkit/backend/inspection/checks.py +349 -0
- fastapi_fastkit/backend/inspection/consistency.py +125 -0
- fastapi_fastkit/backend/inspection/context.py +71 -0
- fastapi_fastkit/backend/inspection/core.py +351 -0
- fastapi_fastkit/backend/inspection/docker.py +320 -0
- fastapi_fastkit/backend/inspection/freshness.py +145 -0
- fastapi_fastkit/backend/inspection/fsutils.py +139 -0
- fastapi_fastkit/backend/inspection/lint.py +103 -0
- fastapi_fastkit/backend/inspection/report.py +58 -0
- fastapi_fastkit/backend/inspection/smoke.py +381 -0
- fastapi_fastkit/backend/inspection/strategies.py +367 -0
- fastapi_fastkit/backend/inspector.py +18 -1400
- fastapi_fastkit/backend/interactive/__init__.py +6 -0
- fastapi_fastkit/backend/interactive/config_builder.py +45 -63
- fastapi_fastkit/backend/interactive/prompts.py +155 -6
- fastapi_fastkit/backend/interactive/selectors.py +18 -2
- fastapi_fastkit/backend/main.py +887 -99
- fastapi_fastkit/backend/package_managers/base.py +172 -0
- fastapi_fastkit/backend/package_managers/factory.py +5 -4
- fastapi_fastkit/backend/package_managers/pdm_manager.py +29 -82
- fastapi_fastkit/backend/package_managers/pip_manager.py +31 -85
- fastapi_fastkit/backend/package_managers/poetry_manager.py +70 -110
- fastapi_fastkit/backend/package_managers/uv_manager.py +43 -112
- fastapi_fastkit/backend/project_builder/__init__.py +8 -1
- fastapi_fastkit/backend/project_builder/config_generator.py +806 -450
- fastapi_fastkit/backend/project_builder/config_schema.py +384 -0
- fastapi_fastkit/backend/project_builder/dependency_collector.py +105 -66
- fastapi_fastkit/backend/project_builder/preset_layout.py +254 -28
- fastapi_fastkit/backend/scaffolder.py +578 -0
- fastapi_fastkit/backend/transducer.py +154 -22
- fastapi_fastkit/cli.py +434 -311
- fastapi_fastkit/core/settings.py +234 -45
- fastapi_fastkit/fastapi_project_template/README.md +26 -10
- fastapi_fastkit/fastapi_project_template/fastapi-async-crud/README.md-tpl +3 -0
- fastapi_fastkit/fastapi_project_template/fastapi-async-crud/pyproject.toml-tpl +21 -22
- fastapi_fastkit/fastapi_project_template/fastapi-async-crud/requirements.txt-tpl +39 -36
- fastapi_fastkit/fastapi_project_template/fastapi-async-crud/src/api/api.py-tpl +3 -0
- fastapi_fastkit/fastapi_project_template/fastapi-async-crud/src/api/routes/items.py-tpl +1 -2
- fastapi_fastkit/fastapi_project_template/fastapi-async-crud/src/core/config.py-tpl +5 -6
- fastapi_fastkit/fastapi_project_template/fastapi-async-crud/src/crud/items.py-tpl +1 -2
- fastapi_fastkit/fastapi_project_template/fastapi-async-crud/src/main.py-tpl +9 -1
- fastapi_fastkit/fastapi_project_template/fastapi-async-crud/src/schemas/items.py-tpl +1 -2
- fastapi_fastkit/fastapi_project_template/fastapi-async-crud/tests/conftest.py-tpl +0 -1
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/.env-tpl +21 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/.gitignore-tpl +33 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/Dockerfile-tpl +18 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/README.md-tpl +210 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/alembic.ini-tpl +119 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/pyproject.toml-tpl +86 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/requirements.txt-tpl +23 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/scripts/format.sh-tpl +5 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/scripts/lint.sh-tpl +6 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/scripts/run-server.sh-tpl +8 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/scripts/test.sh-tpl +6 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/__init__.py-tpl +0 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/__init__.py-tpl +0 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/alembic/README-tpl +1 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/alembic/env.py-tpl +51 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/alembic/script.py.mako-tpl +26 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/alembic/versions/0001_initial_auth_schema.py-tpl +62 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/api/__init__.py-tpl +0 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/api/deps.py-tpl +91 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/api/health.py-tpl +11 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/api/router.py-tpl +17 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/core/__init__.py-tpl +0 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/core/config.py-tpl +64 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/core/security.py-tpl +100 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/db/__init__.py-tpl +0 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/db/session.py-tpl +36 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/domains/__init__.py-tpl +0 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/domains/auth/__init__.py-tpl +0 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/domains/auth/models.py-tpl +25 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/domains/auth/repository.py-tpl +41 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/domains/auth/router.py-tpl +82 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/domains/auth/schemas.py-tpl +14 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/domains/auth/service.py-tpl +65 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/domains/users/__init__.py-tpl +0 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/domains/users/models.py-tpl +35 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/domains/users/repository.py-tpl +33 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/domains/users/router.py-tpl +71 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/domains/users/schemas.py-tpl +29 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/domains/users/service.py-tpl +55 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/src/app/main.py-tpl +69 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/template-config.yml-tpl +16 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/tests/__init__.py-tpl +0 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/tests/conftest.py-tpl +73 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/tests/test_auth.py-tpl +201 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/tests/test_health.py-tpl +15 -0
- fastapi_fastkit/fastapi_project_template/fastapi-auth-jwt/tests/test_users.py-tpl +77 -0
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/pyproject.toml-tpl +21 -22
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/requirements.txt-tpl +39 -36
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/src/api/api.py-tpl +3 -0
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/src/api/routes/items.py-tpl +3 -3
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/src/core/config.py-tpl +5 -6
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/src/crud/items.py-tpl +1 -2
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/src/helper/pagination.py-tpl +8 -8
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/src/main.py-tpl +9 -0
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/src/schemas/base.py-tpl +1 -1
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/src/schemas/items.py-tpl +1 -2
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/tests/conftest.py-tpl +0 -1
- fastapi_fastkit/fastapi_project_template/fastapi-default/pyproject.toml-tpl +18 -19
- fastapi_fastkit/fastapi_project_template/fastapi-default/requirements.txt-tpl +37 -34
- fastapi_fastkit/fastapi_project_template/fastapi-default/src/api/api.py-tpl +3 -0
- fastapi_fastkit/fastapi_project_template/fastapi-default/src/api/routes/items.py-tpl +1 -2
- fastapi_fastkit/fastapi_project_template/fastapi-default/src/core/config.py-tpl +5 -6
- fastapi_fastkit/fastapi_project_template/fastapi-default/src/crud/items.py-tpl +1 -2
- fastapi_fastkit/fastapi_project_template/fastapi-default/src/main.py-tpl +9 -1
- fastapi_fastkit/fastapi_project_template/fastapi-default/src/schemas/items.py-tpl +1 -2
- fastapi_fastkit/fastapi_project_template/fastapi-default/tests/conftest.py-tpl +2 -3
- fastapi_fastkit/fastapi_project_template/fastapi-default/tests/test_items.py-tpl +0 -2
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/Dockerfile-tpl +1 -1
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/README.md-tpl +3 -0
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/pyproject.toml-tpl +21 -22
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/requirements.txt-tpl +41 -36
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/src/api/api.py-tpl +3 -0
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/src/api/routes/items.py-tpl +1 -2
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/src/core/config.py-tpl +5 -6
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/src/crud/items.py-tpl +1 -2
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/src/main.py-tpl +9 -1
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/src/schemas/items.py-tpl +1 -2
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/tests/conftest.py-tpl +2 -3
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/tests/test_items.py-tpl +0 -2
- fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/pyproject.toml-tpl +13 -14
- fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/requirements.txt-tpl +36 -10
- fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/src/app/api/router.py-tpl +3 -0
- fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/src/app/core/config.py-tpl +4 -4
- fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/src/app/db/memory.py-tpl +4 -3
- fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/src/app/domains/items/repository.py-tpl +4 -5
- fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/src/app/domains/items/router.py-tpl +2 -3
- fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/src/app/domains/items/service.py-tpl +2 -3
- fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/src/app/main.py-tpl +3 -0
- fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/tests/conftest.py-tpl +0 -1
- fastapi_fastkit/fastapi_project_template/fastapi-empty/pyproject.toml-tpl +14 -15
- fastapi_fastkit/fastapi_project_template/fastapi-empty/requirements.txt-tpl +36 -10
- fastapi_fastkit/fastapi_project_template/fastapi-empty/src/core/config.py-tpl +5 -6
- fastapi_fastkit/fastapi_project_template/fastapi-empty/src/main.py-tpl +3 -1
- fastapi_fastkit/fastapi_project_template/fastapi-empty/tests/conftest.py-tpl +0 -1
- fastapi_fastkit/fastapi_project_template/fastapi-empty/tests/test_main.py-tpl +0 -1
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/.env-tpl +15 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/.gitignore-tpl +31 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/Dockerfile-tpl +18 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/README.md-tpl +193 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/pyproject.toml-tpl +76 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/requirements.txt-tpl +15 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/scripts/chat.sh-tpl +10 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/scripts/format.sh-tpl +5 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/scripts/lint.sh-tpl +6 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/scripts/run-server.sh-tpl +8 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/scripts/test.sh-tpl +6 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/src/__init__.py-tpl +0 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/src/app/__init__.py-tpl +0 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/src/app/api/__init__.py-tpl +0 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/src/app/api/chat.py-tpl +62 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/src/app/api/deps.py-tpl +23 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/src/app/api/health.py-tpl +11 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/src/app/api/router.py-tpl +13 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/src/app/core/__init__.py-tpl +0 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/src/app/core/config.py-tpl +67 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/src/app/llm/__init__.py-tpl +0 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/src/app/llm/agent.py-tpl +136 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/src/app/llm/client.py-tpl +30 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/src/app/llm/tools.py-tpl +131 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/src/app/main.py-tpl +53 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/src/app/memory/__init__.py-tpl +0 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/src/app/memory/base.py-tpl +27 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/src/app/memory/in_memory.py-tpl +32 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/src/app/schemas/__init__.py-tpl +0 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/src/app/schemas/chat.py-tpl +46 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/template-config.yml-tpl +16 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/tests/__init__.py-tpl +0 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/tests/conftest.py-tpl +158 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/tests/test_chat.py-tpl +160 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/tests/test_health.py-tpl +12 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/tests/test_memory.py-tpl +38 -0
- fastapi_fastkit/fastapi_project_template/fastapi-llm-agent/tests/test_tools.py-tpl +37 -0
- fastapi_fastkit/fastapi_project_template/fastapi-mcp/.env-tpl +1 -1
- fastapi_fastkit/fastapi_project_template/fastapi-mcp/README.md-tpl +2 -2
- fastapi_fastkit/fastapi_project_template/fastapi-mcp/pyproject.toml-tpl +25 -26
- fastapi_fastkit/fastapi_project_template/fastapi-mcp/requirements.txt-tpl +65 -40
- fastapi_fastkit/fastapi_project_template/fastapi-mcp/setup.cfg-tpl +2 -3
- fastapi_fastkit/fastapi_project_template/fastapi-mcp/src/api/api.py-tpl +3 -0
- fastapi_fastkit/fastapi_project_template/fastapi-mcp/src/api/routes/auth.py-tpl +13 -14
- fastapi_fastkit/fastapi_project_template/fastapi-mcp/src/api/routes/items.py-tpl +3 -4
- fastapi_fastkit/fastapi_project_template/fastapi-mcp/src/auth/dependencies.py-tpl +6 -7
- fastapi_fastkit/fastapi_project_template/fastapi-mcp/src/core/config.py-tpl +5 -6
- fastapi_fastkit/fastapi_project_template/fastapi-mcp/src/main.py-tpl +4 -1
- fastapi_fastkit/fastapi_project_template/fastapi-mcp/src/{mcp → mcp_server}/router.py-tpl +3 -5
- fastapi_fastkit/fastapi_project_template/fastapi-mcp/src/schemas/items.py-tpl +6 -7
- fastapi_fastkit/fastapi_project_template/fastapi-mcp/tests/conftest.py-tpl +1 -1
- fastapi_fastkit/fastapi_project_template/fastapi-mcp/tests/test_auth.py-tpl +0 -1
- fastapi_fastkit/fastapi_project_template/fastapi-mcp/tests/test_items.py-tpl +0 -1
- fastapi_fastkit/fastapi_project_template/fastapi-mcp/tests/test_mcp.py-tpl +0 -1
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/.env-tpl +2 -0
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/Dockerfile-tpl +1 -1
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/pyproject.toml-tpl +22 -23
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/requirements.txt-tpl +44 -41
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/src/alembic/env.py-tpl +1 -3
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/src/alembic/versions/bedcdc35b64a_first_alembic.py-tpl +5 -6
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/src/api/api.py-tpl +2 -0
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/src/api/routes/items.py-tpl +2 -4
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/src/core/config.py-tpl +1 -2
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/src/core/db.py-tpl +1 -1
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/src/crud/items.py-tpl +4 -6
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/src/main.py-tpl +9 -0
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/src/schemas/items.py-tpl +4 -5
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/tests/conftest.py-tpl +1 -5
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/tests/test_items.py-tpl +0 -2
- fastapi_fastkit/fastapi_project_template/fastapi-single-module/.env-tpl +1 -0
- fastapi_fastkit/fastapi_project_template/fastapi-single-module/.gitignore-tpl +30 -0
- fastapi_fastkit/fastapi_project_template/fastapi-single-module/pyproject.toml-tpl +3 -4
- fastapi_fastkit/fastapi_project_template/fastapi-single-module/requirements.txt-tpl +26 -6
- fastapi_fastkit/fastapi_project_template/fastapi-single-module/src/main.py-tpl +3 -0
- fastapi_fastkit/fastapi_project_template/fastapi-single-module/tests/__init__.py-tpl +0 -0
- fastapi_fastkit/fastapi_project_template/fastapi-single-module/tests/conftest.py-tpl +0 -1
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/.env-tpl +7 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/.gitignore-tpl +35 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/Dockerfile-tpl +18 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/README.md-tpl +166 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/alembic.ini-tpl +44 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/pyproject.toml-tpl +82 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/requirements.txt-tpl +21 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/scripts/format.sh-tpl +5 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/scripts/lint.sh-tpl +6 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/scripts/migrate.sh-tpl +7 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/scripts/run-server.sh-tpl +8 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/scripts/test.sh-tpl +6 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/__init__.py-tpl +0 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/app/__init__.py-tpl +0 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/app/alembic/README-tpl +9 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/app/alembic/env.py-tpl +72 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/app/alembic/script.py.mako-tpl +27 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/app/alembic/versions/0001_create_items_table.py-tpl +38 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/app/api/__init__.py-tpl +0 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/app/api/health.py-tpl +11 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/app/api/router.py-tpl +15 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/app/core/__init__.py-tpl +0 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/app/core/config.py-tpl +62 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/app/crud/__init__.py-tpl +0 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/app/crud/base.py-tpl +62 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/app/crud/pagination.py-tpl +21 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/app/db/__init__.py-tpl +0 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/app/db/base.py-tpl +12 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/app/db/session.py-tpl +50 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/app/domains/__init__.py-tpl +0 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/app/domains/items/__init__.py-tpl +0 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/app/domains/items/crud.py-tpl +26 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/app/domains/items/models.py-tpl +70 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/app/domains/items/router.py-tpl +69 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/src/app/main.py-tpl +43 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/template-config.yml-tpl +16 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/tests/__init__.py-tpl +0 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/tests/conftest.py-tpl +60 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/tests/test_crud_base.py-tpl +43 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/tests/test_health.py-tpl +12 -0
- fastapi_fastkit/fastapi_project_template/fastapi-sqlmodel/tests/test_items.py-tpl +110 -0
- fastapi_fastkit/fastapi_project_template/modules/api/routes/new_route.py-tpl +2 -2
- fastapi_fastkit/fragments/README.md +90 -0
- fastapi_fastkit/fragments/auth/fastapi_users.py.j2 +8 -0
- fastapi_fastkit/fragments/auth/jwt.py.j2 +44 -0
- fastapi_fastkit/fragments/auth/oauth2.py.j2 +105 -0
- fastapi_fastkit/fragments/auth/session.py.j2 +67 -0
- fastapi_fastkit/fragments/db/mongodb.py.j2 +19 -0
- fastapi_fastkit/fragments/db/redis.py.j2 +26 -0
- fastapi_fastkit/fragments/db/sqlalchemy.py.j2 +45 -0
- fastapi_fastkit/fragments/docker/docker_compose.j2 +84 -0
- fastapi_fastkit/fragments/docker/dockerfile.j2 +19 -0
- fastapi_fastkit/fragments/features/cache.py.j2 +26 -0
- fastapi_fastkit/fragments/features/pagination.py.j2 +31 -0
- fastapi_fastkit/fragments/features/tasks_celery.py.j2 +50 -0
- fastapi_fastkit/fragments/features/tasks_dramatiq.py.j2 +59 -0
- fastapi_fastkit/fragments/features/websocket.py.j2 +66 -0
- fastapi_fastkit/fragments/header.j2 +4 -0
- fastapi_fastkit/fragments/logging/structured.py.j2 +88 -0
- fastapi_fastkit/fragments/main/imports/async_tasks.j2 +1 -0
- fastapi_fastkit/fragments/main/imports/caching.j2 +5 -0
- fastapi_fastkit/fragments/main/imports/fastapi_users.j2 +1 -0
- fastapi_fastkit/fragments/main/imports/jwt.j2 +1 -0
- fastapi_fastkit/fragments/main/imports/loguru.j2 +1 -0
- fastapi_fastkit/fragments/main/imports/mongodb.j2 +1 -0
- fastapi_fastkit/fragments/main/imports/oauth2.j2 +4 -0
- fastapi_fastkit/fragments/main/imports/opentelemetry.j2 +6 -0
- fastapi_fastkit/fragments/main/imports/pagination.j2 +3 -0
- fastapi_fastkit/fragments/main/imports/prometheus.j2 +1 -0
- fastapi_fastkit/fragments/main/imports/rate_limiting.j2 +3 -0
- fastapi_fastkit/fragments/main/imports/session_auth.j2 +4 -0
- fastapi_fastkit/fragments/main/imports/sqlalchemy.j2 +2 -0
- fastapi_fastkit/fragments/main/imports/structured_logging.j2 +1 -0
- fastapi_fastkit/fragments/main/imports/websocket.j2 +1 -0
- fastapi_fastkit/fragments/main/lifespan/caching.j2 +7 -0
- fastapi_fastkit/fragments/main/lifespan/mongodb.j2 +2 -0
- fastapi_fastkit/fragments/main/lifespan/prometheus.j2 +2 -0
- fastapi_fastkit/fragments/main/lifespan/sqlalchemy.j2 +2 -0
- fastapi_fastkit/fragments/main/setup/async_tasks.j2 +1 -0
- fastapi_fastkit/fragments/main/setup/caching.j2 +1 -0
- fastapi_fastkit/fragments/main/setup/cors.j2 +8 -0
- fastapi_fastkit/fragments/main/setup/oauth2.j2 +3 -0
- fastapi_fastkit/fragments/main/setup/opentelemetry.j2 +19 -0
- fastapi_fastkit/fragments/main/setup/pagination.j2 +2 -0
- fastapi_fastkit/fragments/main/setup/rate_limiting.j2 +3 -0
- fastapi_fastkit/fragments/main/setup/session_auth.j2 +6 -0
- fastapi_fastkit/fragments/main/setup/structured_logging.j2 +2 -0
- fastapi_fastkit/fragments/main/setup/websocket.j2 +1 -0
- fastapi_fastkit/fragments/main.py.j2 +57 -0
- fastapi_fastkit/fragments/migrations/alembic_ini.j2 +42 -0
- fastapi_fastkit/fragments/migrations/env.py.j2 +76 -0
- fastapi_fastkit/fragments/migrations/initial_revision.py.j2 +27 -0
- fastapi_fastkit/fragments/migrations/migrate_sh.j2 +34 -0
- fastapi_fastkit/fragments/migrations/script.py.mako.j2 +26 -0
- fastapi_fastkit/fragments/tasks/celery_worker.py.j2 +49 -0
- fastapi_fastkit/fragments/tasks/dramatiq_worker.py.j2 +41 -0
- fastapi_fastkit/fragments/test/factories.py.j2 +68 -0
- fastapi_fastkit/fragments/test/pytest_ini.j2 +16 -0
- fastapi_fastkit/fragments/test/test_factories.py.j2 +27 -0
- fastapi_fastkit/fragments/test/test_main.py.j2 +46 -0
- fastapi_fastkit/fragments/tooling/devcontainer.j2 +26 -0
- fastapi_fastkit/fragments/tooling/github_actions.j2 +37 -0
- fastapi_fastkit/fragments/tooling/makefile.j2 +40 -0
- fastapi_fastkit/fragments/tooling/pre_commit.j2 +36 -0
- fastapi_fastkit/fragments/tooling/ruff_toml.j2 +16 -0
- fastapi_fastkit/utils/config_file.py +263 -0
- fastapi_fastkit/utils/main.py +38 -0
- {fastapi_fastkit-1.3.0.dist-info → fastapi_fastkit-1.4.1.dist-info}/METADATA +49 -6
- fastapi_fastkit-1.4.1.dist-info/RECORD +484 -0
- {fastapi_fastkit-1.3.0.dist-info → fastapi_fastkit-1.4.1.dist-info}/WHEEL +1 -1
- fastapi_fastkit/fastapi_project_template/fastapi-async-crud/setup.py-tpl +0 -25
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/setup.py-tpl +0 -27
- fastapi_fastkit/fastapi_project_template/fastapi-default/setup.py-tpl +0 -22
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/setup.py-tpl +0 -22
- fastapi_fastkit/fastapi_project_template/fastapi-empty/setup.py-tpl +0 -22
- fastapi_fastkit/fastapi_project_template/fastapi-mcp/setup.py-tpl +0 -51
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/setup.py-tpl +0 -25
- fastapi_fastkit/fastapi_project_template/fastapi-single-module/setup.py-tpl +0 -17
- fastapi_fastkit-1.3.0.dist-info/RECORD +0 -287
- /fastapi_fastkit/fastapi_project_template/fastapi-mcp/src/{mcp → mcp_server}/__init__.py-tpl +0 -0
- {fastapi_fastkit-1.3.0.dist-info → fastapi_fastkit-1.4.1.dist-info}/entry_points.txt +0 -0
- {fastapi_fastkit-1.3.0.dist-info → fastapi_fastkit-1.4.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# --------------------------------------------------------------------------
|
|
2
|
+
# Dependency freshness reporting.
|
|
3
|
+
#
|
|
4
|
+
# Each pinned dependency is compared against the latest release published on
|
|
5
|
+
# PyPI. Findings are warnings only - a template lagging behind upstream is
|
|
6
|
+
# worth surfacing but never a reason to fail an inspection. Network failures
|
|
7
|
+
# are skipped silently, and ``--offline`` disables the lookups entirely.
|
|
8
|
+
#
|
|
9
|
+
# @author bnbong
|
|
10
|
+
# --------------------------------------------------------------------------
|
|
11
|
+
import json
|
|
12
|
+
import re
|
|
13
|
+
import tomllib
|
|
14
|
+
import urllib.error
|
|
15
|
+
import urllib.request
|
|
16
|
+
from typing import Dict, Optional, Tuple
|
|
17
|
+
|
|
18
|
+
from fastapi_fastkit.backend.package_managers.poetry_manager import (
|
|
19
|
+
_parse_pip_requirement,
|
|
20
|
+
)
|
|
21
|
+
from fastapi_fastkit.utils.logging import debug_log
|
|
22
|
+
|
|
23
|
+
from .context import InspectionContext
|
|
24
|
+
|
|
25
|
+
PYPI_JSON_URL = "https://pypi.org/pypi/{package}/json"
|
|
26
|
+
PYPI_TIMEOUT = 5
|
|
27
|
+
_VERSION_RE = re.compile(r"(\d+)\.(\d+)")
|
|
28
|
+
#: Only exact/lower-bound pins carry a meaningful "current version".
|
|
29
|
+
_PIN_OPERATORS = ("==", ">=", "~=")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _pinned_version(requirement: str) -> Tuple[str, str]:
|
|
33
|
+
"""Return ``(package name, pinned version)`` for a requirement string.
|
|
34
|
+
|
|
35
|
+
Version is empty when the requirement is unpinned or uses an operator
|
|
36
|
+
that does not identify a concrete current version (e.g. ``<2``).
|
|
37
|
+
"""
|
|
38
|
+
name, _extras, version_spec, _marker = _parse_pip_requirement(requirement)
|
|
39
|
+
if not name or not version_spec:
|
|
40
|
+
return name.lower() if name else "", ""
|
|
41
|
+
for operator in _PIN_OPERATORS:
|
|
42
|
+
if version_spec.startswith(operator):
|
|
43
|
+
return name.lower(), version_spec[len(operator) :].strip()
|
|
44
|
+
return name.lower(), ""
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _parse_version(version: str) -> Optional[Tuple[int, int]]:
|
|
48
|
+
"""Extract a (major, minor) pair from a version string."""
|
|
49
|
+
match = _VERSION_RE.match(version.strip())
|
|
50
|
+
if not match:
|
|
51
|
+
return None
|
|
52
|
+
return int(match.group(1)), int(match.group(2))
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def fetch_latest_version(package: str) -> Optional[str]:
|
|
56
|
+
"""Return the latest version of ``package`` on PyPI, or ``None`` on failure."""
|
|
57
|
+
url = PYPI_JSON_URL.format(package=package)
|
|
58
|
+
try:
|
|
59
|
+
with urllib.request.urlopen(url, timeout=PYPI_TIMEOUT) as response:
|
|
60
|
+
payload = json.loads(response.read().decode("utf-8"))
|
|
61
|
+
except (urllib.error.URLError, OSError, ValueError, TimeoutError) as e:
|
|
62
|
+
debug_log(f"PyPI lookup failed for {package}: {e}", "debug")
|
|
63
|
+
return None
|
|
64
|
+
version = payload.get("info", {}).get("version")
|
|
65
|
+
return str(version) if version else None
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _collect_pins(ctx: InspectionContext) -> Dict[str, str]:
|
|
69
|
+
"""Map package name -> pinned version from the template's metadata files."""
|
|
70
|
+
pins: Dict[str, str] = {}
|
|
71
|
+
|
|
72
|
+
requirements_path = ctx.template_path / "requirements.txt-tpl"
|
|
73
|
+
if requirements_path.exists():
|
|
74
|
+
try:
|
|
75
|
+
with open(requirements_path, encoding="utf-8") as f:
|
|
76
|
+
lines = f.read().splitlines()
|
|
77
|
+
except (OSError, UnicodeDecodeError):
|
|
78
|
+
lines = []
|
|
79
|
+
for line in lines:
|
|
80
|
+
line = line.strip()
|
|
81
|
+
if not line or line.startswith("#"):
|
|
82
|
+
continue
|
|
83
|
+
name, version = _pinned_version(line)
|
|
84
|
+
if name and version:
|
|
85
|
+
pins.setdefault(name, version)
|
|
86
|
+
|
|
87
|
+
pyproject_path = ctx.template_path / "pyproject.toml-tpl"
|
|
88
|
+
if pyproject_path.exists():
|
|
89
|
+
try:
|
|
90
|
+
with open(pyproject_path, "rb") as f:
|
|
91
|
+
data = tomllib.load(f)
|
|
92
|
+
except (OSError, ValueError):
|
|
93
|
+
data = {}
|
|
94
|
+
for dep in data.get("project", {}).get("dependencies", []) or []:
|
|
95
|
+
if not isinstance(dep, str):
|
|
96
|
+
continue
|
|
97
|
+
name, version = _pinned_version(dep)
|
|
98
|
+
if name and version:
|
|
99
|
+
pins.setdefault(name, version)
|
|
100
|
+
|
|
101
|
+
return pins
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def check_dependency_freshness(ctx: InspectionContext) -> bool:
|
|
105
|
+
"""Warn about dependencies that lag behind their latest PyPI release.
|
|
106
|
+
|
|
107
|
+
Always returns ``True``: freshness is advisory.
|
|
108
|
+
"""
|
|
109
|
+
if ctx.options.offline:
|
|
110
|
+
debug_log("Offline mode - skipping dependency freshness check", "info")
|
|
111
|
+
return True
|
|
112
|
+
|
|
113
|
+
pins = _collect_pins(ctx)
|
|
114
|
+
if not pins:
|
|
115
|
+
debug_log("No pinned dependencies found for freshness check", "info")
|
|
116
|
+
return True
|
|
117
|
+
|
|
118
|
+
checked = 0
|
|
119
|
+
for package, pinned in sorted(pins.items()):
|
|
120
|
+
latest = fetch_latest_version(package)
|
|
121
|
+
if latest is None:
|
|
122
|
+
continue
|
|
123
|
+
checked += 1
|
|
124
|
+
current_parts = _parse_version(pinned)
|
|
125
|
+
latest_parts = _parse_version(latest)
|
|
126
|
+
if current_parts is None or latest_parts is None:
|
|
127
|
+
continue
|
|
128
|
+
if latest_parts[0] > current_parts[0]:
|
|
129
|
+
ctx.add_warning(
|
|
130
|
+
f"{package} is {latest_parts[0] - current_parts[0]} major "
|
|
131
|
+
f"version(s) behind: pinned {pinned}, latest {latest}"
|
|
132
|
+
)
|
|
133
|
+
elif latest_parts[0] == current_parts[0] and latest_parts[1] > current_parts[1]:
|
|
134
|
+
ctx.add_warning(
|
|
135
|
+
f"{package} is {latest_parts[1] - current_parts[1]} minor "
|
|
136
|
+
f"version(s) behind: pinned {pinned}, latest {latest}"
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
if checked == 0:
|
|
140
|
+
debug_log(
|
|
141
|
+
"Dependency freshness check skipped - no PyPI responses received", "info"
|
|
142
|
+
)
|
|
143
|
+
else:
|
|
144
|
+
debug_log(f"Dependency freshness check completed ({checked} packages)", "info")
|
|
145
|
+
return True
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# --------------------------------------------------------------------------
|
|
2
|
+
# Filesystem helpers shared by the inspection pipeline: shell script line
|
|
3
|
+
# ending normalisation and best-effort removal of temporary directories that
|
|
4
|
+
# Docker or a test run may have left behind with awkward permissions.
|
|
5
|
+
#
|
|
6
|
+
# @author bnbong
|
|
7
|
+
# --------------------------------------------------------------------------
|
|
8
|
+
import os
|
|
9
|
+
import shutil
|
|
10
|
+
import stat
|
|
11
|
+
import subprocess
|
|
12
|
+
import time
|
|
13
|
+
|
|
14
|
+
from fastapi_fastkit.utils.logging import debug_log
|
|
15
|
+
|
|
16
|
+
CLEANUP_MAX_RETRIES = 5
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def fix_script_line_endings(script_path: str) -> None:
|
|
20
|
+
"""Convert Windows line endings in a script to Unix ones."""
|
|
21
|
+
try:
|
|
22
|
+
with open(script_path, "rb") as f:
|
|
23
|
+
content = f.read()
|
|
24
|
+
|
|
25
|
+
content = content.replace(b"\r\n", b"\n").replace(b"\r", b"\n")
|
|
26
|
+
|
|
27
|
+
with open(script_path, "wb") as f:
|
|
28
|
+
f.write(content)
|
|
29
|
+
|
|
30
|
+
debug_log(f"Fixed line endings in {script_path}", "debug")
|
|
31
|
+
except OSError as e:
|
|
32
|
+
debug_log(f"Failed to fix line endings in {script_path}: {e}", "warning")
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def fix_all_script_line_endings(directory: str) -> None:
|
|
36
|
+
"""Normalise line endings for every shell script below ``directory``."""
|
|
37
|
+
for root, _dirs, files in os.walk(directory):
|
|
38
|
+
for file_name in files:
|
|
39
|
+
if file_name.endswith((".sh", ".bash")):
|
|
40
|
+
fix_script_line_endings(os.path.join(root, file_name))
|
|
41
|
+
|
|
42
|
+
debug_log("Fixed line endings for all shell scripts", "info")
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _fix_permissions(path: str) -> None:
|
|
46
|
+
"""Grant full permissions to ``path``, ignoring failures."""
|
|
47
|
+
try:
|
|
48
|
+
os.chmod(path, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO)
|
|
49
|
+
except OSError:
|
|
50
|
+
pass
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def fix_directory_permissions(directory_path: str) -> None:
|
|
54
|
+
"""Make a directory tree writable so it can be removed."""
|
|
55
|
+
_fix_permissions(directory_path)
|
|
56
|
+
for root, dirs, files in os.walk(directory_path):
|
|
57
|
+
_fix_permissions(root)
|
|
58
|
+
for name in dirs + files:
|
|
59
|
+
_fix_permissions(os.path.join(root, name))
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def remove_directory_contents(directory_path: str) -> None:
|
|
63
|
+
"""Remove a directory's contents entry by entry, ignoring failures."""
|
|
64
|
+
for root, dirs, files in os.walk(directory_path, topdown=False):
|
|
65
|
+
for file_name in files:
|
|
66
|
+
file_path = os.path.join(root, file_name)
|
|
67
|
+
_fix_permissions(file_path)
|
|
68
|
+
try:
|
|
69
|
+
os.remove(file_path)
|
|
70
|
+
except OSError:
|
|
71
|
+
pass
|
|
72
|
+
|
|
73
|
+
for dir_name in dirs:
|
|
74
|
+
dir_path = os.path.join(root, dir_name)
|
|
75
|
+
_fix_permissions(dir_path)
|
|
76
|
+
try:
|
|
77
|
+
os.rmdir(dir_path)
|
|
78
|
+
except OSError:
|
|
79
|
+
pass
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def force_cleanup_directory(directory_path: str) -> None:
|
|
83
|
+
"""Remove ``directory_path`` using progressively more forceful strategies."""
|
|
84
|
+
for attempt in range(CLEANUP_MAX_RETRIES):
|
|
85
|
+
try:
|
|
86
|
+
shutil.rmtree(directory_path)
|
|
87
|
+
debug_log(
|
|
88
|
+
f"Successfully cleaned up temp directory: {directory_path}", "info"
|
|
89
|
+
)
|
|
90
|
+
return
|
|
91
|
+
except (OSError, PermissionError) as e:
|
|
92
|
+
if attempt == CLEANUP_MAX_RETRIES - 1:
|
|
93
|
+
break
|
|
94
|
+
debug_log(
|
|
95
|
+
f"Attempt {attempt + 1} failed to cleanup {directory_path}: {e}",
|
|
96
|
+
"warning",
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
try:
|
|
100
|
+
fix_directory_permissions(directory_path)
|
|
101
|
+
shutil.rmtree(directory_path)
|
|
102
|
+
debug_log(
|
|
103
|
+
"Successfully cleaned up temp directory after permission fix: "
|
|
104
|
+
f"{directory_path}",
|
|
105
|
+
"info",
|
|
106
|
+
)
|
|
107
|
+
return
|
|
108
|
+
except OSError:
|
|
109
|
+
pass
|
|
110
|
+
|
|
111
|
+
try:
|
|
112
|
+
remove_directory_contents(directory_path)
|
|
113
|
+
os.rmdir(directory_path)
|
|
114
|
+
debug_log(
|
|
115
|
+
"Successfully cleaned up temp directory by removing contents: "
|
|
116
|
+
f"{directory_path}",
|
|
117
|
+
"info",
|
|
118
|
+
)
|
|
119
|
+
return
|
|
120
|
+
except OSError:
|
|
121
|
+
pass
|
|
122
|
+
|
|
123
|
+
time.sleep(2)
|
|
124
|
+
|
|
125
|
+
debug_log(
|
|
126
|
+
f"Failed to cleanup temp directory after {CLEANUP_MAX_RETRIES} attempts: "
|
|
127
|
+
f"{directory_path}",
|
|
128
|
+
"warning",
|
|
129
|
+
)
|
|
130
|
+
try:
|
|
131
|
+
if os.name == "nt": # pragma: no cover - Windows-only branch
|
|
132
|
+
subprocess.run(
|
|
133
|
+
["rmdir", "/s", "/q", directory_path], check=False, shell=True
|
|
134
|
+
)
|
|
135
|
+
else:
|
|
136
|
+
subprocess.run(["rm", "-rf", directory_path], check=False)
|
|
137
|
+
debug_log(f"Force cleanup completed for: {directory_path}", "info")
|
|
138
|
+
except OSError:
|
|
139
|
+
debug_log(f"All cleanup attempts failed for: {directory_path}", "warning")
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# --------------------------------------------------------------------------
|
|
2
|
+
# Static analysis of the generated project.
|
|
3
|
+
#
|
|
4
|
+
# The compile check is mandatory - a template that cannot be compiled is
|
|
5
|
+
# broken beyond argument. It is performed in-process with ``compile()`` and
|
|
6
|
+
# deliberately writes nothing to disk: a Docker test run beforehand can leave
|
|
7
|
+
# root-owned ``__pycache__`` directories in the bind-mounted project, and
|
|
8
|
+
# ``compileall`` would then fail with a PermissionError that says nothing
|
|
9
|
+
# about the template. ``mypy`` is opt-in because it needs the template's own
|
|
10
|
+
# dependencies installed and is far slower.
|
|
11
|
+
#
|
|
12
|
+
# @author bnbong
|
|
13
|
+
# --------------------------------------------------------------------------
|
|
14
|
+
import os
|
|
15
|
+
import subprocess
|
|
16
|
+
import sys
|
|
17
|
+
from typing import List
|
|
18
|
+
|
|
19
|
+
from fastapi_fastkit.utils.logging import debug_log
|
|
20
|
+
|
|
21
|
+
from .context import InspectionContext
|
|
22
|
+
|
|
23
|
+
MYPY_TIMEOUT = 300
|
|
24
|
+
|
|
25
|
+
#: Directories never worth compiling - third party code and build artefacts.
|
|
26
|
+
EXCLUDED_DIRS = {".venv", "venv", "__pycache__", "node_modules"}
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _interpreter(ctx: InspectionContext) -> str:
|
|
30
|
+
"""Prefer the inspection venv interpreter, falling back to the host one."""
|
|
31
|
+
return ctx.python_executable() or sys.executable
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _iter_python_files(root: str) -> List[str]:
|
|
35
|
+
"""Collect every ``.py`` file below ``root``, skipping excluded directories."""
|
|
36
|
+
python_files: List[str] = []
|
|
37
|
+
for dirpath, dirnames, filenames in os.walk(root):
|
|
38
|
+
dirnames[:] = [name for name in dirnames if name not in EXCLUDED_DIRS]
|
|
39
|
+
for file_name in sorted(filenames):
|
|
40
|
+
if file_name.endswith(".py"):
|
|
41
|
+
python_files.append(os.path.join(dirpath, file_name))
|
|
42
|
+
return python_files
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def check_compileall(ctx: InspectionContext) -> bool:
|
|
46
|
+
"""Compile every Python file of the generated project, in memory.
|
|
47
|
+
|
|
48
|
+
Nothing is written to disk - no ``__pycache__`` is created - so the check
|
|
49
|
+
stays a pure syntax gate that cannot trip over file ownership left behind
|
|
50
|
+
by a containerised test run.
|
|
51
|
+
"""
|
|
52
|
+
failures: List[str] = []
|
|
53
|
+
|
|
54
|
+
for path in _iter_python_files(ctx.temp_dir):
|
|
55
|
+
try:
|
|
56
|
+
with open(path, "rb") as f:
|
|
57
|
+
source = f.read()
|
|
58
|
+
except OSError as e:
|
|
59
|
+
failures.append(f"{path}: could not be read ({e})")
|
|
60
|
+
continue
|
|
61
|
+
|
|
62
|
+
try:
|
|
63
|
+
compile(source, path, "exec", dont_inherit=True)
|
|
64
|
+
except (SyntaxError, ValueError) as e:
|
|
65
|
+
failures.append(f"{path}: {e}")
|
|
66
|
+
|
|
67
|
+
if failures:
|
|
68
|
+
detail = "\n".join(failures)
|
|
69
|
+
ctx.add_error(f"Generated project failed to compile:\n{detail}")
|
|
70
|
+
return False
|
|
71
|
+
|
|
72
|
+
debug_log("compileall check passed", "info")
|
|
73
|
+
return True
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def check_mypy(ctx: InspectionContext) -> bool:
|
|
77
|
+
"""Run mypy inside the generated project (opt-in via ``--mypy``)."""
|
|
78
|
+
if not ctx.options.run_mypy:
|
|
79
|
+
debug_log("mypy check disabled", "info")
|
|
80
|
+
return True
|
|
81
|
+
|
|
82
|
+
try:
|
|
83
|
+
result = subprocess.run(
|
|
84
|
+
[_interpreter(ctx), "-m", "mypy", "."],
|
|
85
|
+
cwd=ctx.temp_dir,
|
|
86
|
+
capture_output=True,
|
|
87
|
+
text=True,
|
|
88
|
+
timeout=MYPY_TIMEOUT,
|
|
89
|
+
)
|
|
90
|
+
except subprocess.TimeoutExpired:
|
|
91
|
+
ctx.add_error("mypy timed out")
|
|
92
|
+
return False
|
|
93
|
+
except OSError as e:
|
|
94
|
+
ctx.add_warning(f"Could not run mypy: {e}")
|
|
95
|
+
return True
|
|
96
|
+
|
|
97
|
+
if result.returncode != 0:
|
|
98
|
+
detail = (result.stdout or result.stderr or "").strip()
|
|
99
|
+
ctx.add_error(f"mypy reported errors in the generated project:\n{detail}")
|
|
100
|
+
return False
|
|
101
|
+
|
|
102
|
+
debug_log("mypy check passed", "info")
|
|
103
|
+
return True
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# --------------------------------------------------------------------------
|
|
2
|
+
# Inspection reporting: turn an InspectionContext into the report dictionary
|
|
3
|
+
# consumed by the CLI, the local scripts and the CI workflows.
|
|
4
|
+
#
|
|
5
|
+
# @author bnbong
|
|
6
|
+
# --------------------------------------------------------------------------
|
|
7
|
+
from typing import Any, Dict
|
|
8
|
+
|
|
9
|
+
from fastapi_fastkit.utils.logging import debug_log
|
|
10
|
+
from fastapi_fastkit.utils.main import print_error, print_success, print_warning
|
|
11
|
+
|
|
12
|
+
from .context import InspectionContext
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def build_report(ctx: InspectionContext) -> Dict[str, Any]:
|
|
16
|
+
"""Build the inspection report and log a summary of the findings."""
|
|
17
|
+
is_valid = len(ctx.errors) == 0
|
|
18
|
+
template_name = ctx.template_path.name
|
|
19
|
+
|
|
20
|
+
if is_valid:
|
|
21
|
+
debug_log(
|
|
22
|
+
f"Template inspection completed successfully for {template_name}", "info"
|
|
23
|
+
)
|
|
24
|
+
else:
|
|
25
|
+
debug_log(
|
|
26
|
+
f"Template inspection failed for {template_name} "
|
|
27
|
+
f"with {len(ctx.errors)} errors",
|
|
28
|
+
"error",
|
|
29
|
+
)
|
|
30
|
+
for index, error in enumerate(ctx.errors, 1):
|
|
31
|
+
debug_log(f"Error {index}: {error}", "error")
|
|
32
|
+
|
|
33
|
+
if ctx.warnings:
|
|
34
|
+
debug_log(
|
|
35
|
+
f"Template {template_name} has {len(ctx.warnings)} warnings: "
|
|
36
|
+
f"{ctx.warnings}",
|
|
37
|
+
"warning",
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
"template_path": str(ctx.template_path),
|
|
42
|
+
"errors": list(ctx.errors),
|
|
43
|
+
"warnings": list(ctx.warnings),
|
|
44
|
+
"is_valid": is_valid,
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def print_report(ctx: InspectionContext, is_valid: bool) -> None:
|
|
49
|
+
"""Print the human-facing inspection outcome."""
|
|
50
|
+
if is_valid:
|
|
51
|
+
print_success(f"Template {ctx.template_path} is valid!")
|
|
52
|
+
else:
|
|
53
|
+
print_error(f"Template {ctx.template_path} validation failed")
|
|
54
|
+
for error in ctx.errors:
|
|
55
|
+
print_error(f" - {error}")
|
|
56
|
+
|
|
57
|
+
for warning in ctx.warnings:
|
|
58
|
+
print_warning(f" - {warning}")
|