Skip to content

Commit 70c6bca

Browse files
authored
Merge pull request #17 from fastapi-mvc/add_type_checking
Add type checking
2 parents 3b00900 + 17cf6ae commit 70c6bca

18 files changed

+203
-93
lines changed

template/Makefile.jinja

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ image: ## Build {{container_image_name}} image
3737
.PHONY: metrics
3838
metrics: install ## Run {{project_name}} metrics checks
3939
echo "[metrics] Run {{project_name}} PEP 8 checks."
40-
${POETRY_BINARY} run flake8 --select=E,W,I --max-line-length 80 --import-order-style pep8 --statistics --count {{package_name}}
40+
${POETRY_BINARY} run flake8 --select=E,W,I --max-line-length 88 --import-order-style pep8 --statistics --count {{package_name}}
4141
echo "[metrics] Run {{project_name}} PEP 257 checks."
4242
${POETRY_BINARY} run flake8 --select=D --ignore D301 --statistics --count {{package_name}}
4343
echo "[metrics] Run {{project_name}} pyflakes checks."
@@ -47,7 +47,7 @@ metrics: install ## Run {{project_name}} metrics checks
4747
echo "[metrics] Run {{project_name}} open TODO checks."
4848
${POETRY_BINARY} run flake8 --select=T --statistics --count {{package_name}} tests
4949
echo "[metrics] Run {{project_name}} black checks."
50-
${POETRY_BINARY} run black -l 80 --check {{package_name}}
50+
${POETRY_BINARY} run black --check {{package_name}}
5151

5252
.PHONY: unit-test
5353
unit-test: install ## Run {{project_name}} unit tests
@@ -71,6 +71,11 @@ test: unit-test integration-test ## Run {{project_name}} tests
7171
docs: install ## Build {{project_name}} documentation
7272
echo "[docs] Build {{project_name}} documentation."
7373
${POETRY_BINARY} run sphinx-build docs site
74+
75+
.PHONY: mypy
76+
mypy: install ## Run {{project_name}} mypy checks
77+
echo "[mypy] Run {{project_name}} mypy checks."
78+
${POETRY_BINARY} run mypy {{package_name}}
7479
{% if helm %}
7580
.PHONY: dev-env
7681
dev-env: image ## Start a local Kubernetes cluster using minikube and deploy application

template/pyproject.toml.jinja

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ redis = "~4.5.1"
2929
aiohttp = "~3.8.4"
3030
{%- endif %}
3131

