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

Improve static code checks #548

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
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
40 changes: 20 additions & 20 deletions jazzmin/templatetags/jazzmin.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@


@register.simple_tag(takes_context=True)
def get_side_menu(context: Context, using: str = "available_apps") -> List[Dict]:
def get_side_menu(context: Context, using: str = "available_apps") -> List[Dict[str, Any]]:
"""
Get the list of apps and models to render out in the side menu and on the dashboard page

Expand Down Expand Up @@ -120,7 +120,7 @@ def get_side_menu(context: Context, using: str = "available_apps") -> List[Dict]


@register.simple_tag
def get_top_menu(user: AbstractUser, admin_site: str = "admin") -> List[Dict]:
def get_top_menu(user: AbstractUser, admin_site: str = "admin") -> List[Dict[str, Any]]:
"""
Produce the menu for the top nav bar
"""
Expand All @@ -129,7 +129,7 @@ def get_top_menu(user: AbstractUser, admin_site: str = "admin") -> List[Dict]:


@register.simple_tag
def get_user_menu(user: AbstractUser, admin_site: str = "admin") -> List[Dict]:
def get_user_menu(user: AbstractUser, admin_site: str = "admin") -> List[Dict[str, Any]]:
"""
Produce the menu for the user dropdown
"""
Expand All @@ -144,7 +144,7 @@ def get_user_menu(user: AbstractUser, admin_site: str = "admin") -> List[Dict]:


@register.simple_tag
def get_jazzmin_settings(request: WSGIRequest) -> Dict:
def get_jazzmin_settings(request: WSGIRequest) -> Dict[str, Any]:
"""
Get Jazzmin settings, update any defaults from the request, and return
"""
Expand All @@ -164,7 +164,7 @@ def get_jazzmin_settings(request: WSGIRequest) -> Dict:


@register.simple_tag
def get_jazzmin_ui_tweaks() -> Dict:
def get_jazzmin_ui_tweaks() -> Dict[str, Any]:
"""
Return Jazzmin ui tweaks
"""
Expand All @@ -190,13 +190,13 @@ def get_user_avatar(user: AbstractUser) -> str:
"""
no_avatar = static("vendor/adminlte/img/user2-160x160.jpg")
options = get_settings()
avatar_field_name: Optional[Union[str, Callable]] = options.get("user_avatar")
avatar_field_name: Optional[Union[str, Callable[[AbstractUser], str]]] = options.get("user_avatar")

if not avatar_field_name:
return no_avatar

if callable(avatar_field_name):
return avatar_field_name(user)
return str, avatar_field_name(user)

# If we find the property directly on the user model (imagefield or URLfield)
avatar_field = getattr(user, avatar_field_name, None)
Expand All @@ -206,17 +206,17 @@ def get_user_avatar(user: AbstractUser) -> str:
if isinstance(avatar_field, str):
return avatar_field
elif hasattr(avatar_field, "url"):
return avatar_field.url
return str, avatar_field.url
elif callable(avatar_field):
return avatar_field()
return str, avatar_field()

logger.warning("Avatar field must be an ImageField/URLField on the user model, or a callable")

return no_avatar


@register.simple_tag
def jazzmin_paginator_number(change_list: ChangeList, i: int) -> SafeText:
def jazzmin_paginator_number(change_list: ChangeList, i: Union[int, str]) -> SafeText:
"""
Generate an individual page index link in a paginated list.
"""
Expand Down Expand Up @@ -267,7 +267,7 @@ def jazzmin_paginator_number(change_list: ChangeList, i: int) -> SafeText:


@register.simple_tag
def admin_extra_filters(cl: ChangeList) -> Dict:
def admin_extra_filters(cl: ChangeList) -> Dict[str, Any]:
"""
Return the dict of used filters which is not included in list_filters form
"""
Expand Down Expand Up @@ -433,11 +433,11 @@ def can_view_self(perms: PermWrapper) -> bool:


