Skip to content

Commit

Permalink
add mypy type checking
Browse files Browse the repository at this point in the history
i added `# type: ignore` where i am unsure if there is an issue
  • Loading branch information
Fogapod committed Feb 9, 2024
1 parent c152ed8 commit b306180
Show file tree
Hide file tree
Showing 10 changed files with 550 additions and 83 deletions.
9 changes: 9 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,15 @@ jobs:
- uses: actions/setup-python@v4
with:
python-version: '3.11'
# because pre-commit uses external mypy
- name: install mypy
run: |
pip install poetry
poetry config virtualenvs.create false
poetry install --only typecheck
# https://github.com/typeddjango/django-stubs/issues/458
- name: create .env file
run: cp example.env .env
- uses: pre-commit/action@v3.0.0

unit_test:
Expand Down
17 changes: 11 additions & 6 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
repos:
- repo: https://github.com/ambv/black
rev: 23.12.1
rev: 24.1.1
hooks:
- id: black

- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.1.13
rev: v0.2.1
hooks:
- id: ruff
args: [ --fix, --exit-non-zero-on-fix ]
Expand All @@ -27,7 +27,12 @@ repos:
exclude: >-
^.*.md$
# - repo: https://github.com/pre-commit/mirrors-mypy
# rev: v1.8.0
# hooks:
# - id: mypy
# local mypy because of stub dependencies
- repo: local
hooks:
- id: typecheck
name: Typecheck
entry: mypy .
types: [python]
language: system
pass_filenames: false
22 changes: 11 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,6 @@ The all-in-one backend application for [Unitystation](https://github.com/unityst

## Development guide

#### pre-commit

pre-commit is a git hook which runs every time you make a commit to catch linting and formatting errors early.

```sh
pre-commit install
```

> Hint: if the world is on fire, production servers down, clown at your doorstep and you don't have time to make linters happy, add `-n` to `git commit` command (CI will still be mad though).
### Environment setup

Copy `example.env` to `.env` and customize it. You can then start development by either using docker or running the project locally.
Expand Down Expand Up @@ -77,6 +67,16 @@ from the src folder run
python manage.py runserver
```

#### pre-commit

pre-commit is a git hook which runs every time you make a commit to catch linting and formatting errors early.

```sh
pre-commit install
```

> Hint: if the world is on fire, production servers down, clown at your doorstep and you don't have time to make linters happy, add `-n` to `git commit` command (CI will still be mad though).
### Setting up Docker

Docker (with help of compose) lets you launch entire project including database locally without installing anything.
Expand All @@ -96,4 +96,4 @@ Some other useful links:
- http://localhost:8000/accounts/login-token -> Test loging in with a token (see admin page if you lost the token after login with credentials).

You can also use [Bruno](https://www.usebruno.com/) (a Postman alternative) to test out the API.
The Bruno project is included in the repository and you can find it in the 'api-collection' folder in the root of the repository.
The Bruno project is included in the repository and you can find it in the 'api-collection' folder in the root of the repository.
512 changes: 471 additions & 41 deletions poetry.lock

Large diffs are not rendered by default.

49 changes: 32 additions & 17 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -58,20 +58,27 @@ fixable = [
combine-as-imports = true
lines-between-types = 1

# with 'mypy' and 'django-stubs[compatible-mypy]' installed
# [tool.mypy]
# plugins = ["mypy_django_plugin.main"]
#
# [tool.django-stubs]
# django_settings_module = "central_command.settings"
#
# [[tool.mypy.overrides]]
# module = [
# "django_email_verification.*",
# "rest_framework.*",
# "knox.*",
# ]
# ignore_missing_imports = true
[tool.mypy]
show_column_numbers = true
show_error_codes = true

# XXX: add new rules here
check_untyped_defs = true

plugins = [
"mypy_django_plugin.main",
"mypy_drf_plugin.main",
]

[tool.django-stubs]
django_settings_module = "central_command.settings"

[[tool.mypy.overrides]]
module = [
"post_office.*",
"knox.*",
]
ignore_missing_imports = true

[tool.poetry]
name = "central-command"
Expand All @@ -91,9 +98,17 @@ whitenoise = "^6.2.0"
django-post-office = "^3.8.0"
drf-spectacular = "^0.27.1"

[tool.poetry.group.dev.dependencies]
pre-commit = "^3.6.0"
ruff = "^0.1.13"
[tool.poetry.group.lint.dependencies]
pre-commit = "3.6.0"
ruff = "0.1.13"
black = "24.1.1"

# typecheck is separate for CI
[tool.poetry.group.typecheck.dependencies]
# django-stubs does not support 1.8 yet
mypy = "1.7"
django-stubs = {extras = ["compatible-mypy"], version = "4.2.7"}
djangorestframework-stubs = {extras = ["compatible-mypy"], version = "3.14.5"}

[build-system]
requires = ["poetry-core>=1.0.0"]
Expand Down
4 changes: 2 additions & 2 deletions src/accounts/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class AccountConfirmationInline(admin.TabularInline):
def is_token_valid_display(self, instance):
return instance.is_token_valid()

is_token_valid_display.short_description = "Is Token Valid"
is_token_valid_display.short_description = "Is Token Valid" # type: ignore[attr-defined]


class PasswordResetRequestInline(admin.TabularInline):
Expand All @@ -22,7 +22,7 @@ class PasswordResetRequestInline(admin.TabularInline):
def is_token_valid_display(self, instance):
return instance.is_token_valid()

is_token_valid_display.short_description = "Is Token Valid"
is_token_valid_display.short_description = "Is Token Valid" # type: ignore[attr-defined]


@admin.register(Account)
Expand Down
2 changes: 1 addition & 1 deletion src/accounts/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class LoginWithCredentialsSerializer(serializers.Serializer):
password = serializers.CharField(style={"input_type": "password"})

def validate(self, data):
account = authenticate(username=data["email"], password=data["password"])
account: Account | None = authenticate(username=data["email"], password=data["password"]) # type: ignore[assignment]
if account is None:
raise serializers.ValidationError("Unable to login with provided credentials.")
if not account.is_confirmed:
Expand Down
13 changes: 10 additions & 3 deletions src/central_command/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@
from pathlib import Path
from urllib.parse import urljoin

from dotenv import load_dotenv

# dotenv needs to be loaded again (after manage.py) because of mypy plugin which runs settings.py separately
# see https://github.com/typeddjango/django-stubs/issues/458
load_dotenv()


# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent

Expand Down Expand Up @@ -187,12 +194,12 @@
STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"

# Website configuration
WEBSITE_URL = os.environ.get("WEBSITE_URL")
WEBSITE_URL = os.environ["WEBSITE_URL"]

PASS_RESET_URL_SUFFIX = os.environ.get("PASS_RESET_URL_SUFFIX")
PASS_RESET_URL_SUFFIX = os.environ["PASS_RESET_URL_SUFFIX"]
PASS_RESET_URL = urljoin(WEBSITE_URL, PASS_RESET_URL_SUFFIX)
PASS_RESET_TOKEN_TTL = 60 # minutes

ACCOUNT_CONFIRMATION_URL_SUFFIX = os.environ.get("ACCOUNT_CONFIRMATION_URL_SUFFIX")
ACCOUNT_CONFIRMATION_URL_SUFFIX = os.environ["ACCOUNT_CONFIRMATION_URL_SUFFIX"]
ACCOUNT_CONFIRMATION_URL = urljoin(WEBSITE_URL, ACCOUNT_CONFIRMATION_URL_SUFFIX)
ACCOUNT_CONFIRMATION_TOKEN_TTL = 24 # hours
1 change: 1 addition & 0 deletions src/central_command/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""

from django.contrib import admin
from django.urls import include, path
from drf_spectacular.views import SpectacularAPIView, SpectacularRedocView
Expand Down
4 changes: 2 additions & 2 deletions src/persistence/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class GetCharacterByIdView(GenericAPIView):
serializer_class = CharacterSerializer

def get_queryset(self):
return Character.objects.filter(account__unique_identifier=self.request.user.unique_identifier)
return Character.objects.filter(account__unique_identifier=self.request.user.unique_identifier) # type: ignore

def get(self, request, pk):
try:
Expand Down Expand Up @@ -169,7 +169,7 @@ def post(self, request):
data_with_account["account"] = request.user.pk

serializer = self.serializer_class(data=data_with_account)
serializer.account = request.user
serializer.account = request.user # type: ignore
try:
serializer.is_valid(raise_exception=True)
except ValidationError as e:
Expand Down

0 comments on commit b306180

Please sign in to comment.