Skip to content
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
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ jobs:
mkdir test_results
uv run ruff check .
uv run ruff format --check .
uv run mypy .
uv run pyrefly check
working-directory: src/

tests:
Expand Down
16 changes: 12 additions & 4 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,21 @@ repos:
- id: check-added-large-files

- repo: https://github.com/PyCQA/bandit
rev: 1.8.6
rev: 1.9.4
hooks:
- id: bandit
args: ["-c", "pyproject.toml"]
additional_dependencies: ["bandit[toml]"]

- repo: https://github.com/asottile/pyupgrade
rev: v3.21.1
rev: v3.21.2
hooks:
- id: pyupgrade
args: ["--py313-plus"]

- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.14.4
rev: v0.15.10
hooks:
# Run the linter.
- id: ruff-check
Expand All @@ -39,4 +39,12 @@ repos:
- id: commitizen-branch
stages:
- pre-push
rev: v4.9.1
rev: v4.13.9

- repo: https://github.com/facebook/pyrefly-pre-commit
rev: 0.60.1
hooks:
- id: pyrefly-check
name: Pyrefly (type checking)
pass_filenames: false # Recommended to do full repo checks. However, you can change this to `true` to only check changed files
language: system # Use system-installed pyrefly
14 changes: 5 additions & 9 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ dependencies = [
"django-health-check~=4.0",
"django-jet-reboot>=1.3.9",
"django-storages[s3]>=1.14.4",
"django-stubs[compatible-mypy]~=5.2.0", # Pinned to 5.2.* for compatibility with Django 5.2
"django-stubs-ext~=5.2.0", # Pinned to 5.2.* for compatibility with Django 5.2
"django~=5.2.0",
"djangorestframework>=3.15.2",
"drf-spectacular>=0.27.2",
Expand All @@ -39,9 +37,9 @@ dev = [
"coverage>=7.6.1",
"django-debug-toolbar>=4.4.6",
"django-filter-stubs>=0.1.3",
"djangorestframework-stubs[compatible-mypy]>=3.16",
"mypy~=1.13.0", # Pinned range for compatibility with Django 5.2
"django-stubs>=5.2.9",
"peek-python>=25.0.7",
"pyrefly>=0.60.1",
"pytest>=8.3.3",
"pytest-cov>=5.0.0",
"pytest-django>=4.9.0",
Expand All @@ -50,11 +48,6 @@ dev = [
"ruff>=0.6.6",
]

[tool.mypy]
strict = true
plugins = ["mypy_django_plugin.main", "mypy_drf_plugin.main"]
mypy_path = "src/"

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

Expand Down Expand Up @@ -163,3 +156,6 @@ insecure = false
[tool.semantic_release.publish]
dist_glob_patterns = ["dist/*"]
upload_to_vcs_release = true

[tool.pyrefly]
search-path = ["src/"]
22 changes: 11 additions & 11 deletions src/api/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@
from api.models.abc import NewsItem


class ArticleForm(forms.ModelForm[NewsItem]):
class ArticleForm(forms.ModelForm):
title = forms.CharField(widget=forms.TextInput(attrs={"size": 70}), required=True)


# Register your models here.
# Models that need customization
@admin.register(Article)
@admin.register(Blog)
class ArticleAdmin(admin.ModelAdmin[NewsItem]):
class ArticleAdmin(admin.ModelAdmin):
"""Admin view for articles and blogs."""

list_per_page = 30
Expand Down Expand Up @@ -74,14 +74,14 @@ class ArticleAdmin(admin.ModelAdmin[NewsItem]):
]

@admin.action(description="Mark selected articles as audited")
def mark_as_audited(self, request: HttpRequest, queryset: QuerySet[NewsItem]) -> None:
def mark_as_audited(self, request: HttpRequest, queryset: QuerySet) -> None:
queryset.update(audited=True)

@admin.action(description="Unmark selected articles as audited")
def unmark_as_audited(self, request: HttpRequest, queryset: QuerySet[NewsItem]) -> None:
def unmark_as_audited(self, request: HttpRequest, queryset: QuerySet) -> None:
queryset.update(audited=False)

def get_queryset(self, request: HttpRequest) -> QuerySet[NewsItem]:
def get_queryset(self, request: HttpRequest) -> QuerySet:
"""Return the queryset with related fields prefetched."""
qs = super().get_queryset(request).select_related("news_site").prefetch_related("launches", "events")
return qs
Expand Down Expand Up @@ -214,7 +214,7 @@ def changelist_view(self, request: HttpRequest, extra_context: dict[str, str] |
extra_context = {"title": "News"}
return super().changelist_view(request, extra_context)

def save_model(self, request: HttpRequest, obj: NewsItem, form: forms.ModelForm[NewsItem], change: bool) -> None:
def save_model(self, request: HttpRequest, obj: NewsItem, form: forms.ModelForm, change: bool) -> None:
if change:
# Ignore the type error as obj will be an instance of Article of Blog
old_object: NewsItem = type(obj).objects.get(pk=obj.pk) # type: ignore
Expand All @@ -231,20 +231,20 @@ def save_model(self, request: HttpRequest, obj: NewsItem, form: forms.ModelForm[


@admin.register(Report)
class ReportAdmin(admin.ModelAdmin[Report]):
class ReportAdmin(admin.ModelAdmin):
"""Custom admin view for reports."""

list_display = ("title", "news_site", "published_at", "is_deleted")
search_fields = ["title"]
ordering = ("-published_at",)

def get_queryset(self, request: HttpRequest) -> QuerySet[Report]:
def get_queryset(self, request: HttpRequest) -> QuerySet:
"""Return the queryset with related fields prefetched."""
return super().get_queryset(request).select_related("news_site")


@admin.register(NewsSite)
class NewsSiteAdmin(admin.ModelAdmin[NewsSite]):
class NewsSiteAdmin(admin.ModelAdmin):
list_display = ("name", "id")

def changelist_view(self, request: HttpRequest, extra_context: dict[str, str] | None = None) -> HttpResponse:
Expand All @@ -255,13 +255,13 @@ def changelist_view(self, request: HttpRequest, extra_context: dict[str, str] |

# Models that can be registered as is
@admin.register(Event)
class EventAdmin(admin.ModelAdmin[Event]):
class EventAdmin(admin.ModelAdmin):
list_display = ("name",)
search_fields = ["name", "event_id"]


@admin.register(Launch)
class LaunchAdmin(admin.ModelAdmin[Launch]):
class LaunchAdmin(admin.ModelAdmin):
list_display = ("name",)
search_fields = ["name", "launch_id"]

Expand Down
8 changes: 4 additions & 4 deletions src/api/serializers/article_serializer.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
from rest_framework import serializers

from api.models import Article, NewsSite
from api.models import Article
from api.serializers.author_serializer import AuthorSerializer
from api.serializers.event_serializer import EventSerializer
from api.serializers.launch_serializer import LaunchSerializer


class ArticleSerializer(serializers.ModelSerializer[Article]):
news_site: "serializers.StringRelatedField[NewsSite]" = serializers.StringRelatedField()
class ArticleSerializer(serializers.ModelSerializer):
news_site = serializers.StringRelatedField()
launches = LaunchSerializer(many=True)
events = EventSerializer(many=True)
authors = AuthorSerializer(many=True)

class Meta:
class Meta: # pyrefly: ignore[bad-override]
model = Article
fields = [
"id",
Expand Down
4 changes: 2 additions & 2 deletions src/api/serializers/author_serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
from api.serializers.socials_serializer import SocialsSerializer


class AuthorSerializer(serializers.ModelSerializer[Author]):
class AuthorSerializer(serializers.ModelSerializer):
socials = SocialsSerializer(required=False)

class Meta:
class Meta: # pyrefly: ignore[bad-override]
model = Author
fields = ["name", "socials"]
6 changes: 3 additions & 3 deletions src/api/serializers/blog_serializer.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
from rest_framework import serializers

from api.models import Blog, NewsSite
from api.models import Blog
from api.serializers.author_serializer import AuthorSerializer
from api.serializers.event_serializer import EventSerializer
from api.serializers.launch_serializer import LaunchSerializer


class BlogSerializer(serializers.ModelSerializer[Blog]):
news_site: "serializers.StringRelatedField[NewsSite]" = serializers.StringRelatedField()
news_site = serializers.StringRelatedField()
launches = LaunchSerializer(many=True)
events = EventSerializer(many=True)
authors = AuthorSerializer(many=True)

class Meta:
class Meta: # pyrefly: ignore[bad-override]
model = Blog
fields = [
"id",
Expand Down
6 changes: 3 additions & 3 deletions src/api/serializers/event_serializer.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
from rest_framework import serializers

from api.models import Event, Provider
from api.models import Event


class EventSerializer(serializers.ModelSerializer[Event]):
provider: "serializers.StringRelatedField[Provider]" = serializers.StringRelatedField()
provider = serializers.StringRelatedField()

class Meta:
class Meta: # pyrefly: ignore[bad-override]
model = Event
fields = ["event_id", "provider"]
8 changes: 4 additions & 4 deletions src/api/serializers/launch_serializer.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
from rest_framework import serializers

from api.models import Launch, Provider
from api.models import Launch


class LaunchSerializer(serializers.ModelSerializer[Launch]):
provider: "serializers.StringRelatedField[Provider]" = serializers.StringRelatedField()
class LaunchSerializer(serializers.ModelSerializer):
provider = serializers.StringRelatedField()

class Meta:
class Meta: # pyrefly: ignore[bad-override]
model = Launch
fields = ["launch_id", "provider"]
4 changes: 2 additions & 2 deletions src/api/serializers/news_site_serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from api.models import NewsSite


class NewsSiteSerializer(serializers.ModelSerializer[NewsSite]):
class Meta:
class NewsSiteSerializer(serializers.ModelSerializer):
class Meta: # pyrefly: ignore[bad-override]
model = NewsSite
fields = ["id", "name"]
6 changes: 3 additions & 3 deletions src/api/serializers/report_serializer.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
from rest_framework import serializers

from api.models import NewsSite, Report
from api.models import Report
from api.serializers.author_serializer import AuthorSerializer


class ReportSerializer(serializers.ModelSerializer[Report]):
news_site: "serializers.StringRelatedField[NewsSite]" = serializers.StringRelatedField()
news_site = serializers.StringRelatedField()
authors = AuthorSerializer(many=True)

class Meta:
class Meta: # pyrefly: ignore[bad-override]
model = Report
fields = [
"id",
Expand Down
4 changes: 2 additions & 2 deletions src/api/serializers/socials_serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from api.models.socials import Socials


class SocialsSerializer(serializers.ModelSerializer[Socials]):
class Meta:
class SocialsSerializer(serializers.ModelSerializer):
class Meta: # pyrefly: ignore[bad-override]
model = Socials
fields = ["x", "youtube", "instagram", "linkedin", "mastodon", "bluesky"]
6 changes: 2 additions & 4 deletions src/snapy/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,14 @@
"""

from pathlib import Path
from typing import Any

import django_stubs_ext
from environs import Env
from snapy import __version__

env = Env()
env.read_env()

# Extensions for Django Stubs
django_stubs_ext.monkeypatch()

VERSION = __version__

Expand Down Expand Up @@ -187,7 +185,7 @@

DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"

REST_FRAMEWORK = {
REST_FRAMEWORK: dict[str, Any] = { # quick hack because type hint is not correct for the "DEFAULT_THROTTLE_RATES"
"DEFAULT_PAGINATION_CLASS": "api.utils.pagination.CustomLimitOffsetPagination",
"PAGE_SIZE": 10,
"DEFAULT_FILTER_BACKENDS": ["django_filters.rest_framework.DjangoFilterBackend"],
Expand Down
Loading
Loading