@register.simple_tag
def header_class(header: Dict, forloop: Dict) -> str:
def header_class(header: Dict[str, Any], forloop: Dict[str, Any]) -> str:
"""
Adds CSS classes to header HTML element depending on its attributes
"""
classes = []
classes: List[str] = []
sorted, asc, desc = (
header.get("sorted"),
header.get("ascending"),
Expand Down Expand Up @@ -473,27 +473,27 @@ def app_is_installed(app: str) -> bool:


@register.simple_tag
def action_message_to_list(action: LogEntry) -> List[Dict]: # noqa: C901
def action_message_to_list(action: LogEntry) -> List[Dict[str, Any]]: # noqa: C901
"""
Retrieves a formatted list with all actions taken by a user given a log entry object
"""
messages = []
messages: List[Dict[str, Any]] = []

def added(x: str) -> Dict:
def added(x: str) -> Dict[str, Any]:
return {
"msg": x,
"icon": "plus-circle",
"colour": "success",
}

def changed(x: str) -> Dict:
def changed(x: str) -> Dict[str, Any]:
return {
"msg": x,
"icon": "edit",
"colour": "blue",
}

def deleted(x: str) -> Dict:
def deleted(x: str) -> Dict[str, Any]:
return {
"msg": x,
"icon": "trash",
Expand Down Expand Up @@ -540,7 +540,7 @@ def style_bold_first_word(message: str) -> SafeText:
message_words = escape(message).split()

if not len(message_words):
return ""
return mark_safe("")

message_words[0] = "<strong>{}</strong>".format(message_words[0])

Expand All @@ -551,4 +551,4 @@ def style_bold_first_word(message: str) -> SafeText:

@register.filter
def unicode_slugify(message: str) -> str:
return slugify(message, allow_unicode=True)
return str, slugify(message, allow_unicode=True)
11 changes: 7 additions & 4 deletions jazzmin/widgets.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from typing import Any, Dict, Optional

from django import forms
from django.forms.widgets import Select, SelectMultiple

Expand All @@ -6,7 +8,7 @@ class JazzminSelect(Select):
template_name = "jazzmin/widgets/select.html"

@property
def media(self):
def media(self) -> forms.Media:
return forms.Media(
css={"all": ("vendor/select2/css/select2.min.css",)},
js=("vendor/select2/js/select2.min.js",),
Expand All @@ -16,12 +18,13 @@ def media(self):
class JazzminSelectMultiple(SelectMultiple):
template_name = "jazzmin/widgets/select.html"

def build_attrs(self, base_attrs, extra_attrs=None):
def build_attrs(self, base_attrs: Dict[str, Any], extra_attrs: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
extra_attrs = extra_attrs or {}
extra_attrs["multiple"] = "multiple"
return {**base_attrs, **(extra_attrs or {})}
return {**base_attrs, **extra_attrs}

@property
def media(self):
def media(self) -> forms.Media:
return forms.Media(
css={"all": ("vendor/select2/css/select2.min.css",)},
js=("vendor/select2/js/select2.min.js",),
Expand Down
21 changes: 21 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,26 @@ norecursedirs = ".git .tox _resource .mypy_cache build dist docs"
DJANGO_SETTINGS_MODULE = "tests.test_app.library.settings"
FAIL_INVALID_TEMPLATE_VARS = 1

[tool.mypy]
python_version = "3.11"
disallow_any_generics = true
disallow_untyped_calls = true
disallow_untyped_defs = true
disallow_untyped_decorators = false
ignore_errors = false
ignore_missing_imports = true
implicit_reexport = false
strict_optional = true
strict_equality = true
no_implicit_optional = true
warn_unused_ignores = true
warn_redundant_casts = true
warn_unused_configs = true
warn_unreachable = true
warn_no_return = true
warn_return_any = false
plugins = []

[tool.ruff]
target-version = "py38"
line-length = 120
Expand Down Expand Up @@ -132,6 +152,7 @@ setenv =
commands =
{envpython} -m ruff format --check jazzmin
{envpython} -m ruff check jazzmin
{envpython} -m mypy jazzmin tests --ignore-missing-imports
{envpython} -m pytest
deps =
django-jazzmin # Installing self to allow automatic getting of `version` which needs `django-jazzmin` to be install to get information about the package.
Expand Down
2 changes: 1 addition & 1 deletion tests/test_app/library/books/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@
class BooksConfig(AppConfig):
name = "tests.test_app.library.books"

def ready(self):
def ready(self) -> None:
from . import receivers # NOQA
10 changes: 5 additions & 5 deletions tests/test_app/library/books/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
class Genre(models.Model):
name = models.CharField(max_length=200, help_text="Enter a book genre (e.g. Science Fiction)")

def __str__(self):
def __str__(self) -> str:
return self.name


Expand All @@ -26,10 +26,10 @@ class Book(models.Model):
last_print = models.DateField(auto_now_add=True)
pages = models.IntegerField(null=True)

def get_absolute_url(self):
def get_absolute_url(self) -> str:
return reverse("admin:books_book_change", args=(self.id,))

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


Expand All @@ -42,8 +42,8 @@ class Author(models.Model):
class Meta:
ordering = ("last_name", "first_name")

def get_absolute_url(self):
def get_absolute_url(self) -> str:
return reverse("admin:books_author_change", args=(self.id,))

def __str__(self):
def __str__(self) -> str:
return "{}, {}".format(self.first_name, self.last_name)
4 changes: 3 additions & 1 deletion tests/test_app/library/books/receivers.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
from typing import Any, Type

from django.contrib.auth.models import User
from django.db.models.signals import pre_save
from django.dispatch import receiver


@receiver(pre_save, sender=User)
def add_user_levels_actions(sender, instance, **kwargs):
def add_user_levels_actions(sender: Type[User], instance: User, **kwargs: Any) -> None:
"""
Dont allow our test user to change their password
"""
Expand Down
4 changes: 3 additions & 1 deletion tests/test_app/library/loans/views.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
from typing import Any, Dict

from django.contrib.admin.sites import site
from django.views.generic import TemplateView


class CustomView(TemplateView):
template_name = "loans/custom.html"

def get_context_data(self, **kwargs):
def get_context_data(self, **kwargs: Any) -> Dict[str, Any]:
ctx = super().get_context_data(**kwargs)
ctx.update(site.each_context(self.request))
return ctx
4 changes: 2 additions & 2 deletions tests/test_app/library/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
from django.conf.urls.i18n import i18n_patterns
from django.contrib import admin, messages
from django.contrib.auth import views as auth_views
from django.http import HttpResponseRedirect
from django.http import HttpRequest, HttpResponseRedirect
from django.urls import include, path, re_path, reverse
from django.views.generic import RedirectView
from django.views.static import serve


def make_messages(request):
def make_messages(request: HttpRequest) -> HttpResponseRedirect:
messages.add_message(request, messages.INFO, "Info message")
messages.add_message(request, messages.ERROR, "Error message")
messages.add_message(request, messages.WARNING, "Warning message")
Expand Down
Loading