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
12 changes: 12 additions & 0 deletions stubs/django-filter/@tests/django_settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
SECRET_KEY = "1"

INSTALLED_APPS = (
"django.contrib.contenttypes",
"django.contrib.sites",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.admin.apps.SimpleAdminConfig",
"django.contrib.staticfiles",
"django.contrib.auth",
"django_filters",
)
12 changes: 12 additions & 0 deletions stubs/django-filter/@tests/stubtest_allowlist.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Iterator class attributes: Runtime behavior differs due to Django 5.0 compatibility logic in choice setters
django_filters.fields.ChoiceField.iterator
django_filters.fields.ModelChoiceField.iterator
django_filters.fields.ModelMultipleChoiceField.iterator
django_filters.fields.MultipleChoiceField.iterator

# Lookup NamedTuple: Parameter name mismatch between inferred stub and runtime
django_filters.fields.Lookup.__new__
django_filters.fields.Lookup.__doc__

# ChoiceIteratorMixin.choices: Cannot define choices property due to incompatibility with base class ChoiceField
django_filters.fields.ChoiceIteratorMixin.choices
7 changes: 7 additions & 0 deletions stubs/django-filter/METADATA.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
version = "25.1.*"
upstream_repository = "https://github.com/carltongibson/django-filter/"
requires = ["django-stubs"]

[tool.stubtest]
mypy_plugins = ["mypy_django_plugin.main"]
mypy_plugins_config = {"django-stubs" = {"django_settings_module" = "@tests.django_settings"}}
10 changes: 10 additions & 0 deletions stubs/django-filter/django_filters/__init__.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from typing import Final

from .filters import *
from .filterset import FilterSet as FilterSet, UnknownFieldBehavior as UnknownFieldBehavior

__version__: Final[str]

def parse_version(version: str) -> tuple[str | int]: ...

VERSION: tuple[str | int, ...]
1 change: 1 addition & 0 deletions stubs/django-filter/django_filters/compat.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
def is_crispy() -> bool: ...
17 changes: 17 additions & 0 deletions stubs/django-filter/django_filters/conf.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from _typeshed import Unused
from typing import Any

DEFAULTS: dict[str, Any] # Configuration values can be strings, booleans, callables, etc.
DEPRECATED_SETTINGS: list[str]

def is_callable(value: Any) -> bool: ... # Accepts any value to test if it's callable

class Settings:
# Setting values can be of any type, so getter and setter methods return/accept Any
def __getattr__(self, name: str) -> Any: ... # Returns setting values of various types
def get_setting(self, setting: str) -> Any: ... # Setting values vary by configuration option
def change_setting(
self, setting: str, value: Any, enter: bool, **kwargs: Unused
) -> None: ... # Accepts any setting value type

settings: Settings
6 changes: 6 additions & 0 deletions stubs/django-filter/django_filters/constants.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from typing import Any, Final

# String constant used to indicate all model fields should be included
ALL_FIELDS: Final[str] = "__all__"
# Collection of values considered empty by Django filters - tuple type allows various empty containers
EMPTY_VALUES: Final[Any] = ...
8 changes: 8 additions & 0 deletions stubs/django-filter/django_filters/exceptions.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from typing import Any

from django.core.exceptions import FieldError
from django.db import models

class FieldLookupError(FieldError):
# Field type params are runtime-determined
def __init__(self, model_field: models.Field[Any, Any], lookup_expr: str) -> None: ...
100 changes: 100 additions & 0 deletions stubs/django-filter/django_filters/fields.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
from collections.abc import Sequence
from typing import Any, NamedTuple
from typing_extensions import TypeAlias

from django import forms

DJANGO_50: bool

# Ref: django-stubs/forms/fields.pyi
# Problem: attribute `widget` is always of type `Widget` after field instantiation.
# However, on class level it can be set to `Type[Widget]` too.
# If we annotate it as `Union[Widget, Type[Widget]]`, every code that uses field
# instances will not typecheck.
# If we annotate it as `Widget`, any widget subclasses that do e.g.
# `widget = Select` will not typecheck.
# `Any` gives too much freedom, but does not create false positives.
_ClassLevelWidget: TypeAlias = Any

