Skip to content

Commit

Permalink
Merge pull request #31 from unicef/feature/ruff_ext
Browse files Browse the repository at this point in the history
Feature/ruff ext
  • Loading branch information
saxix authored Dec 24, 2024
2 parents b3f63b3 + e37dd0d commit 9cbe0e1
Show file tree
Hide file tree
Showing 134 changed files with 881 additions and 680 deletions.
13 changes: 0 additions & 13 deletions .flake8

This file was deleted.

2 changes: 1 addition & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ jobs:
run: pip install ruff
- name: Check syntax
# Stop the build if there are Python syntax errors or undefined names
run: ruff check src/
run: ruff check --output-format concise src/

- name: Warnings
run: ruff format --check
1 change: 0 additions & 1 deletion bandit.yaml

This file was deleted.

22 changes: 19 additions & 3 deletions ruff.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,31 +6,46 @@ case-sensitive = true

[lint]
select = [
"A", ## prevent using keywords that clobber python builtins
"A", # prevent using keywords that clobber python builtins
"ANN", # flake8 annotations
"B", # bugbear: security warnings
"BLE", # blind exceptions
"C4", # flake8-comprehensions
"C901", # McCabe complexity
"C90", # McCabe complexity
"COM", # flake8-commas
"D", # pydocstyle
"DJ", # flake8-django
"E", # pycodestylex
"E4", "E7", "E9",
"ERA", # eradicate
"F", # pyflakes
"FLY", # flynt
"FURB", # refurb
"I", # isort
"ICN", # flake8-import-conventions
"ISC", # implicit string concatenation
"N", # Pep* naming
"PERF", # perflint
"PIE", # flake8-pie
"PL", # PyLint
"PT", # flake8-pytest-style
"Q", # flake8-quotes
"R", # PyLint Refactor
"RET", # flake8-return
"S", # bandit,
"SIM", # flake8-simplify
"T10", # flake8-debugger
"T20", # flake8-print
"TC", # flake8-type-checking
"UP", # pyupgrade
"W", # pycodestyle warnings
"YTT", # flake8 2020
]
extend-select = ["UP", ]
ignore = [
"ANN401",
"B904", # raise-without-from-inside-except: syntax not compatible with py2
"COM812",
"D100", # Missing docstring in public module
"D101", # Missing docstring in public class
"D102", # Missing docstring in public method
Expand Down Expand Up @@ -62,4 +77,5 @@ skip-magic-trailing-comma = false
line-ending = "auto"

[lint.per-file-ignores]
"tests/**.py" = ["S101", "PLR2004", "S", "SIM117", "D", "UP", "PLR0913"]
"tests/**.py" = ["S101", "PLR2004", "S", "SIM117", "D", "UP", "PLR0913", "ANN", "N999"]
"src/**/versioning/**.py" = ["N999", ]
3 changes: 2 additions & 1 deletion src/country_workspace/admin/batch.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from django.contrib import admin
from django.http import HttpRequest
from django.urls import reverse

from admin_extra_buttons.buttons import LinkButton
Expand All @@ -23,7 +24,7 @@ class BatchAdmin(BaseModelAdmin):
readonly_fields = ("country_office", "program", "imported_by")
search_fields = ("name",)

def has_add_permission(self, request):
def has_add_permission(self, request: HttpRequest) -> bool:
return False

@link(change_list=False)
Expand Down
30 changes: 16 additions & 14 deletions src/country_workspace/admin/filters.py
Original file line number Diff line number Diff line change
@@ -1,31 +1,33 @@
from django.contrib.admin import SimpleListFilter
from django.contrib.admin import ModelAdmin, SimpleListFilter
from django.db.models import Model, QuerySet
from django.http import HttpRequest
from django.utils.translation import gettext as _


class FailedFilter(SimpleListFilter):
title = "Status"
parameter_name = "failed"

def lookups(self, request, model_admin):
def lookups(self, request: HttpRequest, model_admin: ModelAdmin) -> tuple[tuple[str, str], ...]:
return (
("f", _("Failed")),
("s", _("Success")),
)

def get_title(self):
def get_title(self) -> str:
return self.title

def queryset(self, request, queryset):
def queryset(self, request: HttpRequest, queryset: QuerySet[Model]) -> QuerySet[Model]:
if self.value() == "s":
return queryset.filter(sentry_id__isnull=True)
elif self.value() == "f":
if self.value() == "f":
return queryset.filter(sentry_id__isnull=False)
return queryset

def has_output(self):
def has_output(self) -> bool:
return True

def html_attrs(self):
def html_attrs(self) -> dict[str, str]:
classes = f"adminfilters {self.__class__.__name__.lower()}"
if self.value():
classes += " active"
Expand All @@ -40,29 +42,29 @@ class IsValidFilter(SimpleListFilter):
title = "Valid"
parameter_name = "valid"

def lookups(self, request, model_admin):
def lookups(self, request: HttpRequest, model_admin: ModelAdmin) -> tuple[tuple[str, str], ...]:
return (
("v", _("Valid")),
("i", _("Invalid")),
("u", _("Not Verified")),
)

def get_title(self):
def get_title(self) -> str:
return self.title

def queryset(self, request, queryset):
def queryset(self, request: HttpRequest, queryset: QuerySet[Model]) -> QuerySet[Model]:
if self.value() == "v":
return queryset.filter(last_checked__isnull=False).filter(errors__iexact="{}")
elif self.value() == "i":
if self.value() == "i":
return queryset.filter(last_checked__isnull=False).exclude(errors__iexact="{}")
elif self.value() == "u":
if self.value() == "u":
return queryset.filter(last_checked__isnull=True)
return queryset

def has_output(self):
def has_output(self) -> bool:
return True

def html_attrs(self):
def html_attrs(self) -> dict[str, str]:
classes = f"adminfilters {self.__class__.__name__.lower()}"
if self.value():
classes += " active"
Expand Down
6 changes: 5 additions & 1 deletion src/country_workspace/admin/household.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from typing import TYPE_CHECKING

from django.contrib import admin, messages
from django.http import HttpRequest, HttpResponse
from django.urls import reverse
from django.utils.translation import gettext as _

Expand All @@ -11,6 +12,9 @@
from .base import BaseModelAdmin
from .filters import IsValidFilter

if TYPE_CHECKING:
from django.http import HttpRequest, HttpResponse


@admin.register(Household)
class HouseholdAdmin(BaseModelAdmin):
Expand Down
6 changes: 5 additions & 1 deletion src/country_workspace/admin/individual.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
from typing import TYPE_CHECKING

from django.contrib import admin
from django.urls import reverse

from admin_extra_buttons.buttons import LinkButton
from admin_extra_buttons.decorators import link
from adminfilters.autocomplete import LinkedAutoCompleteFilter

from ..models import Individual
from .base import BaseModelAdmin
from .filters import IsValidFilter

if TYPE_CHECKING:
from admin_extra_buttons.buttons import LinkButton


@admin.register(Individual)
class IndividualAdmin(BaseModelAdmin):
Expand Down
6 changes: 4 additions & 2 deletions src/country_workspace/admin/job.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from typing import Sequence
from typing import TYPE_CHECKING, Sequence

from django.contrib import admin
from django.http import HttpRequest

from adminfilters.autocomplete import AutoCompleteFilter, LinkedAutoCompleteFilter
from django_celery_boost.admin import CeleryTaskModelAdmin
Expand All @@ -10,6 +9,9 @@
from .base import BaseModelAdmin
from .filters import FailedFilter

if TYPE_CHECKING:
from django.http import HttpRequest


@admin.register(AsyncJob)
class AsyncJobAdmin(CeleryTaskModelAdmin, BaseModelAdmin):
Expand Down
5 changes: 3 additions & 2 deletions src/country_workspace/admin/locations.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import logging
from typing import TYPE_CHECKING, Any
from typing import TYPE_CHECKING

from django.contrib import admin
from django.contrib.admin import ModelAdmin, RelatedFieldListFilter
from django.db.models import Field
from django.forms import FileField, FileInput, Form

from adminfilters.autocomplete import AutoCompleteFilter
Expand Down Expand Up @@ -44,7 +45,7 @@ class AreaTypeAdmin(AdminFiltersMixin, admin.ModelAdmin):


class AreaTypeFilter(RelatedFieldListFilter):
def field_choices(self, field: Any, request: "HttpRequest", model_admin: ModelAdmin) -> list[tuple[str, str]]:
def field_choices(self, field: Field, request: "HttpRequest", model_admin: ModelAdmin) -> list[tuple[str, str]]:
if "area_type__country__exact" not in request.GET:
return []
return AreaType.objects.filter(country=request.GET["area_type__country__exact"]).values_list("id", "name")
Expand Down
12 changes: 7 additions & 5 deletions src/country_workspace/admin/user.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.db.models import Q
from django.http import JsonResponse
from django.db.models import Q, QuerySet
from django.http import HttpRequest, JsonResponse

from admin_extra_buttons.decorators import view
from admin_extra_buttons.mixins import ExtraButtonsMixin
Expand All @@ -11,18 +11,20 @@

@admin.register(User)
class UserAdmin(ExtraButtonsMixin, BaseUserAdmin):
def get_search_results(self, request, queryset, search_term):
def get_search_results(
self, request: HttpRequest, queryset: QuerySet[User], search_term: str
) -> tuple[QuerySet[User], bool]:
return super().get_search_results(request, queryset, search_term)

@view()
def autocomplete(self, request):
def autocomplete(self, request: HttpRequest) -> JsonResponse:
filters = {}
if term := request.GET.get("term"):
filters["username__icontains"] = term
qs = User.objects.filter(**filters).distinct()
if program := request.GET.get("program"):
qs = qs.filter(
Q(roles__program__id=program) | Q(is_superuser=True) | Q(roles__country_office__programs__pk=program)
Q(roles__program__id=program) | Q(is_superuser=True) | Q(roles__country_office__programs__pk=program),
)
results = [{"id": user.id, "text": user.username} for user in qs.all()]
res = {"results": results, "pagination": {"more": False}}
Expand Down
20 changes: 11 additions & 9 deletions src/country_workspace/cache/ddt.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import functools
from typing import Any

from django.http import HttpRequest, HttpResponse
from django.utils import timezone
from django.utils.translation import gettext as _

Expand All @@ -10,15 +12,15 @@


class CacheHit:
def __init__(self, data, **kwargs):
def __init__(self, data: dict[str, str], **kwargs: Any) -> None:
self.timestamp = timezone.now()
self.key = data["key"]
self.tenant = state.tenant
self.program = state.program
self.hit = data.get("hit", "")
self.extra = kwargs

def __repr__(self):
def __repr__(self) -> str:
return str(self.__dict__)


Expand All @@ -27,40 +29,40 @@ class WSCachePanel(Panel):
nav_title = _("Cache")
template = "debug_toolbar/panels/cache.html"

def __init__(self, *args, **kwargs):
def __init__(self, *args: Any, **kwargs: Any) -> None:
super().__init__(*args, **kwargs)
self.gets = []
self.sets = []
self.stores = []

def _log(self, action, **kwargs):
def _log(self, action: str, **kwargs: Any) -> None:
if action == "get":
self.gets.append(CacheHit(kwargs))
elif action == "set":
self.sets.append(CacheHit(kwargs))
elif action == "store":
self.stores.append(CacheHit(kwargs, path=kwargs["request"].path))

def enable_instrumentation(self):
def enable_instrumentation(self) -> None:
cache_get.connect(functools.partial(self._log, action="get"))
cache_set.connect(functools.partial(self._log, action="set"))
cache_store.connect(functools.partial(self._log, action="store"))

def process_request(self, request):
def process_request(self, request: HttpRequest) -> HttpResponse:
self.gets = []
self.sets = []
self.stores = []
return self.get_response(request)

@property
def nav_subtitle(self):
def nav_subtitle(self) -> str:
return f"~{len(self.gets)} gets / ~{len(self.sets)} sets"

def generate_stats(self, request, response):
def generate_stats(self, request: HttpRequest, response: HttpResponse) -> None:
self.record_stats(
{
"gets": self.gets,
"sets": self.sets,
"stores": self.stores,
}
},
)
4 changes: 3 additions & 1 deletion src/country_workspace/cache/handlers.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from typing import Any

from django.db.models import Model
from django.db.models.signals import post_save
from django.dispatch import receiver
Expand All @@ -8,7 +10,7 @@


@receiver(post_save)
def update_cache(sender: "type[Model]", instance: Model, **kwargs):
def update_cache(sender: "type[Model]", instance: Model, **kwargs: Any) -> None:
program = None
if isinstance(instance, (Household | Individual | CountryHousehold | CountryIndividual)):
program = instance.program
Expand Down
Loading

0 comments on commit 9cbe0e1

Please sign in to comment.