Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add mypy type checking #79

Merged
merged 3 commits into from
Feb 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion .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 main,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 All @@ -35,7 +44,9 @@ jobs:
run: |
pip install poetry
poetry config virtualenvs.create false
poetry install
poetry install --only main
- name: create .env file
run: cp example.env .env
- name: Run tests
env:
SECRET_KEY: secret
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
Loading