class RangeField(forms.MultiValueField):
widget: _ClassLevelWidget = ...
def __init__(
self, fields: tuple[forms.Field, forms.Field] | None = None, *args: Any, **kwargs: Any
) -> None: ... # Args/kwargs can be any field params, passes to parent
def compress(self, data_list: list[Any] | None) -> slice | None: ... # Data list elements can be any field value type

class DateRangeField(RangeField):
widget: _ClassLevelWidget = ...
def __init__(self, *args: Any, **kwargs: Any) -> None: ... # Args/kwargs can be any field params for parent
def compress(self, data_list: list[Any] | None) -> slice | None: ... # Date values in list can be any date type

class DateTimeRangeField(RangeField):
widget: _ClassLevelWidget = ...
def __init__(self, *args: Any, **kwargs: Any) -> None: ... # Args/kwargs can be any field params for parent

class IsoDateTimeRangeField(RangeField):
widget: _ClassLevelWidget = ...
def __init__(self, *args: Any, **kwargs: Any) -> None: ... # Args/kwargs can be any field params for parent

class TimeRangeField(RangeField):
widget: _ClassLevelWidget = ...
def __init__(self, *args: Any, **kwargs: Any) -> None: ... # Args/kwargs can be any field params for parent

class Lookup(NamedTuple):
value: Any # Lookup values can be any filterable type
lookup_expr: str

class LookupChoiceField(forms.MultiValueField):
def __init__(
self, field: forms.Field, lookup_choices: Sequence[tuple[str, str]], *args: Any, **kwargs: Any
) -> None: ... # Args/kwargs can be any field params, uses kwargs for empty_label
def compress(self, data_list: list[Any] | None) -> Lookup | None: ... # Data list can contain any lookup components

class IsoDateTimeField(forms.DateTimeField):
ISO_8601: str
input_formats: list[str]
def strptime(self, value: str, format: str) -> Any: ... # Returns datetime objects or parsing results

class BaseCSVField(forms.Field):
base_widget_class: _ClassLevelWidget = ...
def __init__(self, *args: Any, **kwargs: Any) -> None: ... # Args/kwargs can be any field params for widget config
def clean(self, value: Any) -> Any: ... # Cleaned values can be any valid field type

class BaseRangeField(BaseCSVField):
widget: _ClassLevelWidget = ...
def clean(self, value: Any) -> Any: ... # Input and output values can be any range type

class ChoiceIterator:
field: ChoiceField
choices: Sequence[tuple[Any, str]] # Choice values can be any type (int, str, Model, etc.)
def __init__(
self, field: ChoiceField, choices: Sequence[tuple[Any, str]]
) -> None: ... # Choice values can be any selectable type
def __iter__(self) -> Any: ... # Iterator yields choice tuples with any value types
def __len__(self) -> int: ...

class ModelChoiceIterator(forms.models.ModelChoiceIterator):
def __iter__(self) -> Any: ... # Iterator yields choice tuples with any value types
def __len__(self) -> int: ...

class ChoiceIteratorMixin:
null_label: str | None
null_value: Any # Null choice values can be any type (None, empty string, etc.)
def __init__(self, *args: Any, **kwargs: Any) -> None: ... # Args/kwargs can be any field params for null config

class ChoiceField(ChoiceIteratorMixin, forms.ChoiceField):
iterator = ChoiceIterator
empty_label: str | None
def __init__(self, *args: Any, **kwargs: Any) -> None: ... # Args/kwargs can be any field params for label config

class MultipleChoiceField(ChoiceIteratorMixin, forms.MultipleChoiceField):
iterator = ChoiceIterator
empty_label: str | None
def __init__(self, *args: Any, **kwargs: Any) -> None: ... # Args/kwargs can be any field params, sets empty_label

class ModelChoiceField(ChoiceIteratorMixin, forms.ModelChoiceField[Any]):
iterator = ModelChoiceIterator
def to_python(self, value: Any) -> Any: ... # Converts any input to Python model objects or values

class ModelMultipleChoiceField(ChoiceIteratorMixin, forms.ModelMultipleChoiceField[Any]):
iterator = ModelChoiceIterator
Loading
Loading