Skip to content

Commit eb8af63

Browse files
authored
Add stubs for django-import-export (#11709)
1 parent ff4237f commit eb8af63

19 files changed

+766
-0
lines changed

pyrightconfig.stricter.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
"stubs/corus",
4545
"stubs/dateparser",
4646
"stubs/defusedxml",
47+
"stubs/django-import-export",
4748
"stubs/docker",
4849
"stubs/docutils",
4950
"stubs/Flask-SocketIO",
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
version = "3.3.*"
2+
upstream_repository = "https://github.com/django-import-export/django-import-export"
3+
requires = ["django-stubs"] # Add tablib when typed, and update _Incomplete aliases in stubs
4+
5+
[tool.stubtest]
6+
skip = true # Django requires configured settings at runtime
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
__version__: str
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
from _typeshed import Incomplete
2+
from collections.abc import Callable
3+
from logging import Logger
4+
from typing import Any, TypeVar
5+
from typing_extensions import TypeAlias, deprecated
6+
7+
from django.contrib import admin
8+
from django.contrib.admin.helpers import ActionForm
9+
from django.core.files import File
10+
from django.db.models import Model, QuerySet
11+
from django.forms import Form, Media
12+
from django.http.request import HttpRequest
13+
from django.http.response import HttpResponse
14+
from django.template.response import TemplateResponse
15+
from django.urls import URLPattern
16+
17+
from .formats.base_formats import Format
18+
from .mixins import BaseExportMixin, BaseImportMixin
19+
from .results import Result
20+
from .tmp_storages import BaseStorage
21+
22+
Dataset: TypeAlias = Incomplete # tablib.Dataset
23+
logger: Logger
24+
25+
_ModelT = TypeVar("_ModelT", bound=Model)
26+
27+
class ImportExportMixinBase:
28+
base_change_list_template: str
29+
change_list_template: str
30+
def __init__(self, *args: Any, **kwargs: Any) -> None: ...
31+
def init_change_list_template(self) -> None: ...
32+
def get_model_info(self) -> tuple[str, str]: ...
33+
def changelist_view(self, request: HttpRequest, extra_context: dict[str, Any] | None = None) -> HttpResponse: ...
34+
35+
class ImportMixin(BaseImportMixin[_ModelT], ImportExportMixinBase):
36+
import_export_change_list_template: str
37+
import_template_name: str
38+
import_form_class: type[Form] = ...
39+
confirm_form_class: type[Form] = ...
40+
from_encoding: str
41+
skip_admin_log: bool | None
42+
tmp_storage_class: str | type[BaseStorage]
43+
def get_skip_admin_log(self) -> bool: ...
44+
def get_tmp_storage_class(self) -> type[BaseStorage]: ...
45+
def has_import_permission(self, request: HttpRequest) -> bool: ...
46+
def get_urls(self) -> list[URLPattern]: ...
47+
def process_import(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse: ...
48+
def process_dataset(
49+
self,
50+
dataset: Dataset,
51+
confirm_form: Form,
52+
request: HttpRequest,
53+
*args: Any,
54+
rollback_on_validation_errors: bool = False,
55+
**kwargs: Any,
56+
) -> Result: ...
57+
def process_result(self, result: Result, request: HttpRequest) -> HttpResponse: ...
58+
def generate_log_entries(self, result: Result, request: HttpRequest) -> None: ...
59+
def add_success_message(self, result: Result, request: HttpRequest) -> None: ...
60+
def get_import_context_data(self, **kwargs: Any) -> dict[str, Any]: ...
61+
def get_context_data(self, **kwargs: Any) -> dict[str, Any]: ...
62+
@deprecated("Use get_import_form_class instead")
63+
def get_import_form(self) -> type[Form]: ...
64+
@deprecated("Use get_confirm_form_class instead")
65+
def get_confirm_import_form(self) -> type[Form]: ...
66+
@deprecated("Use get_import_form_kwargs or get_confirm_form_kwargs")
67+
def get_form_kwargs(self, form: Form, *args: Any, **kwargs: Any) -> dict[str, Any]: ...
68+
def create_import_form(self, request: HttpRequest) -> Form: ...
69+
def get_import_form_class(self, request: HttpRequest) -> type[Form]: ...
70+
def get_import_form_kwargs(self, request: HttpRequest) -> dict[str, Any]: ...
71+
def get_import_form_initial(self, request: HttpRequest) -> dict[str, Any]: ...
72+
def create_confirm_form(self, request: HttpRequest, import_form: Form | None = None) -> Form: ...
73+
def get_confirm_form_class(self, request: HttpRequest) -> type[Form]: ...
74+
def get_confirm_form_kwargs(self, request: HttpRequest, import_form: Form | None = None) -> dict[str, Any]: ...
75+
def get_confirm_form_initial(self, request: HttpRequest, import_form: Form | None) -> dict[str, Any]: ...
76+
def get_import_data_kwargs(self, request: HttpRequest, *args: Any, **kwargs: Any) -> dict[str, Any]: ...
77+
def write_to_tmp_storage(self, import_file: File[bytes], input_format: Format) -> BaseStorage: ...
78+
def add_data_read_fail_error_to_form(self, form: Form, e: Exception) -> None: ...
79+
def import_action(self, request: HttpRequest, *args: Any, **kwargs: Any) -> TemplateResponse: ...
80+
def changelist_view(self, request: HttpRequest, extra_context: dict[str, Any] | None = None) -> HttpResponse: ...
81+
82+
class ExportMixin(BaseExportMixin[_ModelT], ImportExportMixinBase):
83+
import_export_change_list_template: str | None
84+
export_template_name: str
85+
to_encoding: str | None
86+
export_form_class: type[Form] = ...
87+
def get_urls(self) -> list[URLPattern]: ...
88+
def has_export_permission(self, request: HttpRequest) -> bool: ...
89+
def get_export_queryset(self, request: HttpRequest) -> QuerySet[_ModelT]: ...
90+
def get_export_data(self, file_format: Format, queryset: QuerySet[_ModelT], *args: Any, **kwargs: Any) -> str | bytes: ...
91+
def get_export_context_data(self, **kwargs: Any) -> dict[str, Any]: ...
92+
def get_context_data(self, **kwargs: Any) -> dict[str, Any]: ...
93+
@deprecated("Use get_export_form_class or use the export_form_class attribute")
94+
def get_export_form(self) -> Form: ...
95+
def get_export_form_class(self) -> type[Form]: ...
96+
def export_action(self, request: HttpRequest, *args: Any, **kwargs: Any) -> TemplateResponse: ...
97+
def changelist_view(self, request: HttpRequest, extra_context: dict[str, Any] | None = None) -> HttpResponse: ...
98+
def get_export_filename(self, request: HttpRequest, queryset: QuerySet[_ModelT], file_format: Format) -> str: ... # type: ignore[override]
99+
100+
class ImportExportMixin(ImportMixin[_ModelT], ExportMixin[_ModelT]):
101+
import_export_change_list_template: str
102+
103+
class ImportExportModelAdmin(ImportExportMixin[_ModelT], admin.ModelAdmin[_ModelT]): ... # type: ignore[misc]
104+
105+
class ExportActionMixin(ExportMixin[_ModelT]):
106+
action_form: type[ActionForm]
107+
def __init__(self, *args: Any, **kwargs: Any) -> None: ...
108+
def export_admin_action(self, request: HttpRequest, queryset: QuerySet[_ModelT]): ...
109+
def get_actions(self, request: HttpRequest) -> dict[str, tuple[Callable[..., str], str, str] | None]: ...
110+
@property
111+
def media(self) -> Media: ...
112+
113+
class ExportActionModelAdmin(ExportActionMixin[_ModelT], admin.ModelAdmin[_ModelT]): ... # type: ignore[misc]
114+
class ImportExportActionModelAdmin(ImportMixin[_ModelT], ExportActionModelAdmin[_ModelT]): ... # type: ignore[misc]
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
class ImportExportError(Exception): ...
2+
class FieldError(ImportExportError): ...
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
from collections.abc import Callable, Mapping
2+
from typing import Any, ClassVar
3+
4+
from django.db.models import Model
5+
from django.db.models.fields import NOT_PROVIDED
6+
7+
from .widgets import Widget
8+
9+
class Field:
10+
empty_values: ClassVar[list[str | None]]
11+
attribute: str | None
12+
default: type[NOT_PROVIDED] | Callable[[], Any] | Any
13+
column_name: str | None
14+
widget: Widget
15+
readonly: bool
16+
saves_null_values: bool
17+
dehydrate_method: str
18+
m2m_add: bool
19+
def __init__(
20+
self,
21+
attribute: str | None = None,
22+
column_name: str | None = None,
23+
widget: Widget | None = None,
24+
default: type[NOT_PROVIDED] | Callable[[], Any] | Any = ...,
25+
readonly: bool = False,
26+
saves_null_values: bool = True,
27+
dehydrate_method: str | None = None,
28+
m2m_add: bool = False,
29+
) -> None: ...
30+
def clean(self, data: Mapping[str, Any], **kwargs: Any) -> Any: ...
31+
def get_value(self, obj: Model) -> Any: ...
32+
def save(self, obj: Model, data: Mapping[str, Any], is_m2m: bool = False, **kwargs: Any) -> None: ...
33+
def export(self, obj: Model) -> str: ...
34+
def get_dehydrate_method(self, field_name: str | None = None) -> str: ...

stubs/django-import-export/import_export/formats/__init__.pyi

Whitespace-only changes.
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
from _typeshed import Incomplete, ReadableBuffer
2+
from typing import IO, Any, ClassVar
3+
from typing_extensions import Self, TypeAlias
4+
5+
Dataset: TypeAlias = Incomplete # tablib.Dataset
6+
7+
class Format:
8+
def get_title(self) -> type[Self]: ...
9+
def create_dataset(self, in_stream: str | bytes | IO[Any]) -> Dataset: ...
10+
def export_data(self, dataset: Dataset, **kwargs: Any) -> Any: ...
11+
def is_binary(self) -> bool: ...
12+
def get_read_mode(self) -> str: ...
13+
def get_extension(self) -> str: ...
14+
def get_content_type(self) -> str: ...
15+
@classmethod
16+
def is_available(cls) -> bool: ...
17+
def can_import(self) -> bool: ...
18+
def can_export(self) -> bool: ...
19+
20+
class TablibFormat(Format):
21+
TABLIB_MODULE: ClassVar[str]
22+
CONTENT_TYPE: ClassVar[str]
23+
encoding: str | None
24+
def __init__(self, encoding: str | None = None) -> None: ...
25+
def get_format(self) -> type[Any]: ...
26+
def get_title(self) -> str: ... # type: ignore[override]
27+
def create_dataset(self, in_stream: str | bytes | IO[Any], **kwargs: Any) -> Dataset: ... # type: ignore[override]
28+
29+
class TextFormat(TablibFormat): ...
30+
31+
class CSV(TextFormat):
32+
def export_data(self, dataset: Dataset, **kwargs: Any) -> str: ...
33+
34+
class JSON(TextFormat):
35+
def export_data(self, dataset: Dataset, **kwargs: Any) -> str: ...
36+
37+
class YAML(TextFormat):
38+
def export_data(self, dataset: Dataset, **kwargs: Any) -> str: ...
39+
40+
class TSV(TextFormat):
41+
def export_data(self, dataset: Dataset, **kwargs: Any) -> str: ...
42+
43+
class ODS(TextFormat):
44+
def export_data(self, dataset: Dataset, **kwargs: Any) -> bytes: ...
45+
46+
class HTML(TextFormat):
47+
def export_data(self, dataset: Dataset, **kwargs: Any) -> str: ...
48+
49+
class XLS(TablibFormat):
50+
def export_data(self, dataset: Dataset, **kwargs: Any) -> bytes: ...
51+
def create_dataset(self, in_stream: bytes) -> Dataset: ... # type: ignore[override]
52+
53+
class XLSX(TablibFormat):
54+
def export_data(self, dataset: Dataset, **kwargs: Any) -> bytes: ...
55+
def create_dataset(self, in_stream: ReadableBuffer) -> Dataset: ... # type: ignore[override]
56+
57+
DEFAULT_FORMATS: list[type[Format]]
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
from typing import Any
2+
3+
from django import forms
4+
from django.contrib.admin.helpers import ActionForm
5+
6+
from .formats.base_formats import Format
7+
from .resources import Resource
8+
9+
class ImportExportFormBase(forms.Form):
10+
resource: forms.ChoiceField
11+
def __init__(self, *args: Any, resources: list[type[Resource[Any]]] | None = None, **kwargs: Any) -> None: ...
12+
13+
class ImportForm(ImportExportFormBase):
14+
import_file: forms.FileField
15+
input_format: forms.ChoiceField
16+
def __init__(self, import_formats: list[Format], *args: Any, **kwargs: Any) -> None: ...
17+
@property
18+
def media(self) -> forms.Media: ...
19+
20+
class ConfirmImportForm(forms.Form):
21+
import_file_name: forms.CharField
22+
original_file_name: forms.CharField
23+
input_format: forms.CharField
24+
resource: forms.CharField
25+
def clean_import_file_name(self) -> str: ...
26+
27+
class ExportForm(ImportExportFormBase):
28+
file_format: forms.ChoiceField
29+
def __init__(self, formats: list[Format], *args: Any, **kwargs: Any) -> None: ...
30+
31+
def export_action_form_factory(formats: list[tuple[str, str]]) -> type[ActionForm]: ...
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
from _typeshed import Incomplete
2+
from typing import Any
3+
from typing_extensions import TypeAlias
4+
5+
from django.db.models import Model, QuerySet
6+
7+
from .fields import Field
8+
from .resources import Resource
9+
10+
Dataset: TypeAlias = Incomplete # tablib.Dataset
11+
12+
class BaseInstanceLoader:
13+
resource: Resource[Any]
14+
dataset: Dataset | None
15+
def __init__(self, resource: Resource[Any], dataset: Dataset | None = None) -> None: ...
16+
def get_instance(self, row: dict[str, Any]) -> Model | None: ...
17+
18+
class ModelInstanceLoader(BaseInstanceLoader):
19+
def get_queryset(self) -> QuerySet[Any]: ...
20+
21+
class CachedInstanceLoader(ModelInstanceLoader):
22+
pk_field: Field
23+
all_instances: dict[Any, Model]
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
from _typeshed import Incomplete, SupportsGetItem
2+
from logging import Logger
3+
from typing import Any, Generic, TypeVar
4+
from typing_extensions import TypeAlias
5+
6+
from django.db.models import Model, QuerySet
7+
from django.forms import BaseForm, Form
8+
from django.http.request import HttpRequest
9+
from django.http.response import HttpResponse
10+
from django.views.generic.edit import FormView
11+
12+
from .formats.base_formats import Format
13+
from .resources import Resource
14+
15+
Dataset: TypeAlias = Incomplete # tablib.Dataset
16+
17+
logger: Logger
18+
19+
_ModelT = TypeVar("_ModelT", bound=Model)
20+
21+
class BaseImportExportMixin(Generic[_ModelT]):
22+
resource_class: type[Resource[_ModelT]]
23+
resource_classes: SupportsGetItem[int, type[Resource[_ModelT]]]
24+
@property
25+
def formats(self) -> list[type[Format]]: ...
26+
@property
27+
def export_formats(self) -> list[type[Format]]: ...
28+
@property
29+
def import_formats(self) -> list[type[Format]]: ...
30+
def check_resource_classes(self, resource_classes: SupportsGetItem[int, type[Resource[_ModelT]]]) -> None: ...
31+
def get_resource_classes(self) -> list[type[Resource[_ModelT]]]: ...
32+
def get_resource_kwargs(self, request: HttpRequest, *args: Any, **kwargs: Any) -> dict[str, Any]: ...
33+
def get_resource_index(self, form: Form) -> int: ...
34+
35+
class BaseImportMixin(BaseImportExportMixin[_ModelT]):
36+
def get_import_resource_classes(self) -> list[type[Resource[_ModelT]]]: ...
37+
def get_import_formats(self) -> list[Format]: ...
38+
def get_import_resource_kwargs(self, request: HttpRequest, *args: Any, **kwargs: Any) -> dict[str, Any]: ...
39+
def choose_import_resource_class(self, form: Form) -> type[Resource[_ModelT]]: ...
40+
41+
class BaseExportMixin(BaseImportExportMixin[_ModelT]):
42+
model: Model
43+
escape_exported_data: bool
44+
escape_html: bool
45+
escape_formulae: bool
46+
@property
47+
def should_escape_html(self) -> bool: ...
48+
@property
49+
def should_escape_formulae(self) -> bool: ...
50+
def get_export_formats(self) -> list[Format]: ...
51+
def get_export_resource_classes(self) -> list[Resource[_ModelT]]: ...
52+
def choose_export_resource_class(self, form: Form) -> Resource[_ModelT]: ...
53+
def get_export_resource_kwargs(self, request: HttpRequest, *args: Any, **kwargs: Any) -> dict[str, Any]: ...
54+
def get_data_for_export(self, request: HttpRequest, queryset: QuerySet[_ModelT], *args: Any, **kwargs: Any) -> Dataset: ...
55+
def get_export_filename(self, file_format: Format) -> str: ...
56+
57+
class ExportViewMixin(BaseExportMixin[_ModelT]):
58+
form_class: type[BaseForm] = ...
59+
def get_export_data(self, file_format: Format, queryset: QuerySet[_ModelT], *args: Any, **kwargs: Any) -> str | bytes: ...
60+
def get_context_data(self, **kwargs: Any) -> dict[str, Any]: ...
61+
def get_form_kwargs(self) -> dict[str, Any]: ...
62+
63+
_FormT = TypeVar("_FormT", bound=BaseForm)
64+
65+
class ExportViewFormMixin(ExportViewMixin[_ModelT], FormView[_FormT]): # type: ignore[misc]
66+
def form_valid(self, form: _FormT) -> HttpResponse: ...

0 commit comments

Comments
 (0)