From 8a1d8abe59ff6adf78969adcb9a5cc718c4bfb9f Mon Sep 17 00:00:00 2001 From: Viicos <65306057+Viicos@users.noreply.github.com> Date: Wed, 3 Apr 2024 19:44:22 +0200 Subject: [PATCH 01/13] Add base stubs for `django-import-export` --- pyrightconfig.stricter.json | 1 + stubs/django-import-export/METADATA.toml | 2 + .../import_export/__init__.pyi | 1 + .../import_export/admin.pyi | 86 +++++++++ .../import_export/exceptions.pyi | 2 + .../import_export/fields.pyi | 28 +++ .../import_export/formats/__init__.pyi | 0 .../import_export/formats/base_formats.pyi | 74 ++++++++ .../import_export/forms.pyi | 27 +++ .../import_export/instance_loaders.pyi | 17 ++ .../import_export/mixins.pyi | 52 ++++++ .../import_export/resources.pyi | 163 ++++++++++++++++++ .../import_export/results.pyi | 70 ++++++++ .../import_export/signals.pyi | 4 + .../import_export/templatetags/__init__.pyi | 0 .../templatetags/import_export_tags.pyi | 5 + .../import_export/tmp_storages.pyi | 30 ++++ .../import_export/utils.pyi | 17 ++ .../import_export/widgets.pyi | 84 +++++++++ 19 files changed, 663 insertions(+) create mode 100644 stubs/django-import-export/METADATA.toml create mode 100644 stubs/django-import-export/import_export/__init__.pyi create mode 100644 stubs/django-import-export/import_export/admin.pyi create mode 100644 stubs/django-import-export/import_export/exceptions.pyi create mode 100644 stubs/django-import-export/import_export/fields.pyi create mode 100644 stubs/django-import-export/import_export/formats/__init__.pyi create mode 100644 stubs/django-import-export/import_export/formats/base_formats.pyi create mode 100644 stubs/django-import-export/import_export/forms.pyi create mode 100644 stubs/django-import-export/import_export/instance_loaders.pyi create mode 100644 stubs/django-import-export/import_export/mixins.pyi create mode 100644 stubs/django-import-export/import_export/resources.pyi create mode 100644 stubs/django-import-export/import_export/results.pyi create mode 100644 stubs/django-import-export/import_export/signals.pyi create mode 100644 stubs/django-import-export/import_export/templatetags/__init__.pyi create mode 100644 stubs/django-import-export/import_export/templatetags/import_export_tags.pyi create mode 100644 stubs/django-import-export/import_export/tmp_storages.pyi create mode 100644 stubs/django-import-export/import_export/utils.pyi create mode 100644 stubs/django-import-export/import_export/widgets.pyi diff --git a/pyrightconfig.stricter.json b/pyrightconfig.stricter.json index 56164322a72b..11f2f96d51eb 100644 --- a/pyrightconfig.stricter.json +++ b/pyrightconfig.stricter.json @@ -42,6 +42,7 @@ "stubs/commonmark", "stubs/dateparser", "stubs/defusedxml", + "stubs/django-import-export", "stubs/docker", "stubs/docutils", "stubs/Flask-SocketIO", diff --git a/stubs/django-import-export/METADATA.toml b/stubs/django-import-export/METADATA.toml new file mode 100644 index 000000000000..d2d36aa0db19 --- /dev/null +++ b/stubs/django-import-export/METADATA.toml @@ -0,0 +1,2 @@ +version = "3.3.*" +upstream_repository = "https://github.com/django-import-export/django-import-export" diff --git a/stubs/django-import-export/import_export/__init__.pyi b/stubs/django-import-export/import_export/__init__.pyi new file mode 100644 index 000000000000..bda5b5a7f4cc --- /dev/null +++ b/stubs/django-import-export/import_export/__init__.pyi @@ -0,0 +1 @@ +__version__: str diff --git a/stubs/django-import-export/import_export/admin.pyi b/stubs/django-import-export/import_export/admin.pyi new file mode 100644 index 000000000000..b34423653956 --- /dev/null +++ b/stubs/django-import-export/import_export/admin.pyi @@ -0,0 +1,86 @@ +from _typeshed import Incomplete + +from django.contrib import admin + +from .forms import ConfirmImportForm, ExportForm, ImportForm +from .mixins import BaseExportMixin, BaseImportMixin + +logger: Incomplete + +class ImportExportMixinBase: + def __init__(self, *args, **kwargs) -> None: ... + base_change_list_template: Incomplete + change_list_template: Incomplete + def init_change_list_template(self) -> None: ... + def get_model_info(self): ... + def changelist_view(self, request, extra_context: Incomplete | None = None): ... + +class ImportMixin(BaseImportMixin, ImportExportMixinBase): + import_export_change_list_template: str + import_template_name: str + import_form_class = ImportForm + confirm_form_class = ConfirmImportForm + from_encoding: str + skip_admin_log: Incomplete + tmp_storage_class: Incomplete + def get_skip_admin_log(self): ... + def get_tmp_storage_class(self): ... + def has_import_permission(self, request): ... + def get_urls(self): ... + def process_import(self, request, *args, **kwargs): ... + def process_dataset(self, dataset, confirm_form, request, *args, rollback_on_validation_errors: bool = False, **kwargs): ... + def process_result(self, result, request): ... + def generate_log_entries(self, result, request) -> None: ... + def add_success_message(self, result, request) -> None: ... + def get_import_context_data(self, **kwargs): ... + def get_context_data(self, **kwargs): ... + def get_import_form(self): ... + def get_confirm_import_form(self): ... + def get_form_kwargs(self, form, *args, **kwargs): ... + def create_import_form(self, request): ... + def get_import_form_class(self, request): ... + def get_import_form_kwargs(self, request): ... + def get_import_form_initial(self, request): ... + def create_confirm_form(self, request, import_form: Incomplete | None = None): ... + def get_confirm_form_class(self, request): ... + def get_confirm_form_kwargs(self, request, import_form: Incomplete | None = None): ... + def get_confirm_form_initial(self, request, import_form): ... + def get_import_data_kwargs(self, request, *args, **kwargs): ... + def write_to_tmp_storage(self, import_file, input_format): ... + def add_data_read_fail_error_to_form(self, form, e) -> None: ... + def import_action(self, request, *args, **kwargs): ... + def changelist_view(self, request, extra_context: Incomplete | None = None): ... + +class ExportMixin(BaseExportMixin, ImportExportMixinBase): + import_export_change_list_template: str + export_template_name: str + to_encoding: Incomplete + export_form_class = ExportForm + def get_urls(self): ... + def has_export_permission(self, request): ... + def get_export_queryset(self, request): ... + def get_export_data(self, file_format, queryset, *args, **kwargs): ... + def get_export_context_data(self, **kwargs): ... + def get_context_data(self, **kwargs): ... + def get_export_form(self): ... + def get_export_form_class(self): ... + def export_action(self, request, *args, **kwargs): ... + def changelist_view(self, request, extra_context: Incomplete | None = None): ... + def get_export_filename(self, request, queryset, file_format): ... + +class ImportExportMixin(ImportMixin, ExportMixin): + import_export_change_list_template: str + +class ImportExportModelAdmin(ImportExportMixin, admin.ModelAdmin): ... + +class ExportActionMixin(ExportMixin): + import_export_change_list_template: Incomplete + action_form: Incomplete + def __init__(self, *args, **kwargs) -> None: ... + def export_admin_action(self, request, queryset): ... + def get_actions(self, request): ... + @property + def media(self): ... + +class ExportActionModelAdmin(ExportActionMixin, admin.ModelAdmin): ... +class ImportExportActionModelAdmin(ImportMixin, ExportActionModelAdmin): ... diff --git a/stubs/django-import-export/import_export/exceptions.pyi b/stubs/django-import-export/import_export/exceptions.pyi new file mode 100644 index 000000000000..f8a69dfbb6d4 --- /dev/null +++ b/stubs/django-import-export/import_export/exceptions.pyi @@ -0,0 +1,2 @@ +class ImportExportError(Exception): ... +class FieldError(ImportExportError): ... diff --git a/stubs/django-import-export/import_export/fields.pyi b/stubs/django-import-export/import_export/fields.pyi new file mode 100644 index 000000000000..92b5b7c9a5ad --- /dev/null +++ b/stubs/django-import-export/import_export/fields.pyi @@ -0,0 +1,28 @@ +from _typeshed import Incomplete + +class Field: + empty_values: Incomplete + attribute: Incomplete + default: Incomplete + column_name: Incomplete + widget: Incomplete + readonly: Incomplete + saves_null_values: Incomplete + dehydrate_method: Incomplete + m2m_add: Incomplete + def __init__( + self, + attribute: Incomplete | None = None, + column_name: Incomplete | None = None, + widget: Incomplete | None = None, + default=..., + readonly: bool = False, + saves_null_values: bool = True, + dehydrate_method: Incomplete | None = None, + m2m_add: bool = False, + ) -> None: ... + def clean(self, data, **kwargs): ... + def get_value(self, obj): ... + def save(self, obj, data, is_m2m: bool = False, **kwargs) -> None: ... + def export(self, obj): ... + def get_dehydrate_method(self, field_name: Incomplete | None = None): ... diff --git a/stubs/django-import-export/import_export/formats/__init__.pyi b/stubs/django-import-export/import_export/formats/__init__.pyi new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/stubs/django-import-export/import_export/formats/base_formats.pyi b/stubs/django-import-export/import_export/formats/base_formats.pyi new file mode 100644 index 000000000000..654f540aae0d --- /dev/null +++ b/stubs/django-import-export/import_export/formats/base_formats.pyi @@ -0,0 +1,74 @@ +from _typeshed import Incomplete + +class Format: + def get_title(self): ... + def create_dataset(self, in_stream) -> None: ... + def export_data(self, dataset, **kwargs) -> None: ... + def is_binary(self): ... + def get_read_mode(self): ... + def get_extension(self): ... + def get_content_type(self): ... + @classmethod + def is_available(cls): ... + def can_import(self): ... + def can_export(self): ... + +class TablibFormat(Format): + TABLIB_MODULE: Incomplete + CONTENT_TYPE: str + encoding: Incomplete + def __init__(self, encoding: Incomplete | None = None) -> None: ... + def get_format(self): ... + @classmethod + def is_available(cls): ... + def get_title(self): ... + def create_dataset(self, in_stream, **kwargs): ... + def export_data(self, dataset, **kwargs): ... + def get_extension(self): ... + def get_content_type(self): ... + def can_import(self): ... + def can_export(self): ... + +class TextFormat(TablibFormat): + def create_dataset(self, in_stream, **kwargs): ... + def get_read_mode(self): ... + def is_binary(self): ... + +class CSV(TextFormat): + TABLIB_MODULE: str + CONTENT_TYPE: str + +class JSON(TextFormat): + TABLIB_MODULE: str + CONTENT_TYPE: str + +class YAML(TextFormat): + TABLIB_MODULE: str + CONTENT_TYPE: str + +class TSV(TextFormat): + TABLIB_MODULE: str + CONTENT_TYPE: str + def create_dataset(self, in_stream, **kwargs): ... + +class ODS(TextFormat): + TABLIB_MODULE: str + CONTENT_TYPE: str + +class HTML(TextFormat): + TABLIB_MODULE: str + CONTENT_TYPE: str + def export_data(self, dataset, **kwargs): ... + +class XLS(TablibFormat): + TABLIB_MODULE: str + CONTENT_TYPE: str + def create_dataset(self, in_stream): ... + +class XLSX(TablibFormat): + TABLIB_MODULE: str + CONTENT_TYPE: str + def create_dataset(self, in_stream): ... + def export_data(self, dataset, **kwargs): ... + +DEFAULT_FORMATS: Incomplete diff --git a/stubs/django-import-export/import_export/forms.pyi b/stubs/django-import-export/import_export/forms.pyi new file mode 100644 index 000000000000..f1eb594c065d --- /dev/null +++ b/stubs/django-import-export/import_export/forms.pyi @@ -0,0 +1,27 @@ +from _typeshed import Incomplete + +from django import forms + +class ImportExportFormBase(forms.Form): + resource: Incomplete + def __init__(self, *args, resources: Incomplete | None = None, **kwargs) -> None: ... + +class ImportForm(ImportExportFormBase): + import_file: Incomplete + input_format: Incomplete + def __init__(self, import_formats, *args, **kwargs) -> None: ... + @property + def media(self): ... + +class ConfirmImportForm(forms.Form): + import_file_name: Incomplete + original_file_name: Incomplete + input_format: Incomplete + resource: Incomplete + def clean_import_file_name(self): ... + +class ExportForm(ImportExportFormBase): + file_format: Incomplete + def __init__(self, formats, *args, **kwargs) -> None: ... + +def export_action_form_factory(formats): ... diff --git a/stubs/django-import-export/import_export/instance_loaders.pyi b/stubs/django-import-export/import_export/instance_loaders.pyi new file mode 100644 index 000000000000..3e18afb6afb6 --- /dev/null +++ b/stubs/django-import-export/import_export/instance_loaders.pyi @@ -0,0 +1,17 @@ +from _typeshed import Incomplete + +class BaseInstanceLoader: + resource: Incomplete + dataset: Incomplete + def __init__(self, resource, dataset: Incomplete | None = None) -> None: ... + def get_instance(self, row) -> None: ... + +class ModelInstanceLoader(BaseInstanceLoader): + def get_queryset(self): ... + def get_instance(self, row): ... + +class CachedInstanceLoader(ModelInstanceLoader): + pk_field: Incomplete + all_instances: Incomplete + def __init__(self, *args, **kwargs) -> None: ... + def get_instance(self, row): ... diff --git a/stubs/django-import-export/import_export/mixins.pyi b/stubs/django-import-export/import_export/mixins.pyi new file mode 100644 index 000000000000..a253d7b0c361 --- /dev/null +++ b/stubs/django-import-export/import_export/mixins.pyi @@ -0,0 +1,52 @@ +from _typeshed import Incomplete + +from django.views.generic.edit import FormView + +from .forms import ExportForm + +logger: Incomplete + +class BaseImportExportMixin: + resource_class: Incomplete + resource_classes: Incomplete + @property + def formats(self): ... + @property + def export_formats(self): ... + @property + def import_formats(self): ... + def check_resource_classes(self, resource_classes) -> None: ... + def get_resource_classes(self): ... + def get_resource_kwargs(self, request, *args, **kwargs): ... + def get_resource_index(self, form): ... + +class BaseImportMixin(BaseImportExportMixin): + def get_import_resource_classes(self): ... + def get_import_formats(self): ... + def get_import_resource_kwargs(self, request, *args, **kwargs): ... + def choose_import_resource_class(self, form): ... + +class BaseExportMixin(BaseImportExportMixin): + model: Incomplete + escape_exported_data: bool + escape_html: bool + escape_formulae: bool + @property + def should_escape_html(self): ... + @property + def should_escape_formulae(self): ... + def get_export_formats(self): ... + def get_export_resource_classes(self): ... + def choose_export_resource_class(self, form): ... + def get_export_resource_kwargs(self, request, *args, **kwargs): ... + def get_data_for_export(self, request, queryset, *args, **kwargs): ... + def get_export_filename(self, file_format): ... + +class ExportViewMixin(BaseExportMixin): + form_class = ExportForm + def get_export_data(self, file_format, queryset, *args, **kwargs): ... + def get_context_data(self, **kwargs): ... + def get_form_kwargs(self): ... + +class ExportViewFormMixin(ExportViewMixin, FormView): + def form_valid(self, form): ... diff --git a/stubs/django-import-export/import_export/resources.pyi b/stubs/django-import-export/import_export/resources.pyi new file mode 100644 index 000000000000..1e28c8df2fae --- /dev/null +++ b/stubs/django-import-export/import_export/resources.pyi @@ -0,0 +1,163 @@ +from _typeshed import Incomplete +from collections.abc import Generator + +from .fields import Field + +logger: Incomplete + +def get_related_model(field): ... +def has_natural_foreign_key(model): ... + +class ResourceOptions: + model: Incomplete + fields: Incomplete + exclude: Incomplete + instance_loader_class: Incomplete + import_id_fields: Incomplete + export_order: Incomplete + widgets: Incomplete + use_transactions: Incomplete + skip_unchanged: bool + report_skipped: bool + clean_model_instances: bool + chunk_size: Incomplete + skip_diff: bool + skip_html_diff: bool + use_bulk: bool + batch_size: int + force_init_instance: bool + using_db: Incomplete + store_row_values: bool + store_instance: bool + use_natural_foreign_keys: bool + +class DeclarativeMetaclass(type): + def __new__(cls, name, bases, attrs): ... + +class Diff: + left: Incomplete + right: Incomplete + new: Incomplete + def __init__(self, resource, instance, new) -> None: ... + def compare_with(self, resource, instance, dry_run: bool = False) -> None: ... + def as_html(self): ... + +class Resource(metaclass=DeclarativeMetaclass): + fields: Incomplete + create_instances: Incomplete + update_instances: Incomplete + delete_instances: Incomplete + def __init__(self, **kwargs) -> None: ... + @classmethod + def get_result_class(self): ... + @classmethod + def get_row_result_class(self): ... + @classmethod + def get_error_result_class(self): ... + @classmethod + def get_diff_class(self): ... + @classmethod + def get_db_connection_name(self): ... + def get_use_transactions(self): ... + def get_chunk_size(self): ... + def get_fields(self, **kwargs): ... + def get_field_name(self, field): ... + def init_instance(self, row: Incomplete | None = None) -> None: ... + def get_instance(self, instance_loader, row): ... + def get_or_init_instance(self, instance_loader, row): ... + def get_import_id_fields(self): ... + def get_bulk_update_fields(self): ... + def bulk_create( + self, using_transactions, dry_run, raise_errors, batch_size: Incomplete | None = None, result: Incomplete | None = None + ) -> None: ... + def bulk_update( + self, using_transactions, dry_run, raise_errors, batch_size: Incomplete | None = None, result: Incomplete | None = None + ) -> None: ... + def bulk_delete(self, using_transactions, dry_run, raise_errors, result: Incomplete | None = None) -> None: ... + def validate_instance( + self, instance, import_validation_errors: Incomplete | None = None, validate_unique: bool = True + ) -> None: ... + def save_instance(self, instance, is_create, using_transactions: bool = True, dry_run: bool = False) -> None: ... + def before_save_instance(self, instance, using_transactions, dry_run) -> None: ... + def after_save_instance(self, instance, using_transactions, dry_run) -> None: ... + def delete_instance(self, instance, using_transactions: bool = True, dry_run: bool = False) -> None: ... + def before_delete_instance(self, instance, dry_run) -> None: ... + def after_delete_instance(self, instance, dry_run) -> None: ... + def import_field(self, field, obj, data, is_m2m: bool = False, **kwargs) -> None: ... + def get_import_fields(self): ... + def import_obj(self, obj, data, dry_run, **kwargs) -> None: ... + def save_m2m(self, obj, data, using_transactions, dry_run) -> None: ... + def for_delete(self, row, instance): ... + def skip_row(self, instance, original, row, import_validation_errors: Incomplete | None = None): ... + def get_diff_headers(self): ... + def before_import(self, dataset, using_transactions, dry_run, **kwargs) -> None: ... + def after_import(self, dataset, result, using_transactions, dry_run, **kwargs) -> None: ... + def before_import_row(self, row, row_number: Incomplete | None = None, **kwargs) -> None: ... + def after_import_row(self, row, row_result, row_number: Incomplete | None = None, **kwargs) -> None: ... + def after_import_instance(self, instance, new, row_number: Incomplete | None = None, **kwargs) -> None: ... + def handle_import_error(self, result, error, raise_errors: bool = False) -> None: ... + def import_row( + self, + row, + instance_loader, + using_transactions: bool = True, + dry_run: bool = False, + raise_errors: Incomplete | None = None, + **kwargs, + ): ... + def import_data( + self, + dataset, + dry_run: bool = False, + raise_errors: bool = False, + use_transactions: Incomplete | None = None, + collect_failed_rows: bool = False, + rollback_on_validation_errors: bool = False, + **kwargs, + ): ... + def import_data_inner( + self, + dataset, + dry_run, + raise_errors, + using_transactions, + collect_failed_rows, + rollback_on_validation_errors: Incomplete | None = None, + **kwargs, + ): ... + def get_export_order(self): ... + def before_export(self, queryset, *args, **kwargs) -> None: ... + def after_export(self, queryset, data, *args, **kwargs) -> None: ... + def filter_export(self, queryset, *args, **kwargs): ... + def export_field(self, field, obj): ... + def get_export_fields(self): ... + def export_resource(self, obj): ... + def get_export_headers(self): ... + def get_user_visible_headers(self): ... + def get_user_visible_fields(self): ... + def iter_queryset(self, queryset) -> Generator[Incomplete, Incomplete, None]: ... + def export(self, *args, queryset: Incomplete | None = None, **kwargs): ... + +class ModelDeclarativeMetaclass(DeclarativeMetaclass): + def __new__(cls, name, bases, attrs): ... + +class ModelResource(Resource, metaclass=ModelDeclarativeMetaclass): + DEFAULT_RESOURCE_FIELD = Field + WIDGETS_MAP: Incomplete + @classmethod + def get_m2m_widget(cls, field): ... + @classmethod + def get_fk_widget(cls, field): ... + @classmethod + def widget_from_django_field(cls, f, default=...): ... + @classmethod + def widget_kwargs_for_field(self, field_name): ... + @classmethod + def field_from_django_field(cls, field_name, django_field, readonly): ... + def get_queryset(self): ... + def init_instance(self, row: Incomplete | None = None): ... + def after_import(self, dataset, result, using_transactions, dry_run, **kwargs) -> None: ... + @classmethod + def get_display_name(cls): ... + +def modelresource_factory(model, resource_class=...): ... diff --git a/stubs/django-import-export/import_export/results.pyi b/stubs/django-import-export/import_export/results.pyi new file mode 100644 index 000000000000..be8cc0cb4b0c --- /dev/null +++ b/stubs/django-import-export/import_export/results.pyi @@ -0,0 +1,70 @@ +from collections import OrderedDict +from collections.abc import Mapping +from typing import Any, ClassVar, Iterator, Literal, TypeAlias + +from django.core.exceptions import ValidationError +from django.db.models import Model +from tablib import Dataset + +class Error: + error: Exception + traceback: str + row: Mapping[str, Any] + def __init__(self, error: Exception, traceback: str | None = None, row: Mapping[str, Any] | None = None) -> None: ... + +_ImportType: TypeAlias = Literal["update", "new", "delete", "skip", "error", "invalid"] + +class RowResult: + IMPORT_TYPE_UPDATE: ClassVar[Literal["update"]] + IMPORT_TYPE_NEW: ClassVar[Literal["new"]] + IMPORT_TYPE_DELETE: ClassVar[Literal["delete"]] + IMPORT_TYPE_SKIP: ClassVar[Literal["skip"]] + IMPORT_TYPE_ERROR: ClassVar[Literal["error"]] + IMPORT_TYPE_INVALID: ClassVar[Literal["invalid"]] + valid_import_types: frozenset[_ImportType] + errors: list[Error] + validation_error: ValidationError | None + diff: list[str] | None + import_type: _ImportType + row_values: dict[str, Any] + object_id: Any | None + object_repr: str | None + instance: Model + original: Model + new_record: bool | None + def __init__(self) -> None: ... + def add_instance_info(self, instance: Model) -> None: ... + +class InvalidRow: + number: int + error: ValidationError + values: tuple[Any, ...] + error_dict: dict[str, list[str]] + def __init__(self, number: int, validation_error: ValidationError, values: tuple[Any, ...]) -> None: ... + @property + def field_specific_errors(self) -> dict[str, list[str]]: ... + @property + def non_field_specific_errors(self) -> list[str]: ... + @property + def error_count(self) -> int: ... + +class Result: + base_errors: list[Error] + diff_headers: list[str] + rows: list[RowResult] + invalid_rows: list[InvalidRow] + failed_dataset: Dataset + totals: OrderedDict[_ImportType, int] + total_rows: int + def __init__(self) -> None: ... + def valid_rows(self) -> list[RowResult]: ... + def append_row_result(self, row_result: RowResult) -> None: ... + def append_base_error(self, error: Error) -> None: ... + def add_dataset_headers(self, headers: list[str] | None) -> None: ... + def append_failed_row(self, row: Mapping[str, Any], error) -> None: ... + def append_invalid_row(self, number: int, row: Mapping[str, Any], validation_error: ValidationError) -> None: ... + def increment_row_result_total(self, row_result: RowResult) -> None: ... + def row_errors(self) -> list[tuple[int, Any]]: ... + def has_errors(self) -> bool: ... + def has_validation_errors(self) -> bool: ... + def __iter__(self) -> Iterator[RowResult]: ... diff --git a/stubs/django-import-export/import_export/signals.pyi b/stubs/django-import-export/import_export/signals.pyi new file mode 100644 index 000000000000..7d32df82f840 --- /dev/null +++ b/stubs/django-import-export/import_export/signals.pyi @@ -0,0 +1,4 @@ +from django.dispatch import Signal + +post_export: Signal +post_import: Signal diff --git a/stubs/django-import-export/import_export/templatetags/__init__.pyi b/stubs/django-import-export/import_export/templatetags/__init__.pyi new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/stubs/django-import-export/import_export/templatetags/import_export_tags.pyi b/stubs/django-import-export/import_export/templatetags/import_export_tags.pyi new file mode 100644 index 000000000000..1b4ebafb218c --- /dev/null +++ b/stubs/django-import-export/import_export/templatetags/import_export_tags.pyi @@ -0,0 +1,5 @@ +from _typeshed import Incomplete + +register: Incomplete + +def compare_values(value1, value2): ... diff --git a/stubs/django-import-export/import_export/tmp_storages.pyi b/stubs/django-import-export/import_export/tmp_storages.pyi new file mode 100644 index 000000000000..ddab041ee2a9 --- /dev/null +++ b/stubs/django-import-export/import_export/tmp_storages.pyi @@ -0,0 +1,30 @@ +from typing import IO, Any, ClassVar + +class BaseStorage: + name: str | None + read_mode: str + encoding: str | None + def __init__(self, *, name: str | None = None, read_mode: str = "", encoding: str | None = None) -> None: ... + def save(self, data: Any) -> None: ... + def read(self) -> None: ... + def remove(self) -> None: ... + +class TempFolderStorage(BaseStorage): + def save(self, data: Any) -> None: ... + def read(self): ... + def remove(self) -> None: ... + def get_full_path(self) -> str: ... + +class CacheStorage(BaseStorage): + CACHE_LIFETIME: int + CACHE_PREFIX: str + def save(self, data: Any) -> None: ... + def read(self): ... + def remove(self) -> None: ... + +class MediaStorage(BaseStorage): + MEDIA_FOLDER: ClassVar[str] + def save(self, data: IO[Any]) -> None: ... + def read(self): ... + def remove(self) -> None: ... + def get_full_path(self) -> str: ... diff --git a/stubs/django-import-export/import_export/utils.pyi b/stubs/django-import-export/import_export/utils.pyi new file mode 100644 index 000000000000..9188f5e563a3 --- /dev/null +++ b/stubs/django-import-export/import_export/utils.pyi @@ -0,0 +1,17 @@ +from types import TracebackType +from typing import Any, Callable, TypeVar + +from django.db.transaction import Atomic + +_C = TypeVar("_C", bound=Callable[..., Any]) + +class atomic_if_using_transaction: + using_transactions: bool + context_manager: Atomic + def __init__(self, using_transactions: bool, using: str | None) -> None: ... + def __enter__(self) -> None: ... + def __exit__( + self, exc_type: type[BaseException] | None, exc_value: BaseException | None, exc_tb: TracebackType | None + ) -> None: ... + +def original(method: _C) -> _C: ... diff --git a/stubs/django-import-export/import_export/widgets.pyi b/stubs/django-import-export/import_export/widgets.pyi new file mode 100644 index 000000000000..b1da466d450a --- /dev/null +++ b/stubs/django-import-export/import_export/widgets.pyi @@ -0,0 +1,84 @@ +from _typeshed import Incomplete + +def format_datetime(value, datetime_format): ... + +class Widget: + def clean(self, value, row: Incomplete | None = None, **kwargs): ... + def render(self, value, obj: Incomplete | None = None): ... + +class NumberWidget(Widget): + coerce_to_string: Incomplete + def __init__(self, coerce_to_string: bool = False) -> None: ... + def is_empty(self, value): ... + def render(self, value, obj: Incomplete | None = None): ... + +class FloatWidget(NumberWidget): + def clean(self, value, row: Incomplete | None = None, **kwargs): ... + +class IntegerWidget(NumberWidget): + def clean(self, value, row: Incomplete | None = None, **kwargs): ... + +class DecimalWidget(NumberWidget): + def clean(self, value, row: Incomplete | None = None, **kwargs): ... + +class CharWidget(Widget): + coerce_to_string: Incomplete + allow_blank: Incomplete + def __init__(self, coerce_to_string: bool = False, allow_blank: bool = False) -> None: ... + def clean(self, value, row: Incomplete | None = None, **kwargs): ... + +class BooleanWidget(Widget): + TRUE_VALUES: Incomplete + FALSE_VALUES: Incomplete + NULL_VALUES: Incomplete + def render(self, value, obj: Incomplete | None = None): ... + def clean(self, value, row: Incomplete | None = None, **kwargs): ... + +class DateWidget(Widget): + formats: Incomplete + def __init__(self, format: Incomplete | None = None) -> None: ... + def clean(self, value, row: Incomplete | None = None, **kwargs): ... + def render(self, value, obj: Incomplete | None = None): ... + +class DateTimeWidget(Widget): + formats: Incomplete + def __init__(self, format: Incomplete | None = None) -> None: ... + def clean(self, value, row: Incomplete | None = None, **kwargs): ... + def render(self, value, obj: Incomplete | None = None): ... + +class TimeWidget(Widget): + formats: Incomplete + def __init__(self, format: Incomplete | None = None) -> None: ... + def clean(self, value, row: Incomplete | None = None, **kwargs): ... + def render(self, value, obj: Incomplete | None = None): ... + +class DurationWidget(Widget): + def clean(self, value, row: Incomplete | None = None, **kwargs): ... + def render(self, value, obj: Incomplete | None = None): ... + +class SimpleArrayWidget(Widget): + separator: Incomplete + def __init__(self, separator: Incomplete | None = None) -> None: ... + def clean(self, value, row: Incomplete | None = None, **kwargs): ... + def render(self, value, obj: Incomplete | None = None): ... + +class JSONWidget(Widget): + def clean(self, value, row: Incomplete | None = None, **kwargs): ... + def render(self, value, obj: Incomplete | None = None): ... + +class ForeignKeyWidget(Widget): + model: Incomplete + field: Incomplete + use_natural_foreign_keys: Incomplete + def __init__(self, model, field: str = "pk", use_natural_foreign_keys: bool = False, **kwargs) -> None: ... + def get_queryset(self, value, row, *args, **kwargs): ... + def clean(self, value, row: Incomplete | None = None, **kwargs): ... + def render(self, value, obj: Incomplete | None = None): ... + +class ManyToManyWidget(Widget): + model: Incomplete + separator: Incomplete + field: Incomplete + def __init__(self, model, separator: Incomplete | None = None, field: Incomplete | None = None, **kwargs) -> None: ... + def clean(self, value, row: Incomplete | None = None, **kwargs): ... + def render(self, value, obj: Incomplete | None = None): ... From ad2541572eb4f4dd28b734d1297816c7b3348847 Mon Sep 17 00:00:00 2001 From: Viicos <65306057+Viicos@users.noreply.github.com> Date: Thu, 4 Apr 2024 21:10:44 +0200 Subject: [PATCH 02/13] More work: admin, fields, formats, mixins, templatetags --- .../import_export/admin.pyi | 156 +++++++++++------- .../import_export/fields.pyi | 46 +++--- .../import_export/formats/base_formats.pyi | 84 ++++------ .../import_export/mixins.pyi | 89 +++++----- .../templatetags/import_export_tags.pyi | 9 +- 5 files changed, 209 insertions(+), 175 deletions(-) diff --git a/stubs/django-import-export/import_export/admin.pyi b/stubs/django-import-export/import_export/admin.pyi index b34423653956..3482b501d339 100644 --- a/stubs/django-import-export/import_export/admin.pyi +++ b/stubs/django-import-export/import_export/admin.pyi @@ -1,86 +1,114 @@ -from _typeshed import Incomplete +from logging import Logger +from typing import Any, Callable, Generic, TypeVar +from typing_extensions import deprecated from django.contrib import admin +from django.contrib.admin.helpers import ActionForm +from django.core.files import File +from django.db.models import Model, QuerySet +from django.forms import Form, Media +from django.http.request import HttpRequest +from django.http.response import HttpResponse +from django.template.response import TemplateResponse +from django.urls import URLPattern +from tablib import Dataset +from .formats.base_formats import Format from .forms import ConfirmImportForm, ExportForm, ImportForm from .mixins import BaseExportMixin, BaseImportMixin +from .results import Result +from .tmp_storages import BaseStorage -logger: Incomplete +logger: Logger + +_ModelT = TypeVar("_ModelT", bound=Model) class ImportExportMixinBase: - def __init__(self, *args, **kwargs) -> None: ... - base_change_list_template: Incomplete - change_list_template: Incomplete + base_change_list_template: str + change_list_template: str + def __init__(self, *args: Any, **kwargs: Any) -> None: ... def init_change_list_template(self) -> None: ... - def get_model_info(self): ... - def changelist_view(self, request, extra_context: Incomplete | None = None): ... + def get_model_info(self) -> tuple[str, str]: ... + def changelist_view(self, request: HttpRequest, extra_context: dict[str, Any] | None = None) -> HttpResponse: ... -class ImportMixin(BaseImportMixin, ImportExportMixinBase): +class ImportMixin(BaseImportMixin[_ModelT], ImportExportMixinBase): import_export_change_list_template: str import_template_name: str - import_form_class = ImportForm - confirm_form_class = ConfirmImportForm + import_form_class: type[Form] = ImportForm + confirm_form_class: type[Form] = ConfirmImportForm from_encoding: str - skip_admin_log: Incomplete - tmp_storage_class: Incomplete - def get_skip_admin_log(self): ... - def get_tmp_storage_class(self): ... - def has_import_permission(self, request): ... - def get_urls(self): ... - def process_import(self, request, *args, **kwargs): ... - def process_dataset(self, dataset, confirm_form, request, *args, rollback_on_validation_errors: bool = False, **kwargs): ... - def process_result(self, result, request): ... - def generate_log_entries(self, result, request) -> None: ... - def add_success_message(self, result, request) -> None: ... - def get_import_context_data(self, **kwargs): ... - def get_context_data(self, **kwargs): ... - def get_import_form(self): ... - def get_confirm_import_form(self): ... - def get_form_kwargs(self, form, *args, **kwargs): ... - def create_import_form(self, request): ... - def get_import_form_class(self, request): ... - def get_import_form_kwargs(self, request): ... - def get_import_form_initial(self, request): ... - def create_confirm_form(self, request, import_form: Incomplete | None = None): ... - def get_confirm_form_class(self, request): ... - def get_confirm_form_kwargs(self, request, import_form: Incomplete | None = None): ... - def get_confirm_form_initial(self, request, import_form): ... - def get_import_data_kwargs(self, request, *args, **kwargs): ... - def write_to_tmp_storage(self, import_file, input_format): ... - def add_data_read_fail_error_to_form(self, form, e) -> None: ... - def import_action(self, request, *args, **kwargs): ... - def changelist_view(self, request, extra_context: Incomplete | None = None): ... + skip_admin_log: bool | None + tmp_storage_class: str | type[BaseStorage] + def get_skip_admin_log(self) -> bool: ... + def get_tmp_storage_class(self) -> type[BaseStorage]: ... + def has_import_permission(self, request: HttpRequest) -> bool: ... + def get_urls(self) -> list[URLPattern]: ... + def process_import(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse: ... + def process_dataset( + self, + dataset: Dataset, + confirm_form: Form, + request: HttpRequest, + *args: Any, + rollback_on_validation_errors: bool = False, + **kwargs: Any, + ) -> Result: ... + def process_result(self, result: Result, request: HttpRequest) -> HttpResponse: ... + def generate_log_entries(self, result: Result, request: HttpRequest) -> None: ... + def add_success_message(self, result: Result, request: HttpRequest) -> None: ... + def get_import_context_data(self, **kwargs: Any) -> dict[str, Any]: ... + def get_context_data(self, **kwargs: Any) -> dict[str, Any]: ... + @deprecated("Use get_import_form_class instead") + def get_import_form(self) -> type[Form]: ... + @deprecated("Use get_confirm_form_class instead") + def get_confirm_import_form(self) -> type[Form]: ... + @deprecated("Use get_import_form_kwargs or get_confirm_form_kwargs") + def get_form_kwargs(self, form: Form, *args: Any, **kwargs: Any) -> dict[str, Any]: ... + def create_import_form(self, request: HttpRequest) -> Form: ... + def get_import_form_class(self, request: HttpRequest) -> type[Form]: ... + def get_import_form_kwargs(self, request: HttpRequest) -> dict[str, Any]: ... + def get_import_form_initial(self, request: HttpRequest) -> dict[str, Any]: ... + def create_confirm_form(self, request: HttpRequest, import_form: Form | None = None) -> Form: ... + def get_confirm_form_class(self, request: HttpRequest) -> type[Form]: ... + def get_confirm_form_kwargs(self, request: HttpRequest, import_form: Form | None = None) -> dict[str, Any]: ... + def get_confirm_form_initial(self, request: HttpRequest, import_form: Form | None) -> dict[str, Any]: ... + def get_import_data_kwargs(self, request: HttpRequest, *args: Any, **kwargs: Any) -> dict[str, Any]: ... + def write_to_tmp_storage(self, import_file: File, input_format: Format) -> BaseStorage: ... + def add_data_read_fail_error_to_form(self, form: Form, e: Exception) -> None: ... + def import_action(self, request: HttpRequest, *args: Any, **kwargs: Any) -> TemplateResponse: ... + def changelist_view(self, request: HttpRequest, extra_context: dict[str, Any] | None = None) -> HttpResponse: ... -class ExportMixin(BaseExportMixin, ImportExportMixinBase): +class ExportMixin(BaseExportMixin[_ModelT], ImportExportMixinBase, Generic[_ModelT]): import_export_change_list_template: str export_template_name: str - to_encoding: Incomplete - export_form_class = ExportForm - def get_urls(self): ... - def has_export_permission(self, request): ... - def get_export_queryset(self, request): ... - def get_export_data(self, file_format, queryset, *args, **kwargs): ... - def get_export_context_data(self, **kwargs): ... - def get_context_data(self, **kwargs): ... - def get_export_form(self): ... - def get_export_form_class(self): ... - def export_action(self, request, *args, **kwargs): ... - def changelist_view(self, request, extra_context: Incomplete | None = None): ... - def get_export_filename(self, request, queryset, file_format): ... + to_encoding: str | None + export_form_class: type[Form] = ExportForm + def get_urls(self) -> list[URLPattern]: ... + def has_export_permission(self, request: HttpRequest) -> bool: ... + def get_export_queryset(self, request: HttpRequest) -> QuerySet[_ModelT]: ... + def get_export_data(self, file_format: Format, queryset: QuerySet[_ModelT], *args: Any, **kwargs: Any) -> str | bytes: ... + def get_export_context_data(self, **kwargs: Any) -> dict[str, Any]: ... + def get_context_data(self, **kwargs: Any) -> dict[str, Any]: ... + @deprecated("Use get_export_form_class or use the export_form_class attribute") + def get_export_form(self) -> Form: ... + def get_export_form_class(self) -> type[Form]: ... + def export_action(self, request: HttpRequest, *args: Any, **kwargs: Any) -> TemplateResponse: ... + def changelist_view(self, request: HttpRequest, extra_context: dict[str, Any] | None = None) -> HttpResponse: ... + def get_export_filename(self, request: HttpRequest, queryset: QuerySet[_ModelT], file_format: Format) -> str: ... -class ImportExportMixin(ImportMixin, ExportMixin): +class ImportExportMixin(ImportMixin[_ModelT], ExportMixin[_ModelT]): import_export_change_list_template: str -class ImportExportModelAdmin(ImportExportMixin, admin.ModelAdmin): ... +class ImportExportModelAdmin(ImportExportMixin[_ModelT], admin.ModelAdmin[_ModelT]): ... -class ExportActionMixin(ExportMixin): - import_export_change_list_template: Incomplete - action_form: Incomplete - def __init__(self, *args, **kwargs) -> None: ... - def export_admin_action(self, request, queryset): ... - def get_actions(self, request): ... +class ExportActionMixin(ExportMixin[_ModelT]): + import_export_change_list_template: str | None + action_form: type[ActionForm] + def __init__(self, *args: Any, **kwargs: Any) -> None: ... + def export_admin_action(self, request: HttpRequest, queryset: QuerySet[_ModelT]): ... + def get_actions(self, request: HttpRequest) -> dict[str, tuple[Callable[..., str], str, str] | None]: ... @property - def media(self): ... + def media(self) -> Media: ... -class ExportActionModelAdmin(ExportActionMixin, admin.ModelAdmin): ... -class ImportExportActionModelAdmin(ImportMixin, ExportActionModelAdmin): ... +class ExportActionModelAdmin(ExportActionMixin[_ModelT], admin.ModelAdmin[_ModelT]): ... +class ImportExportActionModelAdmin(ImportMixin[_ModelT], ExportActionModelAdmin[_ModelT]): ... diff --git a/stubs/django-import-export/import_export/fields.pyi b/stubs/django-import-export/import_export/fields.pyi index 92b5b7c9a5ad..c2d71375ce61 100644 --- a/stubs/django-import-export/import_export/fields.pyi +++ b/stubs/django-import-export/import_export/fields.pyi @@ -1,28 +1,34 @@ -from _typeshed import Incomplete +from collections.abc import Mapping +from typing import Any, Callable, ClassVar + +from django.db.models import Model +from django.db.models.fields import NOT_PROVIDED + +from .widgets import Widget class Field: - empty_values: Incomplete - attribute: Incomplete - default: Incomplete - column_name: Incomplete - widget: Incomplete - readonly: Incomplete - saves_null_values: Incomplete - dehydrate_method: Incomplete - m2m_add: Incomplete + empty_values: ClassVar[list[str | None]] + attribute: str | None + default: type[NOT_PROVIDED] | Callable[[], Any] | Any + column_name: str | None + widget: Widget + readonly: bool + saves_null_values: bool + dehydrate_method: str + m2m_add: bool def __init__( self, - attribute: Incomplete | None = None, - column_name: Incomplete | None = None, - widget: Incomplete | None = None, - default=..., + attribute: str | None = None, + column_name: str | None = None, + widget: Widget | None = None, + default: type[NOT_PROVIDED] | Callable[[], Any] | Any = ..., readonly: bool = False, saves_null_values: bool = True, - dehydrate_method: Incomplete | None = None, + dehydrate_method: str | None = None, m2m_add: bool = False, ) -> None: ... - def clean(self, data, **kwargs): ... - def get_value(self, obj): ... - def save(self, obj, data, is_m2m: bool = False, **kwargs) -> None: ... - def export(self, obj): ... - def get_dehydrate_method(self, field_name: Incomplete | None = None): ... + def clean(self, data: Mapping[str, Any], **kwargs: Any): ... + def get_value(self, obj: Model) -> Any: ... + def save(self, obj: Model, data: Mapping[str, Any], is_m2m: bool = False, **kwargs: Any) -> None: ... + def export(self, obj: Model) -> str: ... + def get_dehydrate_method(self, field_name: str | None = None) -> str: ... diff --git a/stubs/django-import-export/import_export/formats/base_formats.pyi b/stubs/django-import-export/import_export/formats/base_formats.pyi index 654f540aae0d..62e3ab8c0175 100644 --- a/stubs/django-import-export/import_export/formats/base_formats.pyi +++ b/stubs/django-import-export/import_export/formats/base_formats.pyi @@ -1,74 +1,58 @@ -from _typeshed import Incomplete +from _typeshed import ReadableBuffer +from typing import IO, Any, ClassVar +from typing_extensions import Self + +from tablib import Dataset class Format: - def get_title(self): ... - def create_dataset(self, in_stream) -> None: ... - def export_data(self, dataset, **kwargs) -> None: ... - def is_binary(self): ... - def get_read_mode(self): ... - def get_extension(self): ... - def get_content_type(self): ... + def get_title(self) -> type[Self]: ... + def create_dataset(self, in_stream: str | bytes | IO[Any]) -> Dataset: ... + def export_data(self, dataset: Dataset, **kwargs: Any) -> Any: ... + def is_binary(self) -> bool: ... + def get_read_mode(self) -> str: ... + def get_extension(self) -> str: ... + def get_content_type(self) -> str: ... @classmethod - def is_available(cls): ... - def can_import(self): ... - def can_export(self): ... + def is_available(cls) -> bool: ... + def can_import(self) -> bool: ... + def can_export(self) -> bool: ... class TablibFormat(Format): - TABLIB_MODULE: Incomplete - CONTENT_TYPE: str - encoding: Incomplete - def __init__(self, encoding: Incomplete | None = None) -> None: ... - def get_format(self): ... - @classmethod - def is_available(cls): ... - def get_title(self): ... - def create_dataset(self, in_stream, **kwargs): ... - def export_data(self, dataset, **kwargs): ... - def get_extension(self): ... - def get_content_type(self): ... - def can_import(self): ... - def can_export(self): ... + TABLIB_MODULE: ClassVar[str] + CONTENT_TYPE: ClassVar[str] + encoding: str | None + def __init__(self, encoding: str | None = None) -> None: ... + def get_format(self) -> type[Any]: ... + def get_title(self) -> str: ... + def create_dataset(self, in_stream: str | bytes | IO[Any], **kwargs: Any) -> Dataset: ... class TextFormat(TablibFormat): - def create_dataset(self, in_stream, **kwargs): ... - def get_read_mode(self): ... - def is_binary(self): ... + ... class CSV(TextFormat): - TABLIB_MODULE: str - CONTENT_TYPE: str + def export_data(self, dataset: Dataset, **kwargs: Any) -> str: ... class JSON(TextFormat): - TABLIB_MODULE: str - CONTENT_TYPE: str + def export_data(self, dataset: Dataset, **kwargs: Any) -> str: ... class YAML(TextFormat): - TABLIB_MODULE: str - CONTENT_TYPE: str + def export_data(self, dataset: Dataset, **kwargs: Any) -> str: ... class TSV(TextFormat): - TABLIB_MODULE: str - CONTENT_TYPE: str - def create_dataset(self, in_stream, **kwargs): ... + def export_data(self, dataset: Dataset, **kwargs: Any) -> str: ... class ODS(TextFormat): - TABLIB_MODULE: str - CONTENT_TYPE: str + def export_data(self, dataset: Dataset, **kwargs: Any) -> bytes: ... class HTML(TextFormat): - TABLIB_MODULE: str - CONTENT_TYPE: str - def export_data(self, dataset, **kwargs): ... + def export_data(self, dataset: Dataset, **kwargs: Any) -> str: ... class XLS(TablibFormat): - TABLIB_MODULE: str - CONTENT_TYPE: str - def create_dataset(self, in_stream): ... + def export_data(self, dataset: Dataset, **kwargs: Any) -> bytes: ... + def create_dataset(self, in_stream: bytes) -> Dataset: ... class XLSX(TablibFormat): - TABLIB_MODULE: str - CONTENT_TYPE: str - def create_dataset(self, in_stream): ... - def export_data(self, dataset, **kwargs): ... + def export_data(self, dataset: Dataset, **kwargs: Any) -> bytes: ... + def create_dataset(self, in_stream: ReadableBuffer) -> Dataset: ... -DEFAULT_FORMATS: Incomplete +DEFAULT_FORMATS: list[type[Format]] diff --git a/stubs/django-import-export/import_export/mixins.pyi b/stubs/django-import-export/import_export/mixins.pyi index a253d7b0c361..a652eda616ce 100644 --- a/stubs/django-import-export/import_export/mixins.pyi +++ b/stubs/django-import-export/import_export/mixins.pyi @@ -1,52 +1,65 @@ -from _typeshed import Incomplete +from _typeshed import SupportsGetItem +from logging import Logger +from typing import Any, Generic, TypeVar +from django.db.models import Model, QuerySet +from django.forms import BaseForm, Form +from django.http.request import HttpRequest +from django.http.response import HttpResponse from django.views.generic.edit import FormView +from tablib import Dataset +from .formats.base_formats import Format from .forms import ExportForm +from .resources import Resource -logger: Incomplete +logger: Logger -class BaseImportExportMixin: - resource_class: Incomplete - resource_classes: Incomplete +_ModelT = TypeVar("_ModelT", bound=Model) + +class BaseImportExportMixin(Generic[_ModelT]): + resource_class: type[Resource] + resource_classes: SupportsGetItem[int, type[Resource]] @property - def formats(self): ... + def formats(self) -> list[type[Format]]: ... @property - def export_formats(self): ... + def export_formats(self) -> list[type[Format]]: ... @property - def import_formats(self): ... - def check_resource_classes(self, resource_classes) -> None: ... - def get_resource_classes(self): ... - def get_resource_kwargs(self, request, *args, **kwargs): ... - def get_resource_index(self, form): ... - -class BaseImportMixin(BaseImportExportMixin): - def get_import_resource_classes(self): ... - def get_import_formats(self): ... - def get_import_resource_kwargs(self, request, *args, **kwargs): ... - def choose_import_resource_class(self, form): ... - -class BaseExportMixin(BaseImportExportMixin): - model: Incomplete + def import_formats(self) -> list[type[Format]]: ... + def check_resource_classes(self, resource_classes: SupportsGetItem[int, type[Resource]]) -> None: ... + def get_resource_classes(self) -> list[type[Resource]]: ... + def get_resource_kwargs(self, request: HttpRequest, *args: Any, **kwargs: Any) -> dict[str, Any]: ... + def get_resource_index(self, form: Form) -> int: ... + +class BaseImportMixin(BaseImportExportMixin[_ModelT]): + def get_import_resource_classes(self) -> list[type[Resource]]: ... + def get_import_formats(self) -> list[Format]: ... + def get_import_resource_kwargs(self, request: HttpRequest, *args: Any, **kwargs: Any) -> dict[str, Any]: ... + def choose_import_resource_class(self, form: Form) -> type[Resource]: ... + +class BaseExportMixin(BaseImportExportMixin[_ModelT]): + model: Model escape_exported_data: bool escape_html: bool escape_formulae: bool @property - def should_escape_html(self): ... + def should_escape_html(self) -> bool: ... @property - def should_escape_formulae(self): ... - def get_export_formats(self): ... - def get_export_resource_classes(self): ... - def choose_export_resource_class(self, form): ... - def get_export_resource_kwargs(self, request, *args, **kwargs): ... - def get_data_for_export(self, request, queryset, *args, **kwargs): ... - def get_export_filename(self, file_format): ... - -class ExportViewMixin(BaseExportMixin): - form_class = ExportForm - def get_export_data(self, file_format, queryset, *args, **kwargs): ... - def get_context_data(self, **kwargs): ... - def get_form_kwargs(self): ... - -class ExportViewFormMixin(ExportViewMixin, FormView): - def form_valid(self, form): ... + def should_escape_formulae(self) -> bool: ... + def get_export_formats(self) -> list[Format]: ... + def get_export_resource_classes(self) -> list[Resource]: ... + def choose_export_resource_class(self, form: Form) -> Resource: ... + def get_export_resource_kwargs(self, request: HttpRequest, *args: Any, **kwargs: Any) -> dict[str, Any]: ... + def get_data_for_export(self, request: HttpRequest, queryset: QuerySet[_ModelT], *args: Any, **kwargs: Any) -> Dataset: ... + def get_export_filename(self, file_format: Format) -> str: ... + +class ExportViewMixin(BaseExportMixin[_ModelT]): + form_class: type[Form] = ExportForm + def get_export_data(self, file_format: Format, queryset: QuerySet[_ModelT], *args: Any, **kwargs: Any) -> str | bytes: ... + def get_context_data(self, **kwargs: Any) -> dict[str, Any]: ... + def get_form_kwargs(self) -> dict[str, Any]: ... + +_FormT = TypeVar("_FormT", bound=BaseForm) + +class ExportViewFormMixin(ExportViewMixin[_ModelT], FormView[_FormT]): + def form_valid(self, form: _FormT) -> HttpResponse: ... diff --git a/stubs/django-import-export/import_export/templatetags/import_export_tags.pyi b/stubs/django-import-export/import_export/templatetags/import_export_tags.pyi index 1b4ebafb218c..9d6c0e4d7f68 100644 --- a/stubs/django-import-export/import_export/templatetags/import_export_tags.pyi +++ b/stubs/django-import-export/import_export/templatetags/import_export_tags.pyi @@ -1,5 +1,8 @@ -from _typeshed import Incomplete +from typing_extensions import LiteralString -register: Incomplete +from django.template import Library -def compare_values(value1, value2): ... +register: Library + +@register.simple_tag +def compare_values(value1: str, value2: str) -> LiteralString: ... From 89812a79c25dcaf131fcc0680c197a08d81e73d7 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 4 Apr 2024 19:13:12 +0000 Subject: [PATCH 03/13] [pre-commit.ci] auto fixes from pre-commit.com hooks --- .../import_export/formats/base_formats.pyi | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/stubs/django-import-export/import_export/formats/base_formats.pyi b/stubs/django-import-export/import_export/formats/base_formats.pyi index 62e3ab8c0175..25bf1f700f15 100644 --- a/stubs/django-import-export/import_export/formats/base_formats.pyi +++ b/stubs/django-import-export/import_export/formats/base_formats.pyi @@ -26,8 +26,7 @@ class TablibFormat(Format): def get_title(self) -> str: ... def create_dataset(self, in_stream: str | bytes | IO[Any], **kwargs: Any) -> Dataset: ... -class TextFormat(TablibFormat): - ... +class TextFormat(TablibFormat): ... class CSV(TextFormat): def export_data(self, dataset: Dataset, **kwargs: Any) -> str: ... From 6d0be1bf6b9f19e8b8d1339ffae8e927d92d32d7 Mon Sep 17 00:00:00 2001 From: Viicos <65306057+Viicos@users.noreply.github.com> Date: Thu, 4 Apr 2024 21:34:48 +0200 Subject: [PATCH 04/13] More work: forms --- .../import_export/forms.pyi | 34 +++++++++++-------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/stubs/django-import-export/import_export/forms.pyi b/stubs/django-import-export/import_export/forms.pyi index f1eb594c065d..6d3da0cc338b 100644 --- a/stubs/django-import-export/import_export/forms.pyi +++ b/stubs/django-import-export/import_export/forms.pyi @@ -1,27 +1,31 @@ -from _typeshed import Incomplete +from typing import Any from django import forms +from django.contrib.admin.helpers import ActionForm + +from .formats.base_formats import Format +from .resources import Resource class ImportExportFormBase(forms.Form): - resource: Incomplete - def __init__(self, *args, resources: Incomplete | None = None, **kwargs) -> None: ... + resource: forms.ChoiceField + def __init__(self, *args: Any, resources: list[type[Resource]] | None = None, **kwargs: Any) -> None: ... class ImportForm(ImportExportFormBase): - import_file: Incomplete - input_format: Incomplete - def __init__(self, import_formats, *args, **kwargs) -> None: ... + import_file: forms.FileField + input_format: forms.ChoiceField + def __init__(self, import_formats: list[Format], *args: Any, **kwargs: Any) -> None: ... @property - def media(self): ... + def media(self) -> forms.Media: ... class ConfirmImportForm(forms.Form): - import_file_name: Incomplete - original_file_name: Incomplete - input_format: Incomplete - resource: Incomplete - def clean_import_file_name(self): ... + import_file_name: forms.CharField + original_file_name: forms.CharField + input_format: forms.CharField + resource: forms.CharField + def clean_import_file_name(self) -> str: ... class ExportForm(ImportExportFormBase): - file_format: Incomplete - def __init__(self, formats, *args, **kwargs) -> None: ... + file_format: forms.ChoiceField + def __init__(self, formats: list[Format], *args: Any, **kwargs: Any) -> None: ... -def export_action_form_factory(formats): ... +def export_action_form_factory(formats: list[tuple[str, str]]) -> type[ActionForm]: ... From 218742a3916b629406c27b1214c7d03a0e373268 Mon Sep 17 00:00:00 2001 From: Viicos <65306057+Viicos@users.noreply.github.com> Date: Thu, 4 Apr 2024 21:42:20 +0200 Subject: [PATCH 05/13] More work: instance_loaders --- .../import_export/fields.pyi | 2 +- .../import_export/instance_loaders.pyi | 26 +++++++++++-------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/stubs/django-import-export/import_export/fields.pyi b/stubs/django-import-export/import_export/fields.pyi index c2d71375ce61..2b7fad05077e 100644 --- a/stubs/django-import-export/import_export/fields.pyi +++ b/stubs/django-import-export/import_export/fields.pyi @@ -27,7 +27,7 @@ class Field: dehydrate_method: str | None = None, m2m_add: bool = False, ) -> None: ... - def clean(self, data: Mapping[str, Any], **kwargs: Any): ... + def clean(self, data: Mapping[str, Any], **kwargs: Any) -> Any: ... def get_value(self, obj: Model) -> Any: ... def save(self, obj: Model, data: Mapping[str, Any], is_m2m: bool = False, **kwargs: Any) -> None: ... def export(self, obj: Model) -> str: ... diff --git a/stubs/django-import-export/import_export/instance_loaders.pyi b/stubs/django-import-export/import_export/instance_loaders.pyi index 3e18afb6afb6..2b25d44e7cbd 100644 --- a/stubs/django-import-export/import_export/instance_loaders.pyi +++ b/stubs/django-import-export/import_export/instance_loaders.pyi @@ -1,17 +1,21 @@ -from _typeshed import Incomplete +from collections.abc import Mapping +from typing import Any + +from django.db.models import Model, QuerySet +from tablib import Dataset + +from .fields import Field +from .resources import Resource class BaseInstanceLoader: - resource: Incomplete - dataset: Incomplete - def __init__(self, resource, dataset: Incomplete | None = None) -> None: ... - def get_instance(self, row) -> None: ... + resource: Resource + dataset: Dataset | None + def __init__(self, resource: Resource, dataset: Dataset | None = None) -> None: ... + def get_instance(self, row: Mapping[str, Any]) -> Model | None: ... class ModelInstanceLoader(BaseInstanceLoader): - def get_queryset(self): ... - def get_instance(self, row): ... + def get_queryset(self) -> QuerySet: ... class CachedInstanceLoader(ModelInstanceLoader): - pk_field: Incomplete - all_instances: Incomplete - def __init__(self, *args, **kwargs) -> None: ... - def get_instance(self, row): ... + pk_field: Field + all_instances: dict[Any, Model] From d8c47e3423d2b768532410f26747b2698ddc7417 Mon Sep 17 00:00:00 2001 From: Viicos <65306057+Viicos@users.noreply.github.com> Date: Sat, 6 Apr 2024 13:43:17 +0200 Subject: [PATCH 06/13] More work: widgets `render` and `clean` should be redefined on each subclass as the return value can change. However, this is usually not an API users will have to deal with, so not worth the trouble --- .../import_export/widgets.pyi | 104 ++++++++---------- 1 file changed, 43 insertions(+), 61 deletions(-) diff --git a/stubs/django-import-export/import_export/widgets.pyi b/stubs/django-import-export/import_export/widgets.pyi index b1da466d450a..58d7cae69917 100644 --- a/stubs/django-import-export/import_export/widgets.pyi +++ b/stubs/django-import-export/import_export/widgets.pyi @@ -1,84 +1,66 @@ -from _typeshed import Incomplete +from collections.abc import Mapping +from datetime import datetime +from typing import Any, ClassVar, Generic, TypeVar -def format_datetime(value, datetime_format): ... +from django.db.models import Model, QuerySet + +def format_datetime(value: datetime, datetime_format: str) -> str: ... class Widget: - def clean(self, value, row: Incomplete | None = None, **kwargs): ... - def render(self, value, obj: Incomplete | None = None): ... + def clean(self, value: Any, row: Mapping[str, Any] | None = None, **kwargs: Any) -> Any: ... + def render(self, value: Any, obj: Model | None = None) -> Any: ... class NumberWidget(Widget): - coerce_to_string: Incomplete + coerce_to_string: bool def __init__(self, coerce_to_string: bool = False) -> None: ... - def is_empty(self, value): ... - def render(self, value, obj: Incomplete | None = None): ... - -class FloatWidget(NumberWidget): - def clean(self, value, row: Incomplete | None = None, **kwargs): ... - -class IntegerWidget(NumberWidget): - def clean(self, value, row: Incomplete | None = None, **kwargs): ... + def is_empty(self, value: Any) -> bool: ... + def render(self, value: Any, obj: Model | None = None) -> Any: ... -class DecimalWidget(NumberWidget): - def clean(self, value, row: Incomplete | None = None, **kwargs): ... +class FloatWidget(NumberWidget): ... +class IntegerWidget(NumberWidget): ... +class DecimalWidget(NumberWidget): ... class CharWidget(Widget): - coerce_to_string: Incomplete - allow_blank: Incomplete + coerce_to_string: bool + allow_blank: bool def __init__(self, coerce_to_string: bool = False, allow_blank: bool = False) -> None: ... - def clean(self, value, row: Incomplete | None = None, **kwargs): ... class BooleanWidget(Widget): - TRUE_VALUES: Incomplete - FALSE_VALUES: Incomplete - NULL_VALUES: Incomplete - def render(self, value, obj: Incomplete | None = None): ... - def clean(self, value, row: Incomplete | None = None, **kwargs): ... + TRUE_VALUES: ClassVar[list[str | int | bool]] + FALSE_VALUES: ClassVar[list[str | int | bool]] + NULL_VALUES: ClassVar[list[str | None]] class DateWidget(Widget): - formats: Incomplete - def __init__(self, format: Incomplete | None = None) -> None: ... - def clean(self, value, row: Incomplete | None = None, **kwargs): ... - def render(self, value, obj: Incomplete | None = None): ... + formats: tuple[str, ...] + def __init__(self, format: str | None = None) -> None: ... class DateTimeWidget(Widget): - formats: Incomplete - def __init__(self, format: Incomplete | None = None) -> None: ... - def clean(self, value, row: Incomplete | None = None, **kwargs): ... - def render(self, value, obj: Incomplete | None = None): ... + formats: tuple[str, ...] + def __init__(self, format: str | None = None) -> None: ... class TimeWidget(Widget): - formats: Incomplete - def __init__(self, format: Incomplete | None = None) -> None: ... - def clean(self, value, row: Incomplete | None = None, **kwargs): ... - def render(self, value, obj: Incomplete | None = None): ... + formats: tuple[str, ...] + def __init__(self, format: str | None = None) -> None: ... -class DurationWidget(Widget): - def clean(self, value, row: Incomplete | None = None, **kwargs): ... - def render(self, value, obj: Incomplete | None = None): ... +class DurationWidget(Widget): ... class SimpleArrayWidget(Widget): - separator: Incomplete - def __init__(self, separator: Incomplete | None = None) -> None: ... - def clean(self, value, row: Incomplete | None = None, **kwargs): ... - def render(self, value, obj: Incomplete | None = None): ... - -class JSONWidget(Widget): - def clean(self, value, row: Incomplete | None = None, **kwargs): ... - def render(self, value, obj: Incomplete | None = None): ... - -class ForeignKeyWidget(Widget): - model: Incomplete - field: Incomplete - use_natural_foreign_keys: Incomplete - def __init__(self, model, field: str = "pk", use_natural_foreign_keys: bool = False, **kwargs) -> None: ... - def get_queryset(self, value, row, *args, **kwargs): ... - def clean(self, value, row: Incomplete | None = None, **kwargs): ... - def render(self, value, obj: Incomplete | None = None): ... + separator: str + def __init__(self, separator: str | None = None) -> None: ... + +class JSONWidget(Widget): ... + +_ModelT = TypeVar("_ModelT", bound=Model) + +class ForeignKeyWidget(Widget, Generic[_ModelT]): + model: _ModelT + field: str + use_natural_foreign_keys: bool + def __init__(self, model: _ModelT, field: str = "pk", use_natural_foreign_keys: bool = False, **kwargs: Any) -> None: ... + def get_queryset(self, value: Any, row: Mapping[str, Any], *args: Any, **kwargs: Any) -> QuerySet[_ModelT]: ... class ManyToManyWidget(Widget): - model: Incomplete - separator: Incomplete - field: Incomplete - def __init__(self, model, separator: Incomplete | None = None, field: Incomplete | None = None, **kwargs) -> None: ... - def clean(self, value, row: Incomplete | None = None, **kwargs): ... - def render(self, value, obj: Incomplete | None = None): ... + model: Model + separator: str + field: str + def __init__(self, model, separator: str | None = None, field: str | None = None, **kwargs: Any) -> None: ... From 0248aa5bee184e5bc19d4fb27b8e2fe2fcc884c4 Mon Sep 17 00:00:00 2001 From: Viicos <65306057+Viicos@users.noreply.github.com> Date: Sun, 14 Apr 2024 11:05:10 +0200 Subject: [PATCH 07/13] Finish work --- stubs/django-import-export/METADATA.toml | 1 + .../import_export/instance_loaders.pyi | 9 +- .../import_export/mixins.pyi | 16 +- .../import_export/resources.pyi | 286 +++++++++++------- .../import_export/results.pyi | 9 +- 5 files changed, 194 insertions(+), 127 deletions(-) diff --git a/stubs/django-import-export/METADATA.toml b/stubs/django-import-export/METADATA.toml index d2d36aa0db19..bb94234e90ed 100644 --- a/stubs/django-import-export/METADATA.toml +++ b/stubs/django-import-export/METADATA.toml @@ -1,2 +1,3 @@ version = "3.3.*" upstream_repository = "https://github.com/django-import-export/django-import-export" +requires = ["tablib"] diff --git a/stubs/django-import-export/import_export/instance_loaders.pyi b/stubs/django-import-export/import_export/instance_loaders.pyi index 2b25d44e7cbd..eaf8bccdf0c0 100644 --- a/stubs/django-import-export/import_export/instance_loaders.pyi +++ b/stubs/django-import-export/import_export/instance_loaders.pyi @@ -1,4 +1,3 @@ -from collections.abc import Mapping from typing import Any from django.db.models import Model, QuerySet @@ -8,13 +7,13 @@ from .fields import Field from .resources import Resource class BaseInstanceLoader: - resource: Resource + resource: Resource[Any] dataset: Dataset | None - def __init__(self, resource: Resource, dataset: Dataset | None = None) -> None: ... - def get_instance(self, row: Mapping[str, Any]) -> Model | None: ... + def __init__(self, resource: Resource[Any], dataset: Dataset | None = None) -> None: ... + def get_instance(self, row: dict[str, Any]) -> Model | None: ... class ModelInstanceLoader(BaseInstanceLoader): - def get_queryset(self) -> QuerySet: ... + def get_queryset(self) -> QuerySet[Any]: ... class CachedInstanceLoader(ModelInstanceLoader): pk_field: Field diff --git a/stubs/django-import-export/import_export/mixins.pyi b/stubs/django-import-export/import_export/mixins.pyi index a652eda616ce..562b759fbe22 100644 --- a/stubs/django-import-export/import_export/mixins.pyi +++ b/stubs/django-import-export/import_export/mixins.pyi @@ -18,24 +18,24 @@ logger: Logger _ModelT = TypeVar("_ModelT", bound=Model) class BaseImportExportMixin(Generic[_ModelT]): - resource_class: type[Resource] - resource_classes: SupportsGetItem[int, type[Resource]] + resource_class: type[Resource[_ModelT]] + resource_classes: SupportsGetItem[int, type[Resource[_ModelT]]] @property def formats(self) -> list[type[Format]]: ... @property def export_formats(self) -> list[type[Format]]: ... @property def import_formats(self) -> list[type[Format]]: ... - def check_resource_classes(self, resource_classes: SupportsGetItem[int, type[Resource]]) -> None: ... - def get_resource_classes(self) -> list[type[Resource]]: ... + def check_resource_classes(self, resource_classes: SupportsGetItem[int, type[Resource[_ModelT]]]) -> None: ... + def get_resource_classes(self) -> list[type[Resource[_ModelT]]]: ... def get_resource_kwargs(self, request: HttpRequest, *args: Any, **kwargs: Any) -> dict[str, Any]: ... def get_resource_index(self, form: Form) -> int: ... class BaseImportMixin(BaseImportExportMixin[_ModelT]): - def get_import_resource_classes(self) -> list[type[Resource]]: ... + def get_import_resource_classes(self) -> list[type[Resource[_ModelT]]]: ... def get_import_formats(self) -> list[Format]: ... def get_import_resource_kwargs(self, request: HttpRequest, *args: Any, **kwargs: Any) -> dict[str, Any]: ... - def choose_import_resource_class(self, form: Form) -> type[Resource]: ... + def choose_import_resource_class(self, form: Form) -> type[Resource[_ModelT]]: ... class BaseExportMixin(BaseImportExportMixin[_ModelT]): model: Model @@ -47,8 +47,8 @@ class BaseExportMixin(BaseImportExportMixin[_ModelT]): @property def should_escape_formulae(self) -> bool: ... def get_export_formats(self) -> list[Format]: ... - def get_export_resource_classes(self) -> list[Resource]: ... - def choose_export_resource_class(self, form: Form) -> Resource: ... + def get_export_resource_classes(self) -> list[Resource[_ModelT]]: ... + def choose_export_resource_class(self, form: Form) -> Resource[_ModelT]: ... def get_export_resource_kwargs(self, request: HttpRequest, *args: Any, **kwargs: Any) -> dict[str, Any]: ... def get_data_for_export(self, request: HttpRequest, queryset: QuerySet[_ModelT], *args: Any, **kwargs: Any) -> Dataset: ... def get_export_filename(self, file_format: Format) -> str: ... diff --git a/stubs/django-import-export/import_export/resources.pyi b/stubs/django-import-export/import_export/resources.pyi index 1e28c8df2fae..b76db3cbfff8 100644 --- a/stubs/django-import-export/import_export/resources.pyi +++ b/stubs/django-import-export/import_export/resources.pyi @@ -1,163 +1,231 @@ -from _typeshed import Incomplete -from collections.abc import Generator +from collections import OrderedDict +from collections.abc import Iterator, Sequence +from functools import partial +from logging import Logger +from typing import Any, ClassVar, Generic, Literal, NoReturn, TypeVar, overload +from typing_extensions import deprecated + +from django.db.models import Field as DjangoField, ForeignObjectRel, Model, QuerySet +from django.utils.safestring import SafeString +from tablib import Dataset from .fields import Field +from .instance_loaders import BaseInstanceLoader +from .results import Error, Result, RowResult +from .widgets import ForeignKeyWidget, ManyToManyWidget, Widget -logger: Incomplete +logger: Logger -def get_related_model(field): ... -def has_natural_foreign_key(model): ... +@overload +def get_related_model(field: ForeignObjectRel) -> Model: ... +@overload +def get_related_model(field: DjangoField[Any, Any]) -> Model | None: ... +def has_natural_foreign_key(model: Model) -> bool: ... -class ResourceOptions: - model: Incomplete - fields: Incomplete - exclude: Incomplete - instance_loader_class: Incomplete - import_id_fields: Incomplete - export_order: Incomplete - widgets: Incomplete - use_transactions: Incomplete +class ResourceOptions(Generic[_ModelT]): + model: _ModelT + fields: Sequence[str] | None + exclude: Sequence[str] | None + instance_loader_class: type[BaseInstanceLoader] | None + import_id_fields: Sequence[str] + export_order: Sequence[str] | None + widgets: dict[str, Any] | None + use_transactions: bool | None skip_unchanged: bool report_skipped: bool clean_model_instances: bool - chunk_size: Incomplete + chunk_size: int | None skip_diff: bool skip_html_diff: bool use_bulk: bool batch_size: int force_init_instance: bool - using_db: Incomplete + using_db: str | None store_row_values: bool store_instance: bool use_natural_foreign_keys: bool class DeclarativeMetaclass(type): - def __new__(cls, name, bases, attrs): ... + def __new__(cls, name: str, bases: tuple[type[Any], ...], attrs: dict[str, Any]): ... class Diff: - left: Incomplete - right: Incomplete - new: Incomplete - def __init__(self, resource, instance, new) -> None: ... - def compare_with(self, resource, instance, dry_run: bool = False) -> None: ... - def as_html(self): ... + left: list[str] + right: list[str] + new: bool + def __init__(self, resource: Resource[_ModelT], instance: _ModelT, new: bool) -> None: ... + def compare_with(self, resource: Resource[_ModelT], instance: _ModelT, dry_run: bool = False) -> None: ... + def as_html(self) -> list[SafeString]: ... + +_ModelT = TypeVar("_ModelT", bound=Model) -class Resource(metaclass=DeclarativeMetaclass): - fields: Incomplete - create_instances: Incomplete - update_instances: Incomplete - delete_instances: Incomplete - def __init__(self, **kwargs) -> None: ... +class Resource(Generic[_ModelT], metaclass=DeclarativeMetaclass): + _meta: ResourceOptions[_ModelT] + fields: OrderedDict[str, Field] + create_instances: list[_ModelT] + update_instances: list[_ModelT] + delete_instances: list[_ModelT] + def __init__(self, **kwargs: Any) -> None: ... @classmethod - def get_result_class(self): ... + def get_result_class(self) -> type[Result]: ... @classmethod - def get_row_result_class(self): ... + def get_row_result_class(self) -> type[RowResult]: ... @classmethod - def get_error_result_class(self): ... + def get_error_result_class(self) -> type[Error]: ... @classmethod - def get_diff_class(self): ... + def get_diff_class(self) -> type[Diff]: ... @classmethod - def get_db_connection_name(self): ... - def get_use_transactions(self): ... - def get_chunk_size(self): ... - def get_fields(self, **kwargs): ... - def get_field_name(self, field): ... - def init_instance(self, row: Incomplete | None = None) -> None: ... - def get_instance(self, instance_loader, row): ... - def get_or_init_instance(self, instance_loader, row): ... - def get_import_id_fields(self): ... - def get_bulk_update_fields(self): ... + def get_db_connection_name(self) -> str: ... + def get_use_transactions(self) -> bool: ... + def get_chunk_size(self) -> int: ... + def get_fields(self, **kwargs: Any) -> list[Field]: ... + def get_field_name(self, field: Field) -> str: ... + def init_instance(self, row: dict[str, Any] | None = None) -> _ModelT: ... + def get_instance(self, instance_loader: BaseInstanceLoader, row: dict[str, Any]) -> _ModelT | None: ... + def get_or_init_instance(self, instance_loader: BaseInstanceLoader, row: dict[str, Any]) -> tuple[_ModelT | None, bool]: ... + def get_import_id_fields(self) -> Sequence[str]: ... + def get_bulk_update_fields(self) -> list[str]: ... def bulk_create( - self, using_transactions, dry_run, raise_errors, batch_size: Incomplete | None = None, result: Incomplete | None = None + self, + using_transactions: bool, + dry_run: bool, + raise_errors: bool, + batch_size: int | None = None, + result: Result | None = None, ) -> None: ... def bulk_update( - self, using_transactions, dry_run, raise_errors, batch_size: Incomplete | None = None, result: Incomplete | None = None + self, + using_transactions: bool, + dry_run: bool, + raise_errors: bool, + batch_size: int | None = None, + result: Result | None = None, ) -> None: ... - def bulk_delete(self, using_transactions, dry_run, raise_errors, result: Incomplete | None = None) -> None: ... + def bulk_delete(self, using_transactions: bool, dry_run: bool, raise_errors: bool, result: Result | None = None) -> None: ... def validate_instance( - self, instance, import_validation_errors: Incomplete | None = None, validate_unique: bool = True + self, instance: _ModelT, import_validation_errors: dict[str, Any] | None = None, validate_unique: bool = True ) -> None: ... - def save_instance(self, instance, is_create, using_transactions: bool = True, dry_run: bool = False) -> None: ... - def before_save_instance(self, instance, using_transactions, dry_run) -> None: ... - def after_save_instance(self, instance, using_transactions, dry_run) -> None: ... - def delete_instance(self, instance, using_transactions: bool = True, dry_run: bool = False) -> None: ... - def before_delete_instance(self, instance, dry_run) -> None: ... - def after_delete_instance(self, instance, dry_run) -> None: ... - def import_field(self, field, obj, data, is_m2m: bool = False, **kwargs) -> None: ... - def get_import_fields(self): ... - def import_obj(self, obj, data, dry_run, **kwargs) -> None: ... - def save_m2m(self, obj, data, using_transactions, dry_run) -> None: ... - def for_delete(self, row, instance): ... - def skip_row(self, instance, original, row, import_validation_errors: Incomplete | None = None): ... - def get_diff_headers(self): ... - def before_import(self, dataset, using_transactions, dry_run, **kwargs) -> None: ... - def after_import(self, dataset, result, using_transactions, dry_run, **kwargs) -> None: ... - def before_import_row(self, row, row_number: Incomplete | None = None, **kwargs) -> None: ... - def after_import_row(self, row, row_result, row_number: Incomplete | None = None, **kwargs) -> None: ... - def after_import_instance(self, instance, new, row_number: Incomplete | None = None, **kwargs) -> None: ... - def handle_import_error(self, result, error, raise_errors: bool = False) -> None: ... + def save_instance( + self, instance: _ModelT, is_create: bool, using_transactions: bool = True, dry_run: bool = False + ) -> None: ... + def before_save_instance(self, instance: _ModelT, using_transactions: bool, dry_run: bool) -> None: ... + def after_save_instance(self, instance: _ModelT, using_transactions: bool, dry_run: bool) -> None: ... + def delete_instance(self, instance: _ModelT, using_transactions: bool = True, dry_run: bool = False) -> None: ... + def before_delete_instance(self, instance: _ModelT, dry_run: bool) -> None: ... + def after_delete_instance(self, instance: _ModelT, dry_run: bool) -> None: ... + def import_field(self, field: Field, obj: _ModelT, data: dict[str, Any], is_m2m: bool = False, **kwargs: Any) -> None: ... + def get_import_fields(self) -> list[Field]: ... + def import_obj(self, obj: _ModelT, data: dict[str, Any], dry_run: bool, **kwargs: Any) -> None: ... + def save_m2m(self, obj: _ModelT, data: dict[str, Any], using_transactions: bool, dry_run: bool) -> None: ... + def for_delete(self, row: dict[str, Any], instance: _ModelT) -> bool: ... + def skip_row( + self, instance: _ModelT, original: _ModelT, row: dict[str, Any], import_validation_errors: dict[str, Any] | None = None + ) -> bool: ... + def get_diff_headers(self) -> list[str]: ... + def before_import(self, dataset: Dataset, using_transactions: bool, dry_run: bool, **kwargs: Any) -> None: ... + def after_import(self, dataset: Dataset, result: Result, using_transactions: bool, dry_run: bool, **kwargs: Any) -> None: ... + def before_import_row(self, row: dict[str, Any], row_number: int | None = None, **kwargs: Any) -> None: ... + def after_import_row( + self, row: dict[str, Any], row_result: RowResult, row_number: int | None = None, **kwargs: Any + ) -> None: ... + def after_import_instance(self, instance: _ModelT, new: bool, row_number: int | None = None, **kwargs: Any) -> None: ... + @overload + def handle_import_error(self, result: Result, error: Exception, raise_errors: Literal[True]) -> NoReturn: ... + @overload + def handle_import_error(self, result: Result, error: Exception, raise_errors: Literal[False] = ...) -> None: ... + @overload + @deprecated("raise_errors argument is deprecated and will be removed in a future release.") def import_row( self, - row, - instance_loader, + row: dict[str, Any], + instance_loader: BaseInstanceLoader, using_transactions: bool = True, dry_run: bool = False, - raise_errors: Incomplete | None = None, - **kwargs, - ): ... + *, + raise_errors: bool, + **kwargs: Any, + ) -> RowResult: ... + @overload + def import_row( + self, + row: dict[str, Any], + instance_loader: BaseInstanceLoader, + using_transactions: bool = True, + dry_run: bool = False, + raise_errors: None = None, + **kwargs: Any, + ) -> RowResult: ... def import_data( self, - dataset, + dataset: Dataset, dry_run: bool = False, raise_errors: bool = False, - use_transactions: Incomplete | None = None, + use_transactions: bool | None = None, collect_failed_rows: bool = False, rollback_on_validation_errors: bool = False, - **kwargs, - ): ... + **kwargs: Any, + ) -> Result: ... + @overload + @deprecated("rollback_on_validation_errors argument is deprecated and will be removed in a future release.") def import_data_inner( self, - dataset, - dry_run, - raise_errors, - using_transactions, - collect_failed_rows, - rollback_on_validation_errors: Incomplete | None = None, - **kwargs, - ): ... - def get_export_order(self): ... - def before_export(self, queryset, *args, **kwargs) -> None: ... - def after_export(self, queryset, data, *args, **kwargs) -> None: ... - def filter_export(self, queryset, *args, **kwargs): ... - def export_field(self, field, obj): ... - def get_export_fields(self): ... - def export_resource(self, obj): ... - def get_export_headers(self): ... - def get_user_visible_headers(self): ... - def get_user_visible_fields(self): ... - def iter_queryset(self, queryset) -> Generator[Incomplete, Incomplete, None]: ... - def export(self, *args, queryset: Incomplete | None = None, **kwargs): ... + dataset: Dataset, + dry_run: bool, + raise_errors: bool, + using_transactions: bool, + collect_failed_rows: bool, + rollback_on_validation_errors: bool, + **kwargs: Any, + ) -> Result: ... + @overload + def import_data_inner( + self, + dataset: Dataset, + dry_run: bool, + raise_errors: bool, + using_transactions: bool, + collect_failed_rows: bool, + rollback_on_validation_errors: None = None, + **kwargs: Any, + ) -> Result: ... + def get_export_order(self) -> tuple[str, ...]: ... + def before_export(self, queryset: QuerySet[_ModelT], *args: Any, **kwargs: Any) -> None: ... + def after_export(self, queryset: QuerySet[_ModelT], data: Dataset, *args: Any, **kwargs: Any) -> None: ... + def filter_export(self, queryset: QuerySet[_ModelT], *args: Any, **kwargs: Any) -> QuerySet[_ModelT]: ... + def export_field(self, field: Field, obj: _ModelT) -> str: ... + def get_export_fields(self) -> list[Field]: ... + def export_resource(self, obj: _ModelT) -> list[str]: ... + def get_export_headers(self) -> list[str]: ... + def get_user_visible_headers(self) -> list[str]: ... + def get_user_visible_fields(self) -> list[str]: ... + def iter_queryset(self, queryset: QuerySet[_ModelT]) -> Iterator[_ModelT]: ... + def export(self, *args: Any, queryset: QuerySet[_ModelT] | None = None, **kwargs: Any) -> Dataset: ... class ModelDeclarativeMetaclass(DeclarativeMetaclass): - def __new__(cls, name, bases, attrs): ... + def __new__(cls, name: str, bases: tuple[type[Any], ...], attrs: dict[str, Any]): ... -class ModelResource(Resource, metaclass=ModelDeclarativeMetaclass): - DEFAULT_RESOURCE_FIELD = Field - WIDGETS_MAP: Incomplete +class ModelResource(Resource[_ModelT], metaclass=ModelDeclarativeMetaclass): + DEFAULT_RESOURCE_FIELD: ClassVar[type[Field]] = Field + WIDGETS_MAP: ClassVar[dict[str, type[Widget]]] @classmethod - def get_m2m_widget(cls, field): ... + def get_m2m_widget(cls, field: DjangoField[Any, Any]) -> partial[ManyToManyWidget]: ... @classmethod - def get_fk_widget(cls, field): ... + def get_fk_widget(cls, field: DjangoField[Any, Any]) -> partial[ForeignKeyWidget[Any]]: ... @classmethod - def widget_from_django_field(cls, f, default=...): ... + def widget_from_django_field(cls, f: DjangoField[Any, Any], default: type[Widget] = ...): ... @classmethod - def widget_kwargs_for_field(self, field_name): ... + def widget_kwargs_for_field(self, field_name: str) -> dict[str, Any]: ... @classmethod - def field_from_django_field(cls, field_name, django_field, readonly): ... - def get_queryset(self): ... - def init_instance(self, row: Incomplete | None = None): ... - def after_import(self, dataset, result, using_transactions, dry_run, **kwargs) -> None: ... + def field_from_django_field(cls, field_name: str, django_field: DjangoField[Any, Any], readonly: bool) -> Field: ... + def get_queryset(self) -> QuerySet[_ModelT]: ... + def init_instance(self, row: dict[str, Any] | None = None): ... + def after_import(self, dataset: Dataset, result: Result, using_transactions: bool, dry_run: bool, **kwargs: Any) -> None: ... @classmethod - def get_display_name(cls): ... + def get_display_name(cls) -> str: ... + +_ResourceT = TypeVar("_ResourceT", bound=Resource[Any]) -def modelresource_factory(model, resource_class=...): ... +# HK Type Vars could help type the first overload: +@overload +def modelresource_factory(model: Model, resource_class: type[_ResourceT]) -> _ResourceT: ... +@overload +def modelresource_factory(model: _ModelT) -> ModelResource[_ModelT]: ... diff --git a/stubs/django-import-export/import_export/results.pyi b/stubs/django-import-export/import_export/results.pyi index be8cc0cb4b0c..243dbcdd36bc 100644 --- a/stubs/django-import-export/import_export/results.pyi +++ b/stubs/django-import-export/import_export/results.pyi @@ -1,5 +1,4 @@ from collections import OrderedDict -from collections.abc import Mapping from typing import Any, ClassVar, Iterator, Literal, TypeAlias from django.core.exceptions import ValidationError @@ -9,8 +8,8 @@ from tablib import Dataset class Error: error: Exception traceback: str - row: Mapping[str, Any] - def __init__(self, error: Exception, traceback: str | None = None, row: Mapping[str, Any] | None = None) -> None: ... + row: dict[str, Any] + def __init__(self, error: Exception, traceback: str | None = None, row: dict[str, Any] | None = None) -> None: ... _ImportType: TypeAlias = Literal["update", "new", "delete", "skip", "error", "invalid"] @@ -61,8 +60,8 @@ class Result: def append_row_result(self, row_result: RowResult) -> None: ... def append_base_error(self, error: Error) -> None: ... def add_dataset_headers(self, headers: list[str] | None) -> None: ... - def append_failed_row(self, row: Mapping[str, Any], error) -> None: ... - def append_invalid_row(self, number: int, row: Mapping[str, Any], validation_error: ValidationError) -> None: ... + def append_failed_row(self, row: dict[str, Any], error) -> None: ... + def append_invalid_row(self, number: int, row: dict[str, Any], validation_error: ValidationError) -> None: ... def increment_row_result_total(self, row_result: RowResult) -> None: ... def row_errors(self) -> list[tuple[int, Any]]: ... def has_errors(self) -> bool: ... From 39dcd6207213b894bc79a690eb67ef837e83aab1 Mon Sep 17 00:00:00 2001 From: Viicos <65306057+Viicos@users.noreply.github.com> Date: Sun, 14 Apr 2024 12:07:00 +0200 Subject: [PATCH 08/13] Apply lint --- stubs/django-import-export/import_export/admin.pyi | 12 ++++++------ stubs/django-import-export/import_export/fields.pyi | 4 ++-- stubs/django-import-export/import_export/mixins.pyi | 3 +-- .../django-import-export/import_export/resources.pyi | 2 +- stubs/django-import-export/import_export/results.pyi | 3 ++- stubs/django-import-export/import_export/utils.pyi | 3 ++- 6 files changed, 14 insertions(+), 13 deletions(-) diff --git a/stubs/django-import-export/import_export/admin.pyi b/stubs/django-import-export/import_export/admin.pyi index 3482b501d339..9f4271fb2da6 100644 --- a/stubs/django-import-export/import_export/admin.pyi +++ b/stubs/django-import-export/import_export/admin.pyi @@ -1,5 +1,6 @@ +from collections.abc import Callable from logging import Logger -from typing import Any, Callable, Generic, TypeVar +from typing import Any, TypeVar from typing_extensions import deprecated from django.contrib import admin @@ -14,7 +15,6 @@ from django.urls import URLPattern from tablib import Dataset from .formats.base_formats import Format -from .forms import ConfirmImportForm, ExportForm, ImportForm from .mixins import BaseExportMixin, BaseImportMixin from .results import Result from .tmp_storages import BaseStorage @@ -34,8 +34,8 @@ class ImportExportMixinBase: class ImportMixin(BaseImportMixin[_ModelT], ImportExportMixinBase): import_export_change_list_template: str import_template_name: str - import_form_class: type[Form] = ImportForm - confirm_form_class: type[Form] = ConfirmImportForm + import_form_class: type[Form] = ... + confirm_form_class: type[Form] = ... from_encoding: str skip_admin_log: bool | None tmp_storage_class: str | type[BaseStorage] @@ -78,11 +78,11 @@ class ImportMixin(BaseImportMixin[_ModelT], ImportExportMixinBase): def import_action(self, request: HttpRequest, *args: Any, **kwargs: Any) -> TemplateResponse: ... def changelist_view(self, request: HttpRequest, extra_context: dict[str, Any] | None = None) -> HttpResponse: ... -class ExportMixin(BaseExportMixin[_ModelT], ImportExportMixinBase, Generic[_ModelT]): +class ExportMixin(BaseExportMixin[_ModelT], ImportExportMixinBase): import_export_change_list_template: str export_template_name: str to_encoding: str | None - export_form_class: type[Form] = ExportForm + export_form_class: type[Form] = ... def get_urls(self) -> list[URLPattern]: ... def has_export_permission(self, request: HttpRequest) -> bool: ... def get_export_queryset(self, request: HttpRequest) -> QuerySet[_ModelT]: ... diff --git a/stubs/django-import-export/import_export/fields.pyi b/stubs/django-import-export/import_export/fields.pyi index 2b7fad05077e..5520ffe6a517 100644 --- a/stubs/django-import-export/import_export/fields.pyi +++ b/stubs/django-import-export/import_export/fields.pyi @@ -1,5 +1,5 @@ -from collections.abc import Mapping -from typing import Any, Callable, ClassVar +from collections.abc import Callable, Mapping +from typing import Any, ClassVar from django.db.models import Model from django.db.models.fields import NOT_PROVIDED diff --git a/stubs/django-import-export/import_export/mixins.pyi b/stubs/django-import-export/import_export/mixins.pyi index 562b759fbe22..38bf829cc6ff 100644 --- a/stubs/django-import-export/import_export/mixins.pyi +++ b/stubs/django-import-export/import_export/mixins.pyi @@ -10,7 +10,6 @@ from django.views.generic.edit import FormView from tablib import Dataset from .formats.base_formats import Format -from .forms import ExportForm from .resources import Resource logger: Logger @@ -54,7 +53,7 @@ class BaseExportMixin(BaseImportExportMixin[_ModelT]): def get_export_filename(self, file_format: Format) -> str: ... class ExportViewMixin(BaseExportMixin[_ModelT]): - form_class: type[Form] = ExportForm + form_class: type[Form] = ... def get_export_data(self, file_format: Format, queryset: QuerySet[_ModelT], *args: Any, **kwargs: Any) -> str | bytes: ... def get_context_data(self, **kwargs: Any) -> dict[str, Any]: ... def get_form_kwargs(self) -> dict[str, Any]: ... diff --git a/stubs/django-import-export/import_export/resources.pyi b/stubs/django-import-export/import_export/resources.pyi index b76db3cbfff8..117852d43f3e 100644 --- a/stubs/django-import-export/import_export/resources.pyi +++ b/stubs/django-import-export/import_export/resources.pyi @@ -204,7 +204,7 @@ class ModelDeclarativeMetaclass(DeclarativeMetaclass): def __new__(cls, name: str, bases: tuple[type[Any], ...], attrs: dict[str, Any]): ... class ModelResource(Resource[_ModelT], metaclass=ModelDeclarativeMetaclass): - DEFAULT_RESOURCE_FIELD: ClassVar[type[Field]] = Field + DEFAULT_RESOURCE_FIELD: ClassVar[type[Field]] = ... WIDGETS_MAP: ClassVar[dict[str, type[Widget]]] @classmethod def get_m2m_widget(cls, field: DjangoField[Any, Any]) -> partial[ManyToManyWidget]: ... diff --git a/stubs/django-import-export/import_export/results.pyi b/stubs/django-import-export/import_export/results.pyi index 243dbcdd36bc..112bb3153863 100644 --- a/stubs/django-import-export/import_export/results.pyi +++ b/stubs/django-import-export/import_export/results.pyi @@ -1,5 +1,6 @@ from collections import OrderedDict -from typing import Any, ClassVar, Iterator, Literal, TypeAlias +from collections.abc import Iterator +from typing import Any, ClassVar, Literal, TypeAlias from django.core.exceptions import ValidationError from django.db.models import Model diff --git a/stubs/django-import-export/import_export/utils.pyi b/stubs/django-import-export/import_export/utils.pyi index 9188f5e563a3..49985a35ada5 100644 --- a/stubs/django-import-export/import_export/utils.pyi +++ b/stubs/django-import-export/import_export/utils.pyi @@ -1,5 +1,6 @@ +from collections.abc import Callable from types import TracebackType -from typing import Any, Callable, TypeVar +from typing import Any, TypeVar from django.db.transaction import Atomic From affd8702a6c63677849516c43018b6a4bf4cd566 Mon Sep 17 00:00:00 2001 From: Viicos <65306057+Viicos@users.noreply.github.com> Date: Sun, 14 Apr 2024 12:07:58 +0200 Subject: [PATCH 09/13] Add `django-stubs` as a dependency --- stubs/django-import-export/METADATA.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stubs/django-import-export/METADATA.toml b/stubs/django-import-export/METADATA.toml index bb94234e90ed..5544ae5dda59 100644 --- a/stubs/django-import-export/METADATA.toml +++ b/stubs/django-import-export/METADATA.toml @@ -1,3 +1,3 @@ version = "3.3.*" upstream_repository = "https://github.com/django-import-export/django-import-export" -requires = ["tablib"] +requires = ["django-stubs", "tablib"] From 81ff10cdffe7a5963e11403cbf0085cab9a04533 Mon Sep 17 00:00:00 2001 From: Viicos <65306057+Viicos@users.noreply.github.com> Date: Sun, 14 Apr 2024 12:34:29 +0200 Subject: [PATCH 10/13] Fix/ignore mypy errors --- stubs/django-import-export/import_export/admin.pyi | 13 ++++++------- .../import_export/formats/base_formats.pyi | 10 +++++----- .../import_export/instance_loaders.pyi | 2 +- stubs/django-import-export/import_export/mixins.pyi | 6 +++--- .../import_export/resources.pyi | 2 +- .../django-import-export/import_export/results.pyi | 5 +++-- 6 files changed, 19 insertions(+), 19 deletions(-) diff --git a/stubs/django-import-export/import_export/admin.pyi b/stubs/django-import-export/import_export/admin.pyi index 9f4271fb2da6..fad5e8271cfc 100644 --- a/stubs/django-import-export/import_export/admin.pyi +++ b/stubs/django-import-export/import_export/admin.pyi @@ -12,7 +12,7 @@ from django.http.request import HttpRequest from django.http.response import HttpResponse from django.template.response import TemplateResponse from django.urls import URLPattern -from tablib import Dataset +from tablib import Dataset # type: ignore[import-untyped] from .formats.base_formats import Format from .mixins import BaseExportMixin, BaseImportMixin @@ -79,7 +79,7 @@ class ImportMixin(BaseImportMixin[_ModelT], ImportExportMixinBase): def changelist_view(self, request: HttpRequest, extra_context: dict[str, Any] | None = None) -> HttpResponse: ... class ExportMixin(BaseExportMixin[_ModelT], ImportExportMixinBase): - import_export_change_list_template: str + import_export_change_list_template: str | None export_template_name: str to_encoding: str | None export_form_class: type[Form] = ... @@ -94,15 +94,14 @@ class ExportMixin(BaseExportMixin[_ModelT], ImportExportMixinBase): def get_export_form_class(self) -> type[Form]: ... def export_action(self, request: HttpRequest, *args: Any, **kwargs: Any) -> TemplateResponse: ... def changelist_view(self, request: HttpRequest, extra_context: dict[str, Any] | None = None) -> HttpResponse: ... - def get_export_filename(self, request: HttpRequest, queryset: QuerySet[_ModelT], file_format: Format) -> str: ... + def get_export_filename(self, request: HttpRequest, queryset: QuerySet[_ModelT], file_format: Format) -> str: ... # type: ignore[override] class ImportExportMixin(ImportMixin[_ModelT], ExportMixin[_ModelT]): import_export_change_list_template: str -class ImportExportModelAdmin(ImportExportMixin[_ModelT], admin.ModelAdmin[_ModelT]): ... +class ImportExportModelAdmin(ImportExportMixin[_ModelT], admin.ModelAdmin[_ModelT]): ... # type: ignore[misc] class ExportActionMixin(ExportMixin[_ModelT]): - import_export_change_list_template: str | None action_form: type[ActionForm] def __init__(self, *args: Any, **kwargs: Any) -> None: ... def export_admin_action(self, request: HttpRequest, queryset: QuerySet[_ModelT]): ... @@ -110,5 +109,5 @@ class ExportActionMixin(ExportMixin[_ModelT]): @property def media(self) -> Media: ... -class ExportActionModelAdmin(ExportActionMixin[_ModelT], admin.ModelAdmin[_ModelT]): ... -class ImportExportActionModelAdmin(ImportMixin[_ModelT], ExportActionModelAdmin[_ModelT]): ... +class ExportActionModelAdmin(ExportActionMixin[_ModelT], admin.ModelAdmin[_ModelT]): ... # type: ignore[misc] +class ImportExportActionModelAdmin(ImportMixin[_ModelT], ExportActionModelAdmin[_ModelT]): ... # type: ignore[misc] diff --git a/stubs/django-import-export/import_export/formats/base_formats.pyi b/stubs/django-import-export/import_export/formats/base_formats.pyi index 25bf1f700f15..f88a5a03b967 100644 --- a/stubs/django-import-export/import_export/formats/base_formats.pyi +++ b/stubs/django-import-export/import_export/formats/base_formats.pyi @@ -2,7 +2,7 @@ from _typeshed import ReadableBuffer from typing import IO, Any, ClassVar from typing_extensions import Self -from tablib import Dataset +from tablib import Dataset # type: ignore[import-untyped] class Format: def get_title(self) -> type[Self]: ... @@ -23,8 +23,8 @@ class TablibFormat(Format): encoding: str | None def __init__(self, encoding: str | None = None) -> None: ... def get_format(self) -> type[Any]: ... - def get_title(self) -> str: ... - def create_dataset(self, in_stream: str | bytes | IO[Any], **kwargs: Any) -> Dataset: ... + def get_title(self) -> str: ... # type: ignore[override] + def create_dataset(self, in_stream: str | bytes | IO[Any], **kwargs: Any) -> Dataset: ... # type: ignore[override] class TextFormat(TablibFormat): ... @@ -48,10 +48,10 @@ class HTML(TextFormat): class XLS(TablibFormat): def export_data(self, dataset: Dataset, **kwargs: Any) -> bytes: ... - def create_dataset(self, in_stream: bytes) -> Dataset: ... + def create_dataset(self, in_stream: bytes) -> Dataset: ... # type: ignore[override] class XLSX(TablibFormat): def export_data(self, dataset: Dataset, **kwargs: Any) -> bytes: ... - def create_dataset(self, in_stream: ReadableBuffer) -> Dataset: ... + def create_dataset(self, in_stream: ReadableBuffer) -> Dataset: ... # type: ignore[override] DEFAULT_FORMATS: list[type[Format]] diff --git a/stubs/django-import-export/import_export/instance_loaders.pyi b/stubs/django-import-export/import_export/instance_loaders.pyi index eaf8bccdf0c0..147b086ff76a 100644 --- a/stubs/django-import-export/import_export/instance_loaders.pyi +++ b/stubs/django-import-export/import_export/instance_loaders.pyi @@ -1,7 +1,7 @@ from typing import Any from django.db.models import Model, QuerySet -from tablib import Dataset +from tablib import Dataset # type: ignore[import-untyped] from .fields import Field from .resources import Resource diff --git a/stubs/django-import-export/import_export/mixins.pyi b/stubs/django-import-export/import_export/mixins.pyi index 38bf829cc6ff..9bfdff21ef67 100644 --- a/stubs/django-import-export/import_export/mixins.pyi +++ b/stubs/django-import-export/import_export/mixins.pyi @@ -7,7 +7,7 @@ from django.forms import BaseForm, Form from django.http.request import HttpRequest from django.http.response import HttpResponse from django.views.generic.edit import FormView -from tablib import Dataset +from tablib import Dataset # type: ignore[import-untyped] from .formats.base_formats import Format from .resources import Resource @@ -53,12 +53,12 @@ class BaseExportMixin(BaseImportExportMixin[_ModelT]): def get_export_filename(self, file_format: Format) -> str: ... class ExportViewMixin(BaseExportMixin[_ModelT]): - form_class: type[Form] = ... + form_class: type[BaseForm] = ... def get_export_data(self, file_format: Format, queryset: QuerySet[_ModelT], *args: Any, **kwargs: Any) -> str | bytes: ... def get_context_data(self, **kwargs: Any) -> dict[str, Any]: ... def get_form_kwargs(self) -> dict[str, Any]: ... _FormT = TypeVar("_FormT", bound=BaseForm) -class ExportViewFormMixin(ExportViewMixin[_ModelT], FormView[_FormT]): +class ExportViewFormMixin(ExportViewMixin[_ModelT], FormView[_FormT]): # type: ignore[misc] def form_valid(self, form: _FormT) -> HttpResponse: ... diff --git a/stubs/django-import-export/import_export/resources.pyi b/stubs/django-import-export/import_export/resources.pyi index 117852d43f3e..ee4d849e6884 100644 --- a/stubs/django-import-export/import_export/resources.pyi +++ b/stubs/django-import-export/import_export/resources.pyi @@ -7,7 +7,7 @@ from typing_extensions import deprecated from django.db.models import Field as DjangoField, ForeignObjectRel, Model, QuerySet from django.utils.safestring import SafeString -from tablib import Dataset +from tablib import Dataset # type: ignore[import-untyped] from .fields import Field from .instance_loaders import BaseInstanceLoader diff --git a/stubs/django-import-export/import_export/results.pyi b/stubs/django-import-export/import_export/results.pyi index 112bb3153863..d231e2c43c1c 100644 --- a/stubs/django-import-export/import_export/results.pyi +++ b/stubs/django-import-export/import_export/results.pyi @@ -1,10 +1,11 @@ from collections import OrderedDict from collections.abc import Iterator -from typing import Any, ClassVar, Literal, TypeAlias +from typing import Any, ClassVar, Literal +from typing_extensions import TypeAlias from django.core.exceptions import ValidationError from django.db.models import Model -from tablib import Dataset +from tablib import Dataset # type: ignore[import-untyped] class Error: error: Exception From d78b26f327ce78e4ad2784ee0b4419f9a69b4723 Mon Sep 17 00:00:00 2001 From: Viicos <65306057+Viicos@users.noreply.github.com> Date: Mon, 15 Apr 2024 08:55:13 +0200 Subject: [PATCH 11/13] Apply feedback --- stubs/django-import-export/METADATA.toml | 5 ++++- stubs/django-import-export/import_export/admin.pyi | 5 +++-- .../import_export/formats/base_formats.pyi | 6 +++--- .../django-import-export/import_export/instance_loaders.pyi | 5 ++++- stubs/django-import-export/import_export/mixins.pyi | 6 ++++-- stubs/django-import-export/import_export/resources.pyi | 5 +++-- stubs/django-import-export/import_export/results.pyi | 4 +++- 7 files changed, 24 insertions(+), 12 deletions(-) diff --git a/stubs/django-import-export/METADATA.toml b/stubs/django-import-export/METADATA.toml index 5544ae5dda59..2f719571620a 100644 --- a/stubs/django-import-export/METADATA.toml +++ b/stubs/django-import-export/METADATA.toml @@ -1,3 +1,6 @@ version = "3.3.*" upstream_repository = "https://github.com/django-import-export/django-import-export" -requires = ["django-stubs", "tablib"] +requires = ["django-stubs"] # Add tablib when typed, and update _Incomplete aliases in stubs + +[tool.stubtest] +skip = true # Django requires configured settings at runtime diff --git a/stubs/django-import-export/import_export/admin.pyi b/stubs/django-import-export/import_export/admin.pyi index fad5e8271cfc..302afb061112 100644 --- a/stubs/django-import-export/import_export/admin.pyi +++ b/stubs/django-import-export/import_export/admin.pyi @@ -1,7 +1,8 @@ +from _typeshed import Incomplete from collections.abc import Callable from logging import Logger from typing import Any, TypeVar -from typing_extensions import deprecated +from typing_extensions import TypeAlias, deprecated from django.contrib import admin from django.contrib.admin.helpers import ActionForm @@ -12,13 +13,13 @@ from django.http.request import HttpRequest from django.http.response import HttpResponse from django.template.response import TemplateResponse from django.urls import URLPattern -from tablib import Dataset # type: ignore[import-untyped] from .formats.base_formats import Format from .mixins import BaseExportMixin, BaseImportMixin from .results import Result from .tmp_storages import BaseStorage +Dataset: TypeAlias = Incomplete # tablib.Dataset logger: Logger _ModelT = TypeVar("_ModelT", bound=Model) diff --git a/stubs/django-import-export/import_export/formats/base_formats.pyi b/stubs/django-import-export/import_export/formats/base_formats.pyi index f88a5a03b967..fdc77740b4e3 100644 --- a/stubs/django-import-export/import_export/formats/base_formats.pyi +++ b/stubs/django-import-export/import_export/formats/base_formats.pyi @@ -1,8 +1,8 @@ -from _typeshed import ReadableBuffer +from _typeshed import Incomplete, ReadableBuffer from typing import IO, Any, ClassVar -from typing_extensions import Self +from typing_extensions import Self, TypeAlias -from tablib import Dataset # type: ignore[import-untyped] +Dataset: TypeAlias = Incomplete # tablib.Dataset class Format: def get_title(self) -> type[Self]: ... diff --git a/stubs/django-import-export/import_export/instance_loaders.pyi b/stubs/django-import-export/import_export/instance_loaders.pyi index 147b086ff76a..433eb9f71796 100644 --- a/stubs/django-import-export/import_export/instance_loaders.pyi +++ b/stubs/django-import-export/import_export/instance_loaders.pyi @@ -1,11 +1,14 @@ +from _typeshed import Incomplete from typing import Any +from typing_extensions import TypeAlias from django.db.models import Model, QuerySet -from tablib import Dataset # type: ignore[import-untyped] from .fields import Field from .resources import Resource +Dataset: TypeAlias = Incomplete # tablib.Dataset + class BaseInstanceLoader: resource: Resource[Any] dataset: Dataset | None diff --git a/stubs/django-import-export/import_export/mixins.pyi b/stubs/django-import-export/import_export/mixins.pyi index 9bfdff21ef67..ac60a5a7ffef 100644 --- a/stubs/django-import-export/import_export/mixins.pyi +++ b/stubs/django-import-export/import_export/mixins.pyi @@ -1,17 +1,19 @@ -from _typeshed import SupportsGetItem +from _typeshed import Incomplete, SupportsGetItem from logging import Logger from typing import Any, Generic, TypeVar +from typing_extensions import TypeAlias from django.db.models import Model, QuerySet from django.forms import BaseForm, Form from django.http.request import HttpRequest from django.http.response import HttpResponse from django.views.generic.edit import FormView -from tablib import Dataset # type: ignore[import-untyped] from .formats.base_formats import Format from .resources import Resource +Dataset: TypeAlias = Incomplete # tablib.Dataset + logger: Logger _ModelT = TypeVar("_ModelT", bound=Model) diff --git a/stubs/django-import-export/import_export/resources.pyi b/stubs/django-import-export/import_export/resources.pyi index ee4d849e6884..3aad1cae9f72 100644 --- a/stubs/django-import-export/import_export/resources.pyi +++ b/stubs/django-import-export/import_export/resources.pyi @@ -1,19 +1,20 @@ +from _typeshed import Incomplete from collections import OrderedDict from collections.abc import Iterator, Sequence from functools import partial from logging import Logger from typing import Any, ClassVar, Generic, Literal, NoReturn, TypeVar, overload -from typing_extensions import deprecated +from typing_extensions import TypeAlias, deprecated from django.db.models import Field as DjangoField, ForeignObjectRel, Model, QuerySet from django.utils.safestring import SafeString -from tablib import Dataset # type: ignore[import-untyped] from .fields import Field from .instance_loaders import BaseInstanceLoader from .results import Error, Result, RowResult from .widgets import ForeignKeyWidget, ManyToManyWidget, Widget +Dataset: TypeAlias = Incomplete # tablib.Dataset logger: Logger @overload diff --git a/stubs/django-import-export/import_export/results.pyi b/stubs/django-import-export/import_export/results.pyi index d231e2c43c1c..249cb31fae20 100644 --- a/stubs/django-import-export/import_export/results.pyi +++ b/stubs/django-import-export/import_export/results.pyi @@ -1,3 +1,4 @@ +from _typeshed import Incomplete from collections import OrderedDict from collections.abc import Iterator from typing import Any, ClassVar, Literal @@ -5,7 +6,8 @@ from typing_extensions import TypeAlias from django.core.exceptions import ValidationError from django.db.models import Model -from tablib import Dataset # type: ignore[import-untyped] + +Dataset: TypeAlias = Incomplete # tablib.Dataset class Error: error: Exception From f3238d4eb4334255d30ba03be8580dc53371a0a8 Mon Sep 17 00:00:00 2001 From: Viicos <65306057+Viicos@users.noreply.github.com> Date: Mon, 15 Apr 2024 09:19:37 +0200 Subject: [PATCH 12/13] pyright errors --- stubs/django-import-export/import_export/admin.pyi | 2 +- stubs/django-import-export/import_export/forms.pyi | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/stubs/django-import-export/import_export/admin.pyi b/stubs/django-import-export/import_export/admin.pyi index 302afb061112..9805df5cdc79 100644 --- a/stubs/django-import-export/import_export/admin.pyi +++ b/stubs/django-import-export/import_export/admin.pyi @@ -74,7 +74,7 @@ class ImportMixin(BaseImportMixin[_ModelT], ImportExportMixinBase): def get_confirm_form_kwargs(self, request: HttpRequest, import_form: Form | None = None) -> dict[str, Any]: ... def get_confirm_form_initial(self, request: HttpRequest, import_form: Form | None) -> dict[str, Any]: ... def get_import_data_kwargs(self, request: HttpRequest, *args: Any, **kwargs: Any) -> dict[str, Any]: ... - def write_to_tmp_storage(self, import_file: File, input_format: Format) -> BaseStorage: ... + def write_to_tmp_storage(self, import_file: File[bytes], input_format: Format) -> BaseStorage: ... def add_data_read_fail_error_to_form(self, form: Form, e: Exception) -> None: ... def import_action(self, request: HttpRequest, *args: Any, **kwargs: Any) -> TemplateResponse: ... def changelist_view(self, request: HttpRequest, extra_context: dict[str, Any] | None = None) -> HttpResponse: ... diff --git a/stubs/django-import-export/import_export/forms.pyi b/stubs/django-import-export/import_export/forms.pyi index 6d3da0cc338b..333ad58e896f 100644 --- a/stubs/django-import-export/import_export/forms.pyi +++ b/stubs/django-import-export/import_export/forms.pyi @@ -8,7 +8,7 @@ from .resources import Resource class ImportExportFormBase(forms.Form): resource: forms.ChoiceField - def __init__(self, *args: Any, resources: list[type[Resource]] | None = None, **kwargs: Any) -> None: ... + def __init__(self, *args: Any, resources: list[type[Resource[Any]]] | None = None, **kwargs: Any) -> None: ... class ImportForm(ImportExportFormBase): import_file: forms.FileField From 366dff49249f73cf87670c8eb995ae8192b41b11 Mon Sep 17 00:00:00 2001 From: Viicos <65306057+Viicos@users.noreply.github.com> Date: Sat, 28 Dec 2024 10:20:12 +0100 Subject: [PATCH 13/13] Comment out decorator --- .../import_export/templatetags/import_export_tags.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stubs/django-import-export/import_export/templatetags/import_export_tags.pyi b/stubs/django-import-export/import_export/templatetags/import_export_tags.pyi index 9d6c0e4d7f68..dcbfa25a67c1 100644 --- a/stubs/django-import-export/import_export/templatetags/import_export_tags.pyi +++ b/stubs/django-import-export/import_export/templatetags/import_export_tags.pyi @@ -4,5 +4,5 @@ from django.template import Library register: Library -@register.simple_tag +# @register.simple_tag # commented out for pytype def compare_values(value1: str, value2: str) -> LiteralString: ...