From 7d3f81a8002275b76f4f6da288e202cdd6789771 Mon Sep 17 00:00:00 2001 From: Jan Pieter Waagmeester Date: Sat, 21 Dec 2024 20:46:28 +0100 Subject: [PATCH] Down to 'Found 51 errors in 12 files (checked 27 source files)' --- .pre-commit-config.yaml | 5 +++ django_tables2/columns/base.py | 13 ++++-- django_tables2/columns/booleancolumn.py | 22 +++++++---- django_tables2/columns/datecolumn.py | 12 ++++-- django_tables2/columns/datetimecolumn.py | 8 +++- django_tables2/columns/emailcolumn.py | 8 +++- django_tables2/columns/filecolumn.py | 13 +++--- django_tables2/columns/jsoncolumn.py | 16 ++++++-- django_tables2/columns/templatecolumn.py | 25 +++++++++--- django_tables2/columns/timecolumn.py | 12 ++++-- django_tables2/columns/urlcolumn.py | 1 + django_tables2/config.py | 13 ++++-- django_tables2/export/export.py | 6 +-- django_tables2/export/views.py | 6 ++- django_tables2/paginators.py | 4 +- django_tables2/tables.py | 50 ++++++++++++++++-------- django_tables2/utils.py | 20 +++++++--- django_tables2/views.py | 25 +++++++----- pyproject.toml | 6 +++ 19 files changed, 185 insertions(+), 80 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 70ab4fa9..6545a9b7 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -32,3 +32,8 @@ repos: - id: pyupgrade args: [--py39-plus] + - repo: https://github.com/pre-commit/mirrors-mypy + rev: v1.14.0 + hooks: + - id: mypy + args: ["django_tables2"] \ No newline at end of file diff --git a/django_tables2/columns/base.py b/django_tables2/columns/base.py index a5f2556d..713722f6 100644 --- a/django_tables2/columns/base.py +++ b/django_tables2/columns/base.py @@ -31,7 +31,7 @@ class Library: def __init__(self): self.columns = [] - def register(self, column: "Column"): + def register(self, column: "type[Column]"): if not hasattr(column, "from_field"): raise ImproperlyConfigured(f"{column.__class__.__name__} is not a subclass of Column") self.columns.append(column) @@ -73,7 +73,7 @@ class LinkTransform: def __init__( self, - url: Union[callable, None] = None, + url: Union[Callable, None] = None, accessor: Union[str, Accessor, None] = None, attrs: Union[dict, None] = None, reverse_args: Union[list, tuple, None] = None, @@ -259,7 +259,7 @@ class Blog(models.Model): # Tracks each time a Column instance is created. Used to retain column order. creation_counter = 0 - empty_values = (None, "") + empty_values: tuple[Any, ...] = (None, "") # by default, contents are not wrapped in an -tag. link = None @@ -358,7 +358,7 @@ def footer(self, bound_column: "BoundColumn", table: "Table") -> Union[str, None return "" - def render(self, value: Any) -> Any: + def render(self, value: Any, **kwargs) -> "Union[SafeString, str]": """ Return the content for a specific cell. @@ -418,6 +418,7 @@ def from_field(cls, field, **kwargs) -> "Union[Column, None]": # column if this class was asked directly. if cls is Column: return cls(**kwargs) + return None class BoundColumn: @@ -440,6 +441,10 @@ class SimpleTable(tables.Table): """ + render: Callable + order: Callable + value: Callable + def __init__(self, table: "Table", column: Column, name: str): self._table = table self.column = column diff --git a/django_tables2/columns/booleancolumn.py b/django_tables2/columns/booleancolumn.py index fba539e1..518ace5a 100644 --- a/django_tables2/columns/booleancolumn.py +++ b/django_tables2/columns/booleancolumn.py @@ -1,11 +1,14 @@ -from typing import Union +from typing import TYPE_CHECKING, Any, Union from django.db import models from django.utils.html import escape, format_html -from django.utils.safestring import SafeString from ..utils import AttributeDict -from .base import Column, library +from .base import BoundColumn, Column, library + +if TYPE_CHECKING: + from django.db.models import Field + from django.utils.safestring import SafeString @library.register @@ -33,7 +36,7 @@ def __init__(self, null=False, yesno="✔,✘", **kwargs): kwargs["empty_values"] = () super().__init__(**kwargs) - def _get_bool_value(self, record, value, bound_column) -> bool: + def _get_bool_value(self, record: Any, value: Any, bound_column: BoundColumn) -> bool: # If record is a model, we need to check if it has choices defined. if hasattr(record, "_meta"): field = bound_column.accessor.get_field(record) @@ -45,7 +48,7 @@ def _get_bool_value(self, record, value, bound_column) -> bool: return bool(value) - def render(self, value, record, bound_column) -> SafeString: + def render(self, value: Any, record: Any, bound_column: BoundColumn) -> "SafeString": value = self._get_bool_value(record, value, bound_column) text = self.yesno[int(not value)] attrs = {"class": str(value).lower()} @@ -60,9 +63,12 @@ def value(self, record, value, bound_column) -> str: return str(self._get_bool_value(record, value, bound_column)) @classmethod - def from_field(cls, field, **kwargs) -> "Union[BooleanColumn, None]": - if isinstance(field, models.NullBooleanField): - return cls(null=True, **kwargs) + def from_field(cls, field: "Field", **kwargs) -> "Union[BooleanColumn, None]": + if NullBooleanField := getattr(models, "NullBooleanField", None): + if isinstance(field, NullBooleanField): + return cls(null=True, **kwargs) if isinstance(field, models.BooleanField): return cls(null=getattr(field, "null", False), **kwargs) + + return None diff --git a/django_tables2/columns/datecolumn.py b/django_tables2/columns/datecolumn.py index 84404d61..788719fc 100644 --- a/django_tables2/columns/datecolumn.py +++ b/django_tables2/columns/datecolumn.py @@ -1,10 +1,13 @@ -from typing import Union +from typing import TYPE_CHECKING, Union from django.db import models from .base import library from .templatecolumn import TemplateColumn +if TYPE_CHECKING: + from django.db.models import Field + @library.register class DateColumn(TemplateColumn): @@ -21,10 +24,11 @@ class DateColumn(TemplateColumn): def __init__(self, format: Union[str, None] = None, short: bool = True, *args, **kwargs): if format is None: format = "SHORT_DATE_FORMAT" if short else "DATE_FORMAT" - template = '{{ value|date:"%s"|default:default }}' % format - super().__init__(template_code=template, *args, **kwargs) + kwargs["template_code"] = '{{ value|date:"%s"|default:default }}' % format + super().__init__(*args, **kwargs) @classmethod - def from_field(cls, field, **kwargs) -> "Union[DateColumn, None]": + def from_field(cls, field: "Field", **kwargs) -> "Union[DateColumn, None]": if isinstance(field, models.DateField): return cls(**kwargs) + return None diff --git a/django_tables2/columns/datetimecolumn.py b/django_tables2/columns/datetimecolumn.py index 8f3f097d..4acceeb3 100644 --- a/django_tables2/columns/datetimecolumn.py +++ b/django_tables2/columns/datetimecolumn.py @@ -1,10 +1,13 @@ -from typing import Union +from typing import TYPE_CHECKING, Union from django.db import models from .base import library from .templatecolumn import TemplateColumn +if TYPE_CHECKING: + from django.db.models.fields import Field + @library.register class DateTimeColumn(TemplateColumn): @@ -25,6 +28,7 @@ def __init__(self, format: Union[str, None] = None, short: bool = True, *args, * super().__init__(template_code=template, *args, **kwargs) @classmethod - def from_field(cls, field, **kwargs) -> "Union[DateTimeColumn, None]": + def from_field(cls, field: "Field", **kwargs) -> "Union[DateTimeColumn, None]": if isinstance(field, models.DateTimeField): return cls(**kwargs) + return None diff --git a/django_tables2/columns/emailcolumn.py b/django_tables2/columns/emailcolumn.py index 79c5b03f..98ab6c6d 100644 --- a/django_tables2/columns/emailcolumn.py +++ b/django_tables2/columns/emailcolumn.py @@ -1,10 +1,13 @@ -from typing import Union +from typing import TYPE_CHECKING, Union from django.db import models from .base import library from .linkcolumn import BaseLinkColumn +if TYPE_CHECKING: + from django.db.models import Field + @library.register class EmailColumn(BaseLinkColumn): @@ -37,6 +40,7 @@ def get_url(self, value) -> str: return f"mailto:{value}" @classmethod - def from_field(cls, field, **kwargs) -> "Union[EmailColumn, None]": + def from_field(cls, field: "Field", **kwargs) -> "Union[EmailColumn, None]": if isinstance(field, models.EmailField): return cls(**kwargs) + return None diff --git a/django_tables2/columns/filecolumn.py b/django_tables2/columns/filecolumn.py index c4f6df06..d16c2deb 100644 --- a/django_tables2/columns/filecolumn.py +++ b/django_tables2/columns/filecolumn.py @@ -1,5 +1,5 @@ import os -from typing import Union +from typing import TYPE_CHECKING, Union from django.db import models from django.utils.html import format_html @@ -9,6 +9,9 @@ from .base import library from .linkcolumn import BaseLinkColumn +if TYPE_CHECKING: + from django.db.models import Field + @library.register class FileColumn(BaseLinkColumn): @@ -52,13 +55,12 @@ def text_value(self, record, value): return os.path.basename(value.name) return super().text_value(record, value) - def render(self, record, value) -> SafeString: + def render(self, record, value) -> "SafeString": attrs = AttributeDict(self.attrs.get("span", {})) classes = [c for c in attrs.get("class", "").split(" ") if c] exists = None - storage = getattr(value, "storage", None) - if storage: + if storage := getattr(value, "storage", None): # we'll assume value is a `django.db.models.fields.files.FieldFile` if self.verify_exists: exists = storage.exists(value.name) @@ -82,6 +84,7 @@ def render(self, record, value) -> SafeString: ) @classmethod - def from_field(cls, field, **kwargs) -> "Union[FileColumn, None]": + def from_field(cls, field: "Field", **kwargs) -> "Union[FileColumn, None]": if isinstance(field, models.FileField): return cls(**kwargs) + return None diff --git a/django_tables2/columns/jsoncolumn.py b/django_tables2/columns/jsoncolumn.py index 3f154865..2e21efd2 100644 --- a/django_tables2/columns/jsoncolumn.py +++ b/django_tables2/columns/jsoncolumn.py @@ -1,5 +1,5 @@ import json -from typing import Union +from typing import TYPE_CHECKING, Union from django.db.models import JSONField from django.utils.html import format_html @@ -9,11 +9,14 @@ from .base import library from .linkcolumn import BaseLinkColumn +if TYPE_CHECKING: + from django.db.models import Field + try: from django.contrib.postgres.fields import HStoreField except ImportError: # psycopg is not available, cannot import from django.contrib.postgres. - HStoreField = object() + HStoreField = None # type: ignore @library.register @@ -48,6 +51,11 @@ def render(self, record, value) -> SafeString: ) @classmethod - def from_field(cls, field, **kwargs) -> "Union[JSONColumn, None]": - if isinstance(field, (JSONField, HStoreField)): + def from_field(cls, field: "Field", **kwargs) -> "Union[JSONColumn, None]": + if ( + isinstance(field, JSONField) + or HStoreField is not None + and isinstance(field, HStoreField) + ): return cls(**kwargs) + return None diff --git a/django_tables2/columns/templatecolumn.py b/django_tables2/columns/templatecolumn.py index 3c7e3b29..a7173f4a 100644 --- a/django_tables2/columns/templatecolumn.py +++ b/django_tables2/columns/templatecolumn.py @@ -1,9 +1,15 @@ +from typing import TYPE_CHECKING, Any, Union + from django.template import Context, Template from django.template.loader import get_template from django.utils.html import strip_tags -from django.utils.safestring import SafeString -from .base import Column, library +from .base import BoundColumn, Column, library + +if TYPE_CHECKING: + from django.utils.safestring import SafeString + + from ..tables import Table @library.register @@ -41,7 +47,13 @@ class ExampleTable(tables.Table): empty_values = () - def __init__(self, template_code=None, template_name=None, extra_context=None, **extra): + def __init__( + self, + template_code: Union[str, None] = None, + template_name: Union[str, None] = None, + extra_context: Union[dict, None] = None, + **extra + ): super().__init__(**extra) self.template_code = template_code self.template_name = template_name @@ -50,7 +62,9 @@ def __init__(self, template_code=None, template_name=None, extra_context=None, * if not self.template_code and not self.template_name: raise ValueError("A template must be provided") - def render(self, record, table, value, bound_column, **kwargs) -> SafeString: + def render( + self, record, table: "Table", value: Any, bound_column: BoundColumn, **kwargs + ) -> "Union[SafeString, str]": # If the table is being rendered using `render_table`, it hackily # attaches the context to the table as a gift to `TemplateColumn`. context = getattr(table, "context", Context()) @@ -65,8 +79,9 @@ def render(self, record, table, value, bound_column, **kwargs) -> SafeString: with context.update(additional_context): if self.template_code: return Template(self.template_code).render(context) - else: + elif self.template_name: return get_template(self.template_name).render(context.flatten()) + return "" def value(self, **kwargs) -> str: """ diff --git a/django_tables2/columns/timecolumn.py b/django_tables2/columns/timecolumn.py index 5a1330ca..69838a32 100644 --- a/django_tables2/columns/timecolumn.py +++ b/django_tables2/columns/timecolumn.py @@ -1,10 +1,13 @@ -from typing import Union +from typing import TYPE_CHECKING, Union from django.db import models from .base import library from .templatecolumn import TemplateColumn +if TYPE_CHECKING: + from django.db.models import Field + @library.register class TimeColumn(TemplateColumn): @@ -19,10 +22,11 @@ class TimeColumn(TemplateColumn): def __init__(self, format: Union[str, None] = None, *args, **kwargs): if format is None: format = "TIME_FORMAT" - template = '{{ value|date:"%s"|default:default }}' % format - super().__init__(template_code=template, *args, **kwargs) + kwargs["template_code"] = '{{ value|date:"%s"|default:default }}' % format + super().__init__(*args, **kwargs) @classmethod - def from_field(cls, field, **kwargs) -> "Union[TimeColumn, None]": + def from_field(cls, field: "Field", **kwargs) -> "Union[TimeColumn, None]": if isinstance(field, models.TimeField): return cls(**kwargs) + return None diff --git a/django_tables2/columns/urlcolumn.py b/django_tables2/columns/urlcolumn.py index 77d76c02..fb16ad91 100644 --- a/django_tables2/columns/urlcolumn.py +++ b/django_tables2/columns/urlcolumn.py @@ -33,3 +33,4 @@ def get_url(self, value: str) -> str: def from_field(cls, field, **kwargs) -> "Union[URLColumn, None]": if isinstance(field, models.URLField): return cls(**kwargs) + return None diff --git a/django_tables2/config.py b/django_tables2/config.py index 097149fc..1dbcb781 100644 --- a/django_tables2/config.py +++ b/django_tables2/config.py @@ -1,5 +1,12 @@ +from typing import TYPE_CHECKING, Union + from django.core.paginator import EmptyPage, PageNotAnInteger +if TYPE_CHECKING: + from django.http import HttpRequest + + from .tables import Table + class RequestConfig: """ @@ -26,11 +33,11 @@ class RequestConfig: """ - def __init__(self, request, paginate=True): + def __init__(self, request: "HttpRequest", paginate: Union[bool, dict, tuple, list] = True): self.request = request self.paginate = paginate - def configure(self, table): + def configure(self, table: "Table"): """ Configure a table using information from the request. @@ -43,7 +50,7 @@ def configure(self, table): if order_by: table.order_by = order_by if self.paginate: - if hasattr(self.paginate, "items"): + if isinstance(self.paginate, (dict, tuple, list)): kwargs = dict(self.paginate) else: kwargs = {} diff --git a/django_tables2/export/export.py b/django_tables2/export/export.py index 00be231e..dc54f961 100644 --- a/django_tables2/export/export.py +++ b/django_tables2/export/export.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING, Union +from typing import IO, TYPE_CHECKING, Union from django.core.exceptions import ImproperlyConfigured from django.http import HttpResponse @@ -61,7 +61,7 @@ def table_to_dataset( ) -> Dataset: """Transform a table to a tablib dataset.""" - def default_dataset_title(): + def default_dataset_title() -> str: try: return table.Meta.model._meta.verbose_name_plural.title() except AttributeError: @@ -88,7 +88,7 @@ def content_type(self) -> str: """Return the content type for the current export format.""" return self.FORMATS[self.format] - def export(self): + def export(self) -> Union[str, bytes]: """Return the string/bytes for the current export format.""" return self.dataset.export(self.format) diff --git a/django_tables2/export/views.py b/django_tables2/export/views.py index d266e4dd..822f04cc 100644 --- a/django_tables2/export/views.py +++ b/django_tables2/export/views.py @@ -1,3 +1,5 @@ +from typing import Any + from django.http import HttpResponse from .export import TableExport @@ -40,7 +42,7 @@ class Table(tables.Table): def get_export_filename(self, export_format: str) -> str: return f"{self.export_name}.{export_format}" - def get_dataset_kwargs(self) -> dict: + def get_dataset_kwargs(self) -> dict[str, Any]: return self.dataset_kwargs def create_export(self, export_format: str) -> HttpResponse: @@ -53,7 +55,7 @@ def create_export(self, export_format: str) -> HttpResponse: return exporter.response(filename=self.get_export_filename(export_format)) - def render_to_response(self, context, **kwargs) -> HttpResponse: + def render_to_response(self, context: dict, **kwargs) -> HttpResponse: export_format = self.request.GET.get(self.export_trigger_param, None) if self.export_class.is_valid_format(export_format): return self.create_export(export_format) diff --git a/django_tables2/paginators.py b/django_tables2/paginators.py index cc4331fd..5d09820d 100644 --- a/django_tables2/paginators.py +++ b/django_tables2/paginators.py @@ -64,7 +64,7 @@ def __init__(self, object_list, per_page, look_ahead=None, **kwargs): super().__init__(object_list, per_page, **kwargs) - def validate_number(self, number: Union[float, int]) -> int: + def validate_number(self, number: Union[int, float, str]) -> int: """Validate the given 1-based page number.""" try: if isinstance(number, float) and not number.is_integer(): @@ -76,7 +76,7 @@ def validate_number(self, number: Union[float, int]) -> int: raise EmptyPage(_("That page number is less than 1")) return number - def page(self, number: Union[float, int]) -> Page: + def page(self, number: Union[int, str]) -> Page: # Number might be None, because the total number of pages is not known in this paginator. # If an unknown page is requested, serve the first page. number = self.validate_number(number or 1) diff --git a/django_tables2/tables.py b/django_tables2/tables.py index 8ce29475..5943a02b 100644 --- a/django_tables2/tables.py +++ b/django_tables2/tables.py @@ -1,6 +1,7 @@ import copy from collections import OrderedDict from itertools import count +from typing import TYPE_CHECKING, Union from django.conf import settings from django.core.paginator import Paginator @@ -14,6 +15,11 @@ from .rows import BoundRows from .utils import Accessor, AttributeDict, OrderBy, OrderByTuple, Sequence +if TYPE_CHECKING: + from django.http import HttpRequest + + from .columns.base import BoundColumn + class DeclarativeColumnsMetaclass(type): """ @@ -252,6 +258,9 @@ class Table(metaclass=DeclarativeColumnsMetaclass): with `name` will be removed from the table. """ + request: "Union[HttpRequest, None]" = None + _meta: TableOptions + def __init__( self, data=None, @@ -500,7 +509,7 @@ def value_name(self, value): force_str(row.get_cell_value(column.name), strings_only=True) for column in columns ] - def has_footer(self): + def has_footer(self) -> bool: """ Returns True if any of the columns define a ``_footer`` attribute or a ``render_footer()`` method @@ -508,11 +517,11 @@ def has_footer(self): return self.show_footer and any(column.has_footer() for column in self.columns) @property - def show_header(self): + def show_header(self) -> bool: return self._show_header if self._show_header is not None else self._meta.show_header @show_header.setter - def show_header(self, value): + def show_header(self, value: bool) -> None: self._show_header = value @property @@ -548,18 +557,25 @@ def order_by_field(self): ) @order_by_field.setter - def order_by_field(self, value): + def order_by_field(self, value: str) -> None: self._order_by_field = value @property - def page_field(self): + def page_field(self) -> str: return self._page_field if self._page_field is not None else self._meta.page_field @page_field.setter - def page_field(self, value): + def page_field(self, value: str) -> None: self._page_field = value - def paginate(self, paginator_class=Paginator, per_page=None, page=1, *args, **kwargs): + def paginate( + self, + paginator_class: type[Paginator] = Paginator, + per_page: Union[int, None] = None, + page: int = 1, + *args, + **kwargs, + ): """ Paginates the table using a paginator and creates a ``page`` property containing information for the current page. @@ -595,23 +611,23 @@ def per_page_field(self, value): self._per_page_field = value @property - def prefix(self): + def prefix(self) -> str: return self._prefix if self._prefix is not None else self._meta.prefix @prefix.setter - def prefix(self, value): + def prefix(self, value: str) -> None: self._prefix = value @property - def prefixed_order_by_field(self): + def prefixed_order_by_field(self) -> str: return f"{self.prefix}{self.order_by_field}" @property - def prefixed_page_field(self): + def prefixed_page_field(self) -> str: return f"{self.prefix}{self.page_field}" @property - def prefixed_per_page_field(self): + def prefixed_per_page_field(self) -> str: return f"{self.prefix}{self.per_page_field}" @property @@ -626,25 +642,25 @@ def sequence(self, value): self._sequence = value @property - def orderable(self): + def orderable(self) -> bool: if self._orderable is not None: return self._orderable else: return self._meta.orderable @orderable.setter - def orderable(self, value): + def orderable(self, value: Union[bool, None]): self._orderable = value @property - def template_name(self): + def template_name(self) -> str: if self._template is not None: return self._template else: return self._meta.template_name @template_name.setter - def template_name(self, value): + def template_name(self, value: str): self._template = value @property @@ -656,7 +672,7 @@ def paginated_rows(self): return self.page.object_list return self.rows - def get_column_class_names(self, classes_set, bound_column): + def get_column_class_names(self, classes_set, bound_column: "BoundColumn"): """ Returns a set of HTML class names for cells (both ``td`` and ``th``) of a **bound column** in this table. diff --git a/django_tables2/utils.py b/django_tables2/utils.py index 429902f3..48a40df4 100644 --- a/django_tables2/utils.py +++ b/django_tables2/utils.py @@ -4,7 +4,7 @@ from collections.abc import Callable from functools import total_ordering from itertools import chain -from typing import Any +from typing import Any, Union from django.core.exceptions import FieldDoesNotExist from django.db import models @@ -305,12 +305,22 @@ class Accessor(str): "Failed lookup for key [{key}] in {context}, when resolving the accessor {accessor}" ) - def __init__(self, value, callable_args=None, callable_kwargs=None): + def __init__( + self, + value: str, + callable_args: Union[list[Callable], None] = None, + callable_kwargs: Union[dict[str, Callable], None] = None, + ): self.callable_args = callable_args or getattr(value, "callable_args", None) or [] self.callable_kwargs = callable_kwargs or getattr(value, "callable_kwargs", None) or {} super().__init__() - def __new__(cls, value, callable_args=None, callable_kwargs=None): + def __new__( + cls, + value, + callable_args: Union[list[Callable], None] = None, + callable_kwargs: Union[dict[str, Callable], None] = None, + ): instance = super().__new__(cls, value) if cls.LEGACY_SEPARATOR in value: instance.SEPARATOR = cls.LEGACY_SEPARATOR @@ -324,7 +334,7 @@ def __new__(cls, value, callable_args=None, callable_kwargs=None): return instance - def resolve(self, context, safe=True, quiet=False): + def resolve(self, context: Any, safe: bool = True, quiet: bool = False): """ Return an object described by the accessor by traversing the attributes of *context*. @@ -532,7 +542,7 @@ def segment(sequence, aliases): yield tuple([valias]) -def signature(fn: Callable) -> tuple[tuple, str]: +def signature(fn: Callable) -> tuple[tuple[Any, ...], Union[str, None]]: """ Return argument names and the name of the kwargs catch all. diff --git a/django_tables2/views.py b/django_tables2/views.py index b972c0a0..8c8768e2 100644 --- a/django_tables2/views.py +++ b/django_tables2/views.py @@ -1,5 +1,5 @@ from itertools import count -from typing import Any, Optional +from typing import TYPE_CHECKING, Any, Optional, Union from django.core.exceptions import ImproperlyConfigured from django.views.generic.list import ListView @@ -7,6 +7,10 @@ from . import tables from .config import RequestConfig +if TYPE_CHECKING: + from .data import TableData + from .tables import Table + class TableMixinBase: """ @@ -16,13 +20,13 @@ class TableMixinBase: context_table_name = "table" table_pagination = None - def get_context_table_name(self, table): + def get_context_table_name(self, table: "Table") -> str: """ Get the name to use for the table's template variable. """ return self.context_table_name - def get_table_pagination(self, table): + def get_table_pagination(self, table: "Table") -> Union[dict[str, Any], bool]: """ Return pagination options passed to `.RequestConfig`: - True for standard pagination (default), @@ -45,11 +49,12 @@ def get_table_pagination(self, table): if paginate_by is not None: paginate["per_page"] = paginate_by - if hasattr(self, "paginator_class"): - paginate["paginator_class"] = self.paginator_class + if paginator_class := getattr(self, "paginator_class", None): + paginate["paginator_class"] = paginator_class - if getattr(self, "paginate_orphans", 0) != 0: - paginate["orphans"] = self.paginate_orphans + paginate_orphans = getattr(self, "paginate_orphans", 0) + if paginate_orphans != 0: + paginate["orphans"] = paginate_orphans # table_pagination overrides any MultipleObjectMixin attributes if self.table_pagination: @@ -61,7 +66,7 @@ def get_table_pagination(self, table): return paginate - def get_paginate_by(self, table_data) -> Optional[int]: + def get_paginate_by(self, table_data: "TableData") -> Optional[int]: """ Determines the number of items per page, or ``None`` for no pagination. @@ -130,8 +135,8 @@ def get_table_data(self): """ if self.table_data is not None: return self.table_data - elif hasattr(self, "object_list"): - return self.object_list + elif object_list := getattr(self, "object_list", None): + return object_list elif hasattr(self, "get_queryset"): return self.get_queryset() diff --git a/pyproject.toml b/pyproject.toml index aa4949aa..3b925b95 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,2 +1,8 @@ [tool.black] line-length = 100 + +[tool.mypy] +python_version = "3.9" +strict = false +ignore_missing_imports = true +