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
fastapi_fastkit/__init__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = 'v1.
|
|
1
|
+
__version__ = 'v1.4.1'
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# --------------------------------------------------------------------------
|
|
2
|
+
# Template inspection package.
|
|
3
|
+
#
|
|
4
|
+
# ``TemplateInspector`` generates a throwaway project from a template and runs
|
|
5
|
+
# the checks defined in the sibling modules against it:
|
|
6
|
+
#
|
|
7
|
+
# - ``checks`` : structure, extensions, dependencies, implementation, tests
|
|
8
|
+
# - ``consistency`` : Python-version pins and dependency drift
|
|
9
|
+
# - ``lint`` : compileall (required) and mypy (opt-in)
|
|
10
|
+
# - ``smoke`` : boots the generated app and probes /docs and /health
|
|
11
|
+
# - ``strategies`` : how the template's own test suite is executed
|
|
12
|
+
# - ``docker`` : Docker Compose orchestration
|
|
13
|
+
# - ``freshness`` : PyPI version-lag warnings
|
|
14
|
+
# - ``report`` : report construction and printing
|
|
15
|
+
#
|
|
16
|
+
# @author bnbong
|
|
17
|
+
# --------------------------------------------------------------------------
|
|
18
|
+
from .context import InspectionContext, InspectionOptions
|
|
19
|
+
from .core import TemplateInspector, inspect_fastapi_template
|
|
20
|
+
|
|
21
|
+
__all__ = [
|
|
22
|
+
"InspectionContext",
|
|
23
|
+
"InspectionOptions",
|
|
24
|
+
"TemplateInspector",
|
|
25
|
+
"inspect_fastapi_template",
|
|
26
|
+
]
|
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
# --------------------------------------------------------------------------
|
|
2
|
+
# Static template checks: structure, file extensions, declared dependencies,
|
|
3
|
+
# FastAPI implementation and the mandatory test suite.
|
|
4
|
+
#
|
|
5
|
+
# Every function takes an ``InspectionContext`` and returns ``True`` when the
|
|
6
|
+
# check passes, appending a descriptive message to ``ctx.errors`` otherwise.
|
|
7
|
+
#
|
|
8
|
+
# @author bnbong
|
|
9
|
+
# --------------------------------------------------------------------------
|
|
10
|
+
import ast
|
|
11
|
+
import os
|
|
12
|
+
import tomllib
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
from typing import List, Optional, Set, Tuple
|
|
15
|
+
|
|
16
|
+
from fastapi_fastkit.backend.main import (
|
|
17
|
+
_parse_setup_dependencies,
|
|
18
|
+
find_template_core_modules,
|
|
19
|
+
)
|
|
20
|
+
from fastapi_fastkit.backend.package_managers.poetry_manager import (
|
|
21
|
+
_parse_pip_requirement,
|
|
22
|
+
)
|
|
23
|
+
from fastapi_fastkit.utils.logging import debug_log
|
|
24
|
+
|
|
25
|
+
from .context import InspectionContext
|
|
26
|
+
|
|
27
|
+
ALWAYS_REQUIRED_PATHS = ["tests", "README.md-tpl"]
|
|
28
|
+
METADATA_CANDIDATES = ["pyproject.toml-tpl", "setup.py-tpl"]
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def extract_pyproject_dependency_names(
|
|
32
|
+
pyproject_path: Path,
|
|
33
|
+
) -> Tuple[Set[str], Optional[str]]:
|
|
34
|
+
"""Parse a pyproject file and return (lowercased dep names, error).
|
|
35
|
+
|
|
36
|
+
``error`` is ``None`` on success. Returns an empty set with a descriptive
|
|
37
|
+
error string when the file is unreadable or malformed.
|
|
38
|
+
"""
|
|
39
|
+
try:
|
|
40
|
+
with open(pyproject_path, "rb") as f:
|
|
41
|
+
data = tomllib.load(f)
|
|
42
|
+
except (OSError, tomllib.TOMLDecodeError, UnicodeDecodeError) as e:
|
|
43
|
+
return set(), f"Invalid pyproject.toml-tpl: {e}"
|
|
44
|
+
|
|
45
|
+
project = data.get("project", {})
|
|
46
|
+
raw_deps = project.get("dependencies", []) or []
|
|
47
|
+
if not isinstance(raw_deps, list):
|
|
48
|
+
return set(), "pyproject.toml-tpl [project].dependencies must be a list"
|
|
49
|
+
|
|
50
|
+
names: Set[str] = set()
|
|
51
|
+
for dep in raw_deps:
|
|
52
|
+
if not isinstance(dep, str) or not dep.strip():
|
|
53
|
+
continue
|
|
54
|
+
name = _parse_pip_requirement(dep)[0]
|
|
55
|
+
if name:
|
|
56
|
+
names.add(name.lower())
|
|
57
|
+
return names, None
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def parse_requirements_names(requirements_path: Path) -> Set[str]:
|
|
61
|
+
"""Return the lowercased package names declared in a requirements file."""
|
|
62
|
+
with open(requirements_path, encoding="utf-8") as f:
|
|
63
|
+
lines = f.read().splitlines()
|
|
64
|
+
return {
|
|
65
|
+
_parse_pip_requirement(line)[0].lower()
|
|
66
|
+
for line in lines
|
|
67
|
+
if line.strip() and not line.strip().startswith("#")
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def check_file_structure(ctx: InspectionContext) -> bool:
|
|
72
|
+
"""Check the required file and directory structure.
|
|
73
|
+
|
|
74
|
+
Modern templates may ship ``pyproject.toml-tpl`` as the primary metadata
|
|
75
|
+
file; ``setup.py-tpl`` remains accepted for backward compatibility.
|
|
76
|
+
A template must provide at least one of the two. ``requirements.txt-tpl``
|
|
77
|
+
is no longer strictly required when ``pyproject.toml-tpl`` declares
|
|
78
|
+
``[project].dependencies``.
|
|
79
|
+
"""
|
|
80
|
+
missing_required = [
|
|
81
|
+
path
|
|
82
|
+
for path in ALWAYS_REQUIRED_PATHS
|
|
83
|
+
if not (ctx.template_path / path).exists()
|
|
84
|
+
]
|
|
85
|
+
has_metadata = any(
|
|
86
|
+
(ctx.template_path / name).exists() for name in METADATA_CANDIDATES
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
for path in missing_required:
|
|
90
|
+
ctx.add_error(f"Missing required path: {path}")
|
|
91
|
+
if not has_metadata:
|
|
92
|
+
ctx.add_error(
|
|
93
|
+
"Missing metadata file: expected pyproject.toml-tpl "
|
|
94
|
+
"(preferred) or setup.py-tpl"
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
if missing_required or not has_metadata:
|
|
98
|
+
return False
|
|
99
|
+
|
|
100
|
+
debug_log("File structure check passed", "info")
|
|
101
|
+
return True
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def check_file_extensions(ctx: InspectionContext) -> bool:
|
|
105
|
+
"""Check all Python files in the template carry the .py-tpl extension."""
|
|
106
|
+
for path in ctx.template_path.rglob("*"):
|
|
107
|
+
if path.is_file() and path.suffix == ".py":
|
|
108
|
+
ctx.add_error(f"Found .py file instead of .py-tpl: {path}")
|
|
109
|
+
return False
|
|
110
|
+
|
|
111
|
+
debug_log("File extension check passed", "info")
|
|
112
|
+
return True
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def check_dependencies(ctx: InspectionContext) -> bool:
|
|
116
|
+
"""Check that FastAPI is declared in at least one supported source.
|
|
117
|
+
|
|
118
|
+
All three metadata sources are consulted independently:
|
|
119
|
+
|
|
120
|
+
- ``requirements.txt-tpl``
|
|
121
|
+
- ``pyproject.toml-tpl`` ``[project].dependencies``
|
|
122
|
+
- ``setup.py-tpl`` ``install_requires``
|
|
123
|
+
|
|
124
|
+
The check passes if *any* source declares ``fastapi``, so a template
|
|
125
|
+
with a stale ``requirements.txt-tpl`` still passes when
|
|
126
|
+
``pyproject.toml-tpl`` is authoritative.
|
|
127
|
+
"""
|
|
128
|
+
req_path = ctx.template_path / "requirements.txt-tpl"
|
|
129
|
+
pyproject_path = ctx.template_path / "pyproject.toml-tpl"
|
|
130
|
+
setup_path = ctx.template_path / "setup.py-tpl"
|
|
131
|
+
|
|
132
|
+
sources_checked: List[str] = []
|
|
133
|
+
fastapi_declared = False
|
|
134
|
+
|
|
135
|
+
if req_path.exists():
|
|
136
|
+
sources_checked.append("requirements.txt-tpl")
|
|
137
|
+
try:
|
|
138
|
+
package_names = parse_requirements_names(req_path)
|
|
139
|
+
except (OSError, UnicodeDecodeError) as e:
|
|
140
|
+
ctx.add_error(f"Error reading requirements.txt-tpl: {e}")
|
|
141
|
+
return False
|
|
142
|
+
debug_log(
|
|
143
|
+
f"requirements.txt-tpl dependencies: {sorted(package_names)}", "debug"
|
|
144
|
+
)
|
|
145
|
+
fastapi_declared = fastapi_declared or "fastapi" in package_names
|
|
146
|
+
|
|
147
|
+
if pyproject_path.exists():
|
|
148
|
+
sources_checked.append("pyproject.toml-tpl")
|
|
149
|
+
package_names, parse_error = extract_pyproject_dependency_names(pyproject_path)
|
|
150
|
+
if parse_error is not None:
|
|
151
|
+
ctx.add_error(parse_error)
|
|
152
|
+
return False
|
|
153
|
+
debug_log(f"pyproject.toml-tpl dependencies: {sorted(package_names)}", "debug")
|
|
154
|
+
fastapi_declared = fastapi_declared or "fastapi" in package_names
|
|
155
|
+
|
|
156
|
+
if setup_path.exists():
|
|
157
|
+
sources_checked.append("setup.py-tpl")
|
|
158
|
+
try:
|
|
159
|
+
with open(setup_path, encoding="utf-8") as f:
|
|
160
|
+
content = f.read()
|
|
161
|
+
except (OSError, UnicodeDecodeError) as e:
|
|
162
|
+
ctx.add_error(f"Error reading setup.py-tpl: {e}")
|
|
163
|
+
return False
|
|
164
|
+
package_names = {
|
|
165
|
+
_parse_pip_requirement(dep)[0].lower()
|
|
166
|
+
for dep in _parse_setup_dependencies(content)
|
|
167
|
+
if dep
|
|
168
|
+
}
|
|
169
|
+
debug_log(f"setup.py-tpl dependencies: {sorted(package_names)}", "debug")
|
|
170
|
+
fastapi_declared = fastapi_declared or "fastapi" in package_names
|
|
171
|
+
|
|
172
|
+
if not sources_checked:
|
|
173
|
+
ctx.add_error(
|
|
174
|
+
"No dependency source found: expected one of requirements.txt-tpl, "
|
|
175
|
+
"pyproject.toml-tpl, or setup.py-tpl"
|
|
176
|
+
)
|
|
177
|
+
return False
|
|
178
|
+
|
|
179
|
+
if not fastapi_declared:
|
|
180
|
+
ctx.add_error(
|
|
181
|
+
"FastAPI dependency not found in any source ("
|
|
182
|
+
+ ", ".join(sources_checked)
|
|
183
|
+
+ ")"
|
|
184
|
+
)
|
|
185
|
+
return False
|
|
186
|
+
|
|
187
|
+
debug_log(
|
|
188
|
+
f"Dependencies check passed (sources: {', '.join(sources_checked)})", "info"
|
|
189
|
+
)
|
|
190
|
+
return True
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def _module_defines_fastapi_app(source: str) -> bool:
|
|
194
|
+
"""Return True when ``source`` assigns a ``FastAPI(...)`` instance."""
|
|
195
|
+
try:
|
|
196
|
+
tree = ast.parse(source)
|
|
197
|
+
except SyntaxError:
|
|
198
|
+
return False
|
|
199
|
+
|
|
200
|
+
for node in ast.walk(tree):
|
|
201
|
+
if not isinstance(node, (ast.Assign, ast.AnnAssign)):
|
|
202
|
+
continue
|
|
203
|
+
value = node.value
|
|
204
|
+
if not isinstance(value, ast.Call):
|
|
205
|
+
continue
|
|
206
|
+
func = value.func
|
|
207
|
+
name = ""
|
|
208
|
+
if isinstance(func, ast.Name):
|
|
209
|
+
name = func.id
|
|
210
|
+
elif isinstance(func, ast.Attribute):
|
|
211
|
+
name = func.attr
|
|
212
|
+
if name == "FastAPI":
|
|
213
|
+
return True
|
|
214
|
+
return False
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def check_fastapi_implementation(ctx: InspectionContext) -> bool:
|
|
218
|
+
"""Check the generated project really instantiates a FastAPI application.
|
|
219
|
+
|
|
220
|
+
This is a static (AST) guard so a broken main module is reported before
|
|
221
|
+
the far more expensive smoke test boots a server.
|
|
222
|
+
"""
|
|
223
|
+
try:
|
|
224
|
+
core_modules = find_template_core_modules(ctx.temp_dir)
|
|
225
|
+
debug_log(f"Found core modules: {core_modules}", "debug")
|
|
226
|
+
|
|
227
|
+
if not core_modules["main"]:
|
|
228
|
+
ctx.add_error("main.py not found in template")
|
|
229
|
+
return False
|
|
230
|
+
|
|
231
|
+
with open(core_modules["main"], encoding="utf-8") as f:
|
|
232
|
+
content = f.read()
|
|
233
|
+
except (OSError, UnicodeDecodeError) as e:
|
|
234
|
+
ctx.add_error(f"Error checking FastAPI implementation: {e}")
|
|
235
|
+
return False
|
|
236
|
+
|
|
237
|
+
if not _module_defines_fastapi_app(content):
|
|
238
|
+
ctx.add_error("FastAPI app creation not found in main.py")
|
|
239
|
+
debug_log(f"main.py content preview: {content[:200]}...", "debug")
|
|
240
|
+
return False
|
|
241
|
+
|
|
242
|
+
debug_log("FastAPI implementation check passed", "info")
|
|
243
|
+
return True
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def check_tests_present(ctx: InspectionContext) -> bool:
|
|
247
|
+
"""A template must ship at least one test module.
|
|
248
|
+
|
|
249
|
+
Previously a missing ``tests/`` directory only produced a warning; an
|
|
250
|
+
untested template is now a hard failure.
|
|
251
|
+
"""
|
|
252
|
+
tests_dir = ctx.template_path / "tests"
|
|
253
|
+
if not tests_dir.is_dir():
|
|
254
|
+
ctx.add_error("Missing required path: tests (templates must ship tests)")
|
|
255
|
+
return False
|
|
256
|
+
|
|
257
|
+
test_files = [
|
|
258
|
+
path
|
|
259
|
+
for path in tests_dir.rglob("*")
|
|
260
|
+
if path.is_file()
|
|
261
|
+
and path.name.startswith("test_")
|
|
262
|
+
and path.name.endswith((".py-tpl", ".py"))
|
|
263
|
+
]
|
|
264
|
+
if not test_files:
|
|
265
|
+
ctx.add_error(
|
|
266
|
+
"No test module found under tests/: at least one test_*.py-tpl file "
|
|
267
|
+
"is required"
|
|
268
|
+
)
|
|
269
|
+
return False
|
|
270
|
+
|
|
271
|
+
debug_log(f"Test suite check passed ({len(test_files)} test files)", "info")
|
|
272
|
+
return True
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def check_no_junk_files(ctx: InspectionContext) -> bool:
|
|
276
|
+
"""Reject OS/editor artifacts and build leftovers committed to a template."""
|
|
277
|
+
junk_names = {".DS_Store", "Thumbs.db", "desktop.ini", ".AppleDouble"}
|
|
278
|
+
junk_dirs = {"__pycache__", ".pytest_cache", ".mypy_cache", ".venv"}
|
|
279
|
+
found: List[str] = []
|
|
280
|
+
|
|
281
|
+
for path in ctx.template_path.rglob("*"):
|
|
282
|
+
rel = path.relative_to(ctx.template_path)
|
|
283
|
+
if path.is_dir():
|
|
284
|
+
if path.name in junk_dirs:
|
|
285
|
+
found.append(str(rel))
|
|
286
|
+
elif path.name in junk_names or path.suffix in {".pyc", ".pyo"}:
|
|
287
|
+
found.append(str(rel))
|
|
288
|
+
|
|
289
|
+
if found:
|
|
290
|
+
for item in sorted(found):
|
|
291
|
+
ctx.add_error(f"Junk file committed to template: {item}")
|
|
292
|
+
return False
|
|
293
|
+
|
|
294
|
+
debug_log("Junk file check passed", "info")
|
|
295
|
+
return True
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
def check_no_placeholder_residue(ctx: InspectionContext) -> bool:
|
|
299
|
+
"""Every ``<placeholder>`` must be substituted during project generation.
|
|
300
|
+
|
|
301
|
+
The inspection generates a real project from the template (copy +
|
|
302
|
+
metadata injection), so any surviving placeholder token means the
|
|
303
|
+
template declares a variable the generator does not know how to fill.
|
|
304
|
+
"""
|
|
305
|
+
placeholders = [
|
|
306
|
+
"<project_name>",
|
|
307
|
+
"<author>",
|
|
308
|
+
"<author_email>",
|
|
309
|
+
"<description>",
|
|
310
|
+
]
|
|
311
|
+
text_suffixes = {
|
|
312
|
+
".py",
|
|
313
|
+
".toml",
|
|
314
|
+
".cfg",
|
|
315
|
+
".txt",
|
|
316
|
+
".md",
|
|
317
|
+
".yml",
|
|
318
|
+
".yaml",
|
|
319
|
+
".json",
|
|
320
|
+
".sh",
|
|
321
|
+
".env",
|
|
322
|
+
".ini",
|
|
323
|
+
}
|
|
324
|
+
residue: List[str] = []
|
|
325
|
+
|
|
326
|
+
for root, dirs, files in os.walk(ctx.temp_dir):
|
|
327
|
+
dirs[:] = [d for d in dirs if d not in {".venv", "venv", "__pycache__", ".git"}]
|
|
328
|
+
for file_name in files:
|
|
329
|
+
file_path = os.path.join(root, file_name)
|
|
330
|
+
suffix = Path(file_name).suffix
|
|
331
|
+
if suffix and suffix not in text_suffixes:
|
|
332
|
+
continue
|
|
333
|
+
try:
|
|
334
|
+
with open(file_path, encoding="utf-8") as f:
|
|
335
|
+
content = f.read()
|
|
336
|
+
except (OSError, UnicodeDecodeError):
|
|
337
|
+
continue
|
|
338
|
+
hits = [token for token in placeholders if token in content]
|
|
339
|
+
if hits:
|
|
340
|
+
rel = os.path.relpath(file_path, ctx.temp_dir)
|
|
341
|
+
residue.append(f"{rel}: {', '.join(hits)}")
|
|
342
|
+
|
|
343
|
+
if residue:
|
|
344
|
+
for item in sorted(residue):
|
|
345
|
+
ctx.add_error(f"Unsubstituted placeholder in generated project: {item}")
|
|
346
|
+
return False
|
|
347
|
+
|
|
348
|
+
debug_log("Placeholder substitution check passed", "info")
|
|
349
|
+
return True
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# --------------------------------------------------------------------------
|
|
2
|
+
# Configuration consistency checks for a template's metadata files.
|
|
3
|
+
#
|
|
4
|
+
# FastAPI-fastkit pins generated projects to Python 3.12, so every tool that
|
|
5
|
+
# carries a Python version must agree. The dependency drift check keeps
|
|
6
|
+
# ``pyproject.toml-tpl`` and ``requirements.txt-tpl`` from diverging, and the
|
|
7
|
+
# self-dependency check keeps fastkit itself out of generated runtimes.
|
|
8
|
+
#
|
|
9
|
+
# @author bnbong
|
|
10
|
+
# --------------------------------------------------------------------------
|
|
11
|
+
import tomllib
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import Any, Dict, List, Optional
|
|
14
|
+
|
|
15
|
+
from fastapi_fastkit.utils.logging import debug_log
|
|
16
|
+
|
|
17
|
+
from .checks import extract_pyproject_dependency_names, parse_requirements_names
|
|
18
|
+
from .context import InspectionContext
|
|
19
|
+
|
|
20
|
+
TARGET_PYTHON = "3.12"
|
|
21
|
+
TARGET_PYTHON_TAG = "py312"
|
|
22
|
+
#: fastkit is a scaffolding tool - a generated project must not depend on it.
|
|
23
|
+
SELF_PACKAGE_NAME = "fastapi-fastkit"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _load_pyproject(pyproject_path: Path) -> Optional[Dict[str, Any]]:
|
|
27
|
+
"""Load a pyproject template file, returning ``None`` when unreadable."""
|
|
28
|
+
try:
|
|
29
|
+
with open(pyproject_path, "rb") as f:
|
|
30
|
+
data: Dict[str, Any] = tomllib.load(f)
|
|
31
|
+
return data
|
|
32
|
+
except (OSError, tomllib.TOMLDecodeError, UnicodeDecodeError) as e:
|
|
33
|
+
debug_log(f"Failed to parse {pyproject_path}: {e}", "warning")
|
|
34
|
+
return None
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _check_python_versions(ctx: InspectionContext, data: Dict[str, Any]) -> bool:
|
|
38
|
+
"""requires-python / black target-version / mypy python_version must be 3.12."""
|
|
39
|
+
passed = True
|
|
40
|
+
tools = data.get("tool", {})
|
|
41
|
+
|
|
42
|
+
requires_python = data.get("project", {}).get("requires-python")
|
|
43
|
+
if requires_python is None:
|
|
44
|
+
ctx.add_error("pyproject.toml-tpl is missing [project].requires-python")
|
|
45
|
+
passed = False
|
|
46
|
+
elif TARGET_PYTHON not in str(requires_python):
|
|
47
|
+
ctx.add_error(
|
|
48
|
+
f"requires-python must target Python {TARGET_PYTHON}, "
|
|
49
|
+
f"found {requires_python!r}"
|
|
50
|
+
)
|
|
51
|
+
passed = False
|
|
52
|
+
|
|
53
|
+
black_target = tools.get("black", {}).get("target-version")
|
|
54
|
+
if black_target is not None:
|
|
55
|
+
targets = black_target if isinstance(black_target, list) else [black_target]
|
|
56
|
+
if TARGET_PYTHON_TAG not in targets:
|
|
57
|
+
ctx.add_error(
|
|
58
|
+
f"[tool.black].target-version must include {TARGET_PYTHON_TAG!r}, "
|
|
59
|
+
f"found {black_target!r}"
|
|
60
|
+
)
|
|
61
|
+
passed = False
|
|
62
|
+
|
|
63
|
+
mypy_version = tools.get("mypy", {}).get("python_version")
|
|
64
|
+
if mypy_version is not None and str(mypy_version) != TARGET_PYTHON:
|
|
65
|
+
ctx.add_error(
|
|
66
|
+
f"[tool.mypy].python_version must be {TARGET_PYTHON!r}, "
|
|
67
|
+
f"found {mypy_version!r}"
|
|
68
|
+
)
|
|
69
|
+
passed = False
|
|
70
|
+
|
|
71
|
+
return passed
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _check_self_dependency(ctx: InspectionContext, names: set[str]) -> bool:
|
|
75
|
+
"""Generated projects must not ship FastAPI-fastkit as a runtime dependency."""
|
|
76
|
+
if SELF_PACKAGE_NAME in {name.lower() for name in names}:
|
|
77
|
+
ctx.add_error(
|
|
78
|
+
"FastAPI-fastkit must not be a runtime dependency of a generated "
|
|
79
|
+
"project (remove it from the template's dependency list)"
|
|
80
|
+
)
|
|
81
|
+
return False
|
|
82
|
+
return True
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def check_configuration_consistency(ctx: InspectionContext) -> bool:
|
|
86
|
+
"""Validate Python-version pins, dependency drift and self-dependency."""
|
|
87
|
+
pyproject_path = ctx.template_path / "pyproject.toml-tpl"
|
|
88
|
+
requirements_path = ctx.template_path / "requirements.txt-tpl"
|
|
89
|
+
passed = True
|
|
90
|
+
|
|
91
|
+
pyproject_deps: set[str] = set()
|
|
92
|
+
if pyproject_path.exists():
|
|
93
|
+
data = _load_pyproject(pyproject_path)
|
|
94
|
+
if data is None:
|
|
95
|
+
ctx.add_error("Invalid pyproject.toml-tpl: could not be parsed")
|
|
96
|
+
return False
|
|
97
|
+
passed = _check_python_versions(ctx, data) and passed
|
|
98
|
+
|
|
99
|
+
pyproject_deps, parse_error = extract_pyproject_dependency_names(pyproject_path)
|
|
100
|
+
if parse_error is not None:
|
|
101
|
+
ctx.add_error(parse_error)
|
|
102
|
+
return False
|
|
103
|
+
passed = _check_self_dependency(ctx, pyproject_deps) and passed
|
|
104
|
+
|
|
105
|
+
requirement_names: set[str] = set()
|
|
106
|
+
if requirements_path.exists():
|
|
107
|
+
try:
|
|
108
|
+
requirement_names = parse_requirements_names(requirements_path)
|
|
109
|
+
except (OSError, UnicodeDecodeError) as e:
|
|
110
|
+
ctx.add_error(f"Error reading requirements.txt-tpl: {e}")
|
|
111
|
+
return False
|
|
112
|
+
passed = _check_self_dependency(ctx, requirement_names) and passed
|
|
113
|
+
|
|
114
|
+
if pyproject_deps and requirement_names:
|
|
115
|
+
missing: List[str] = sorted(pyproject_deps - requirement_names)
|
|
116
|
+
if missing:
|
|
117
|
+
ctx.add_error(
|
|
118
|
+
"Dependency drift: declared in pyproject.toml-tpl but absent from "
|
|
119
|
+
"requirements.txt-tpl: " + ", ".join(missing)
|
|
120
|
+
)
|
|
121
|
+
passed = False
|
|
122
|
+
|
|
123
|
+
if passed:
|
|
124
|
+
debug_log("Configuration consistency check passed", "info")
|
|
125
|
+
return passed
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# --------------------------------------------------------------------------
|
|
2
|
+
# Shared state for the template inspection pipeline.
|
|
3
|
+
#
|
|
4
|
+
# Every check and test strategy operates on an ``InspectionContext``: it owns
|
|
5
|
+
# the template source path, the temporary generated-project path, the loaded
|
|
6
|
+
# template configuration and the accumulated errors/warnings.
|
|
7
|
+
#
|
|
8
|
+
# @author bnbong
|
|
9
|
+
# --------------------------------------------------------------------------
|
|
10
|
+
import os
|
|
11
|
+
from dataclasses import dataclass, field
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import Any, Dict, List, Optional
|
|
14
|
+
|
|
15
|
+
from fastapi_fastkit.utils.logging import debug_log
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass
|
|
19
|
+
class InspectionOptions:
|
|
20
|
+
"""Tunable switches for a single inspection run."""
|
|
21
|
+
|
|
22
|
+
#: Skip every network access (dependency freshness lookups).
|
|
23
|
+
offline: bool = False
|
|
24
|
+
#: Boot the generated project with uvicorn and probe its HTTP endpoints.
|
|
25
|
+
run_smoke_test: bool = True
|
|
26
|
+
#: Run ``mypy`` inside the generated project (opt-in, slow).
|
|
27
|
+
run_mypy: bool = False
|
|
28
|
+
#: Run the template's own test suite.
|
|
29
|
+
run_template_tests: bool = True
|
|
30
|
+
#: Seconds to wait for the smoke-test server to answer.
|
|
31
|
+
smoke_timeout: int = 60
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@dataclass
|
|
35
|
+
class InspectionContext:
|
|
36
|
+
"""Mutable state shared by all inspection steps."""
|
|
37
|
+
|
|
38
|
+
template_path: Path
|
|
39
|
+
temp_dir: str
|
|
40
|
+
options: InspectionOptions = field(default_factory=InspectionOptions)
|
|
41
|
+
template_config: Optional[Dict[str, Any]] = None
|
|
42
|
+
errors: List[str] = field(default_factory=list)
|
|
43
|
+
warnings: List[str] = field(default_factory=list)
|
|
44
|
+
#: Populated by a test strategy so later checks can reuse the environment.
|
|
45
|
+
venv_path: Optional[str] = None
|
|
46
|
+
#: Populated when a test strategy already exercised the app's HTTP surface
|
|
47
|
+
#: (the Docker strategy probes the running container), so the Smoke Test
|
|
48
|
+
#: step reuses that verdict instead of booting a second server.
|
|
49
|
+
smoke_result: Optional[bool] = None
|
|
50
|
+
|
|
51
|
+
def add_error(self, message: str) -> None:
|
|
52
|
+
"""Record a fatal finding."""
|
|
53
|
+
self.errors.append(message)
|
|
54
|
+
debug_log(f"Inspection error: {message}", "error")
|
|
55
|
+
|
|
56
|
+
def add_warning(self, message: str) -> None:
|
|
57
|
+
"""Record a non-fatal finding."""
|
|
58
|
+
self.warnings.append(message)
|
|
59
|
+
debug_log(f"Inspection warning: {message}", "warning")
|
|
60
|
+
|
|
61
|
+
def temp_path(self, *parts: str) -> str:
|
|
62
|
+
"""Join ``parts`` onto the generated project directory."""
|
|
63
|
+
return os.path.join(self.temp_dir, *parts)
|
|
64
|
+
|
|
65
|
+
def python_executable(self) -> Optional[str]:
|
|
66
|
+
"""Return the interpreter of the inspection venv, if one was created."""
|
|
67
|
+
if not self.venv_path:
|
|
68
|
+
return None
|
|
69
|
+
if os.name == "nt": # pragma: no cover - Windows-only branch
|
|
70
|
+
return os.path.join(self.venv_path, "Scripts", "python")
|
|
71
|
+
return os.path.join(self.venv_path, "bin", "python")
|