32-
[tool.poetry.dev-dependencies]
32+
[tool.poetry.group.dev.dependencies]
3333
pytest = "~7.2.1"
3434
pytest-cov = "~4.0.0"
3535
pytest-asyncio = "~0.20.3"
@@ -38,6 +38,7 @@ httpx = "~0.23.3"
3838
{%- if aiohttp %}
3939
aioresponses = "~0.7.3"
4040
{%- endif %}
41+
mypy = "~1.0.1"
4142
flake8 = "~5.0.4"
4243
flake8-docstrings = "~1.7.0"
4344
flake8-import-order = "~0.18.1"
@@ -67,3 +68,31 @@ omit = [
6768
exclude_lines = [
6869
"pass",
6970
]
71+
72+
[tool.mypy]
73+
exclude = [
74+
"config/gunicorn.py"
75+
]
76+
python_version = '3.10'
77+
show_error_codes = true
78+
follow_imports = 'silent'
79+
strict_optional = true
80+
warn_redundant_casts = true
81+
warn_unused_ignores = true
82+
disallow_any_generics = true
83+
check_untyped_defs = true
84+
no_implicit_reexport = true
85+
warn_unused_configs = true
86+
disallow_subclassing_any = true
87+
disallow_incomplete_defs = true
88+
disallow_untyped_decorators = true
89+
disallow_untyped_calls = true
90+
disallow_untyped_defs = true
91+
implicit_optional = true
92+
93+
[[tool.mypy.overrides]]
94+
module = [
95+
"gunicorn.*",
96+
"redis.*",
97+
]
98+
ignore_missing_imports = true

template/{% if github_actions %}.github{% endif %}/workflows/main.yml

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ env:
1313
POETRY_CONFIG_DIR: /opt/poetry
1414
POETRY_CACHE_DIR: /opt/poetry/cache
1515
POETRY_VIRTUALENVS_PATH: /opt/poetry/store
16+
DEFAULT_PYTHON: '3.10'
1617

1718
jobs:
1819
# This job checks if an identical workflow is being triggered by different
@@ -48,7 +49,7 @@ jobs:
4849
uses: actions/cache@v3
4950
with:
5051
path: ${{ env.POETRY_HOME }}
51-
key: ${{ runner.os }}-${{ matrix.python-version }}-${{ hashFiles('./pyproject.toml') }}-{{ hashFiles('./poetry.lock') }}
52+
key: ${{ runner.os }}-${{ matrix.python-version }}-${{ hashFiles('./pyproject.toml') }}-${{ hashFiles('./poetry.lock') }}
5253
- name: Install package
5354
run: make install
5455
if: steps.cached-poetry.outputs.cache-hit != 'true'
@@ -58,15 +59,15 @@ jobs:
5859

5960
steps:
6061
- uses: actions/checkout@v3
61-
- name: Set up Python 3.9
62+
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
6263
uses: actions/setup-python@v4
6364
with:
64-
python-version: 3.9
65+
python-version: ${{ env.DEFAULT_PYTHON }}
6566
- name: Load Poetry cache
6667
uses: actions/cache@v3
6768
with:
6869
path: ${{ env.POETRY_HOME }}
69-
key: ${{ runner.os }}-3.9-${{ hashFiles('./pyproject.toml') }}-{{ hashFiles('./poetry.lock') }}
70+
key: ${{ runner.os }}-${{ env.DEFAULT_PYTHON }}-${{ hashFiles('./pyproject.toml') }}-${{ hashFiles('./poetry.lock') }}
7071
- name: Build wheel
7172
run: make build
7273
- name: Archive build artifacts
@@ -81,15 +82,15 @@ jobs:
8182

8283
steps:
8384
- uses: actions/checkout@v3
84-
- name: Set up Python 3.9
85+
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
8586
uses: actions/setup-python@v4
8687
with:
87-
python-version: 3.9
88+
python-version: ${{ env.DEFAULT_PYTHON }}
8889
- name: Load Poetry cache
8990
uses: actions/cache@v3
9091
with:
9192
path: ${{ env.POETRY_HOME }}
92-
key: ${{ runner.os }}-3.9-${{ hashFiles('./pyproject.toml') }}-{{ hashFiles('./poetry.lock') }}
93+
key: ${{ runner.os }}-${{ env.DEFAULT_PYTHON }}-${{ hashFiles('./pyproject.toml') }}-${{ hashFiles('./poetry.lock') }}
9394
- name: Run metrics checks
9495
run: make metrics
9596
unit-tests:
@@ -110,7 +111,7 @@ jobs:
110111
uses: actions/cache@v3
111112
with:
112113
path: ${{ env.POETRY_HOME }}
113-
key: ${{ runner.os }}-${{ matrix.python-version }}-${{ hashFiles('./pyproject.toml') }}-{{ hashFiles('./poetry.lock') }}
114+
key: ${{ runner.os }}-${{ matrix.python-version }}-${{ hashFiles('./pyproject.toml') }}-${{ hashFiles('./poetry.lock') }}
114115
- name: Run unit tests
115116
run: make unit-test
116117
integration-tests:
@@ -131,7 +132,7 @@ jobs:
131132
uses: actions/cache@v3
132133
with:
133134
path: ${{ env.POETRY_HOME }}
134-
key: ${{ runner.os }}-${{ matrix.python-version }}-${{ hashFiles('./pyproject.toml') }}-{{ hashFiles('./poetry.lock') }}
135+
key: ${{ runner.os }}-${{ matrix.python-version }}-${{ hashFiles('./pyproject.toml') }}-${{ hashFiles('./poetry.lock') }}
135136
- name: Run integration tests
136137
run: make integration-test
137138
coverage:
@@ -140,14 +141,31 @@ jobs:
140141

141142
steps:
142143
- uses: actions/checkout@v3
143-
- name: Set up Python 3.9
144+
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
144145
uses: actions/setup-python@v4
145146
with:
146-
python-version: 3.9
147+
python-version: ${{ env.DEFAULT_PYTHON }}
147148
- name: Load Poetry cache
148149
uses: actions/cache@v3
149150
with:
150151
path: ${{ env.POETRY_HOME }}
151-
key: ${{ runner.os }}-3.9-${{ hashFiles('./pyproject.toml') }}-{{ hashFiles('./poetry.lock') }}
152+
key: ${{ runner.os }}-${{ env.DEFAULT_PYTHON }}-${{ hashFiles('./pyproject.toml') }}-${{ hashFiles('./poetry.lock') }}
152153
- name: Run coverage
153-
run: make coverage
154+
run: make coverage
155+
mypy:
156+
needs: install
157+
runs-on: ubuntu-latest
158+
159+
steps:
160+
- uses: actions/checkout@v3
161+
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
162+
uses: actions/setup-python@v4
163+
with:
164+
python-version: ${{ env.DEFAULT_PYTHON }}
165+
- name: Load Poetry cache
166+
uses: actions/cache@v3
167+
with:
168+
path: ${{ env.POETRY_HOME }}
169+
key: ${{ runner.os }}-${{ env.DEFAULT_PYTHON }}-${{ hashFiles('./pyproject.toml') }}-${{ hashFiles('./poetry.lock') }}
170+
- name: Run mypy checks
171+
run: make mypy

template/{% if github_actions %}.github{% endif %}/workflows/{% if nix %}nix.yml{% endif %}.jinja

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ jobs:
5353
run: nix flake check
5454
- name: Run metrics checks
5555
run: nix run .#metrics
56+
- name: Run mypy checks
57+
run: nix run .#mypy
5658
- name: Run tests
5759
run: nix run .#test
5860
nix-build:

template/{% if nix %}flake.nix{% endif %}.jinja

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@
6868
echo "[nix][metrics] Run {{project_name}} open TODO checks."
6969
flake8 --select=T --statistics --count {{package_name}} tests
7070
echo "[nix][metrics] Run {{project_name}} black checks."
71-
black -l 80 --check {{package_name}}
71+
black --check {{package_name}}
7272
'');
7373
};
7474
docs = {
@@ -117,6 +117,17 @@
117117
pytest --cov={{package_name}} --cov-fail-under=90 --cov-report=xml --cov-report=term-missing tests
118118
'');
119119
};
120+
mypy = {
121+
type = "app";
122+
program = toString (pkgs.writeScript "mypy" ''
123+
export PATH="${pkgs.lib.makeBinPath [
124+
pkgs.{{project_name}}-dev
125+
pkgs.git
126+
]}"
127+
echo "[nix][mypy] Run {{project_name}} mypy checks."
128+
mypy {{package_name}}
129+
'');
130+
};
120131
test = {
121132
type = "app";
122133
program = toString (pkgs.writeScript "test" ''

template/{{package_name}}/app/asgi.py.jinja

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ from {{package_name}}.app.exceptions import (
2020
log = logging.getLogger(__name__)
2121

2222

23-
async def on_startup():
23+
async def on_startup() -> None:
2424
"""Define FastAPI startup event handler.
2525

2626
Resources:
@@ -43,7 +43,7 @@ async def on_startup():
4343
{%- endif %}
4444

4545

46-
async def on_shutdown():
46+
async def on_shutdown() -> None:
4747
"""Define FastAPI shutdown event handler.
4848

4949
Resources:
@@ -67,7 +67,7 @@ async def on_shutdown():
6767
{%- endif %}
6868

6969

70-
def get_application():
70+
def get_application() -> FastAPI:
7171
"""Initialize FastAPI application.
7272

7373
Returns:

template/{{package_name}}/app/controllers/ready.py.jinja

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ log = logging.getLogger(__name__)
2626
responses={502: {"model": ErrorResponse}},
2727
{%- endif %}
2828
)
29-
async def readiness_check():
29+
async def readiness_check() -> ReadyResponse:
3030
"""Run basic application health check.
3131

3232
If the application is up and running then this endpoint will return simple
@@ -50,9 +50,9 @@ async def readiness_check():
5050
log.error("Could not connect to Redis")
5151
raise HTTPException(
5252
status_code=502,
53-
content=ErrorResponse(
54-
code=502, message="Could not connect to Redis"
55-
).dict(exclude_none=True),
53+
content=ErrorResponse(code=502, message="Could not connect to Redis").dict(
54+
exclude_none=True
55+
),
5656
)
5757
{% else %}
5858
if settings.USE_REDIS:

template/{{package_name}}/app/exceptions/http.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ def __init__(
3636
self.content = content
3737
self.headers = headers
3838

39-
def __repr__(self):
39+
def __repr__(self) -> str:
4040
"""Class custom __repr__ method implementation.
4141
4242
Returns:
@@ -52,7 +52,9 @@ def __repr__(self):
5252
return f"{self.__class__.__name__}({', '.join(kwargs)})"
5353

5454

55-
async def http_exception_handler(request: Request, exception: HTTPException):
55+
async def http_exception_handler(
56+
request: Request, exception: HTTPException
57+
) -> JSONResponse:
5658
"""Define custom HTTPException handler.
5759
5860
In this application custom handler is added in asgi.py while initializing

0 commit comments

Comments
 (0)