From 0931e9815dac4a73ddf9b675f7b50696c797b82a Mon Sep 17 00:00:00 2001 From: Marti Raudsepp Date: Wed, 18 Oct 2023 13:12:04 +0300 Subject: [PATCH 1/9] Update Field constructor kwargs-only parameters The number of allowed positional parameters was significantly reduced by DRF upstream, but some remain regardless. --- rest_framework-stubs/fields.pyi | 11 +++++-- rest_framework-stubs/relations.pyi | 11 +++++-- rest_framework-stubs/serializers.pyi | 1 + tests/typecheck/test_fields.yml | 47 ++++++++-------------------- 4 files changed, 30 insertions(+), 40 deletions(-) diff --git a/rest_framework-stubs/fields.pyi b/rest_framework-stubs/fields.pyi index 8bbedcb9f..7778e165d 100644 --- a/rest_framework-stubs/fields.pyi +++ b/rest_framework-stubs/fields.pyi @@ -367,6 +367,7 @@ class DateTimeField(Field[datetime.datetime, datetime.datetime | str, str, Any]) format: str | None = ..., input_formats: Sequence[str] = ..., default_timezone: datetime.tzinfo | None = ..., + *, read_only: bool = ..., write_only: bool = ..., required: bool = ..., @@ -391,6 +392,7 @@ class DateField(Field[datetime.date, datetime.date | str, str, Any]): self, format: str | None = ..., input_formats: Sequence[str] = ..., + *, read_only: bool = ..., write_only: bool = ..., required: bool = ..., @@ -413,6 +415,7 @@ class TimeField(Field[datetime.time, datetime.time | str, str, Any]): self, format: str | None = ..., input_formats: Sequence[str] = ..., + *, read_only: bool = ..., write_only: bool = ..., required: bool = ..., @@ -493,6 +496,7 @@ class MultipleChoiceField( allow_empty: bool def __init__( self, + *, choices: Iterable[Any], read_only: bool = ..., write_only: bool = ..., @@ -521,6 +525,7 @@ class FilePathField(ChoiceField): allow_files: bool = ..., allow_folders: bool = ..., required: bool = ..., + *, read_only: bool = ..., write_only: bool = ..., default: _DefaultInitial[str] = ..., @@ -593,6 +598,7 @@ class ListField(Field[list[Any], list[Any], list[Any], Any]): min_length: int | None def __init__( self, + *, read_only: bool = ..., write_only: bool = ..., required: bool = ..., @@ -605,7 +611,6 @@ class ListField(Field[list[Any], list[Any], list[Any], Any]): error_messages: dict[str, StrOrPromise] = ..., validators: Sequence[Validator[list[Any]]] | None = ..., allow_null: bool = ..., - *, child: Field = ..., allow_empty: bool = ..., max_length: int = ..., @@ -618,6 +623,7 @@ class DictField(Field[dict[Any, Any], dict[Any, Any], dict[Any, Any], Any]): allow_empty: bool def __init__( self, + *, read_only: bool = ..., write_only: bool = ..., required: bool = ..., @@ -630,7 +636,6 @@ class DictField(Field[dict[Any, Any], dict[Any, Any], dict[Any, Any], Any]): error_messages: dict[str, StrOrPromise] = ..., validators: Sequence[Validator[dict[Any, Any]]] | None = ..., allow_null: bool = ..., - *, child: Field = ..., allow_empty: bool = ..., ): ... @@ -645,6 +650,7 @@ class JSONField(Field[dict[str, Any] | list[dict[str, Any]], dict[str, Any] | li decoder: type[JSONDecoder] | None def __init__( self, + *, read_only: bool = ..., write_only: bool = ..., required: bool = ..., @@ -657,7 +663,6 @@ class JSONField(Field[dict[str, Any] | list[dict[str, Any]], dict[str, Any] | li error_messages: dict[str, StrOrPromise] = ..., validators: Sequence[Validator[Any]] | None = ..., allow_null: bool = ..., - *, binary: bool = ..., encoder: type[JSONEncoder] | None = ..., decoder: type[JSONDecoder] | None = ..., diff --git a/rest_framework-stubs/relations.pyi b/rest_framework-stubs/relations.pyi index 07f7de078..d7b524da5 100644 --- a/rest_framework-stubs/relations.pyi +++ b/rest_framework-stubs/relations.pyi @@ -37,6 +37,7 @@ class RelatedField(Generic[_MT, _DT, _PT], Field[_MT, _DT, _PT, Any]): html_cutoff_text: str | None def __init__( self, + *, many: bool = ..., allow_empty: bool = ..., queryset: QuerySet[_MT] | Manager[_MT] | None = ..., @@ -76,6 +77,7 @@ class PrimaryKeyRelatedField(RelatedField[_MT, _MT, Any]): pk_field: str | None def __init__( self, + *, many: bool = ..., allow_empty: bool = ..., queryset: QuerySet[_MT] | Manager[_MT] | None = ..., @@ -104,6 +106,8 @@ class HyperlinkedRelatedField(RelatedField[_MT, str, Hyperlink]): view_name: str | None def __init__( self, + view_name: str, + *, many: bool = ..., allow_empty: bool = ..., queryset: QuerySet[_MT] | Manager[_MT] | None = ..., @@ -121,7 +125,6 @@ class HyperlinkedRelatedField(RelatedField[_MT, str, Hyperlink]): validators: Sequence[Validator[_MT]] | None = ..., error_messages: dict[str, StrOrPromise] | None = ..., style: dict[str, str] | None = ..., - view_name: str | None = ..., lookup_field: str | None = ..., lookup_url_kwarg: str | None = ..., format: str | None = ..., @@ -135,6 +138,8 @@ class SlugRelatedField(RelatedField[_MT, str, str]): slug_field: str | None def __init__( self, + slug_field: str, + *, many: bool = ..., allow_empty: bool = ..., queryset: QuerySet[_MT] | Manager[_MT] | None = ..., @@ -152,7 +157,6 @@ class SlugRelatedField(RelatedField[_MT, str, str]): validators: Sequence[Validator[_MT]] | None = ..., error_messages: dict[str, StrOrPromise] | None = ..., style: dict[str, str] | None = ..., - slug_field: str | None = ..., ): ... def to_internal_value(self, data: Any) -> _MT: ... def to_representation(self, value: _MT) -> str: ... @@ -165,6 +169,8 @@ class ManyRelatedField(Field[Sequence[Any], Sequence[Any], list[Any], Any]): allow_empty: bool def __init__( self, + child_relation: RelatedField = ..., + *, read_only: bool = ..., write_only: bool = ..., required: bool = ..., @@ -178,7 +184,6 @@ class ManyRelatedField(Field[Sequence[Any], Sequence[Any], list[Any], Any]): validators: Sequence[Validator[Sequence[Any]]] | None = ..., allow_null: bool = ..., allow_empty: bool = ..., - child_relation: RelatedField = ..., ): ... def get_value(self, dictionary: Mapping[Any, Any]) -> list[Any]: ... def get_choices(self, cutoff: int | None = ...) -> dict: ... diff --git a/rest_framework-stubs/serializers.pyi b/rest_framework-stubs/serializers.pyi index 7b198820b..05275a059 100644 --- a/rest_framework-stubs/serializers.pyi +++ b/rest_framework-stubs/serializers.pyi @@ -83,6 +83,7 @@ class BaseSerializer(Generic[_IN], Field[Any, Any, Any, _IN]): self, instance: _IN | None = ..., data: Any = ..., + *, partial: bool = ..., many: bool = ..., allow_empty: bool = ..., diff --git a/tests/typecheck/test_fields.yml b/tests/typecheck/test_fields.yml index 1ae55ae57..7e8b6fc90 100644 --- a/tests/typecheck/test_fields.yml +++ b/tests/typecheck/test_fields.yml @@ -9,8 +9,9 @@ - case: some_positional_args_fields main: | + from datetime import datetime, time from django.db import models - from rest_framework.fields import DecimalField, IPAddressField, SlugField, RegexField, ModelField, SerializerMethodField, ChoiceField + from rest_framework.fields import DecimalField, IPAddressField, SlugField, RegexField, ModelField, SerializerMethodField, ChoiceField, DateTimeField, DateField, TimeField DecimalField(1, 1, False, 1, 1, False, None, True) DecimalField(1, 1, False, 1, 1, False, None, True, True) # E: Too many positional arguments for "DecimalField" @@ -34,37 +35,15 @@ ChoiceField([]) ChoiceField([], False) # E: Too many positional arguments for "ChoiceField" -- case: most_positional_args_fields - main: | - from rest_framework.fields import Field, ListField, DictField, JSONField - f: Field = Field() - - ListField(True, True, True, [{"key": "value"}], [{"key": "value"}], 'src', 'l', 'ht', {"key": "value"}, {"key": "value"}, [lambda x: None], True) - ListField(True, True, True, [{"key": "value"}], [{"key": "value"}], 'src', 'l', 'ht', {"key": "value"}, {"key": "value"}, [lambda x: None], True, f) # E: Too many positional arguments for "ListField" - ListField(True, True, True, [{"key": "value"}], [{"key": "value"}], 'src', 'l', 'ht', {"key": "value"}, {"key": "value"}, [lambda x: None], True, child=f, allow_empty=True, max_length=1, min_length=1) - - DictField(True, True, True, {}, {}, 'src', 'l', 'ht', {}, {}, [], True) - DictField(True, True, True, {}, {}, 'src', 'l', 'ht', {}, {}, [], True, f) # E: Too many positional arguments for "DictField" - DictField(True, True, True, {}, {}, 'src', 'l', 'ht', {}, {}, [], True, child=f, allow_empty=True) - - JSONField(True, True, True, {}, {}, 'src', 'l', 'ht', {}, {}, [], True) - JSONField(True, True, True, {}, {}, 'src', 'l', 'ht', {}, {}, [], True, True) # E: Too many positional arguments for "JSONField" - JSONField(True, True, True, {}, {}, 'src', 'l', 'ht', {}, {}, [], True, binary=True, encoder=None, decoder=None) - -- case: all_positional_args_fields - main: | - from datetime import datetime, time - from rest_framework.fields import DateTimeField, DateField, TimeField - d: datetime = datetime.now() - DateTimeField('', [], None, True, True, True, d, d, 'src', 'l', 'ht', {}, {}, [], True) - DateTimeField('', [], None, True, True, True, d, d, 'src', 'l', 'ht', {}, {}, [], True, 1) # E: Too many arguments for "DateTimeField" + DateTimeField('', [], None, read_only=True, write_only=True, allow_null=True) + DateTimeField('', [], None, True) # E: Too many positional arguments for "DateTimeField" - DateField('', [], True, True, True, d, d, 'src', 'l', 'ht', {}, {}, [], True) - DateField('', [], True, True, True, d, d, 'src', 'l', 'ht', {}, {}, [], True, 1) # E: Too many arguments for "DateField" + DateField('', [], read_only=True, write_only=True, allow_null=True) + DateField('', [], True) # E: Too many positional arguments for "DateField" - TimeField('', [], True, True, True, time(hour=1), time(hour=1), 'src', 'l', 'ht', {}, {}, [], True) - TimeField('', [], True, True, True, time(hour=1), time(hour=1), 'src', 'l', 'ht', {}, {}, [], True, 1) # E: Too many arguments for "TimeField" + TimeField('', [], read_only=True, write_only=True, allow_null=True) + TimeField('', [], True) # E: Too many positional arguments for "TimeField" - case: default_and_inital_args_fields main: | @@ -107,11 +86,11 @@ def int_set_callback() -> Set[int]: ... def mixed_set_callback() -> Set[Union[int, str]]: ... - MultipleChoiceField([1], default={1}) - MultipleChoiceField(['test'], allow_null=True, default=None) - MultipleChoiceField([1], default=int_set_callback) - MultipleChoiceField([1, 'lulz'], default=mixed_set_callback) - MultipleChoiceField([1], default=lambda: [1]) # E: Argument "default" to "MultipleChoiceField" has incompatible type "Callable[[], List[int]]"; expected "Union[Set[Union[str, int]], Set[str], Set[int], Callable[[], Union[Set[Union[str, int]], Set[str], Set[int]]], None, _Empty]" # E: Incompatible return value type (got "List[int]", expected "Union[Set[Union[str, int]], Set[str], Set[int]]") + MultipleChoiceField(choices=[1], default={1}) + MultipleChoiceField(choices=['test'], allow_null=True, default=None) + MultipleChoiceField(choices=[1], default=int_set_callback) + MultipleChoiceField(choices=[1, 'lulz'], default=mixed_set_callback) + MultipleChoiceField(choices=[1], default=lambda: [1]) # E: Argument "default" to "MultipleChoiceField" has incompatible type "Callable[[], List[int]]"; expected "Union[Set[Union[str, int]], Set[str], Set[int], Callable[[], Union[Set[Union[str, int]], Set[str], Set[int]]], None, _Empty]" # E: Incompatible return value type (got "List[int]", expected "Union[Set[Union[str, int]], Set[str], Set[int]]") MultipleChoiceField(choices=[(1, "1"), (2, "2")], default={1}) MultipleChoiceField(choices=[(1, "1"), (2, "2")], default=[1]) # E: Argument "default" to "MultipleChoiceField" has incompatible type "List[int]"; expected "Union[Set[Union[str, int]], Set[str], Set[int], Callable[[], Union[Set[Union[str, int]], Set[str], Set[int]]], None, _Empty]" From c116d443182b42253e1a154c312eb5321c131ccb Mon Sep 17 00:00:00 2001 From: Marti Raudsepp Date: Wed, 18 Oct 2023 14:10:34 +0300 Subject: [PATCH 2/9] Initial stubtest testing with autogenerated allowlist --- .github/workflows/test.yml | 27 ++++ CONTRIBUTING.md | 14 ++ scripts/stubtest.sh | 14 ++ scripts/stubtest/allowlist.txt | 7 + scripts/stubtest/allowlist_todo.txt | 207 ++++++++++++++++++++++++++++ 5 files changed, 269 insertions(+) create mode 100755 scripts/stubtest.sh create mode 100644 scripts/stubtest/allowlist.txt create mode 100644 scripts/stubtest/allowlist_todo.txt diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2744986e6..7d58c71d7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -9,6 +9,7 @@ on: jobs: mypy-self-check: + timeout-minutes: 10 runs-on: ubuntu-latest strategy: matrix: @@ -31,6 +32,7 @@ jobs: run: mypy --cache-dir=/dev/null --no-incremental rest_framework-stubs test: + timeout-minutes: 10 runs-on: ubuntu-latest strategy: matrix: @@ -52,3 +54,28 @@ jobs: - name: Run tests run: pytest + + stubtest: + timeout-minutes: 10 + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ['3.12'] + fail-fast: false + steps: + - uses: actions/checkout@v4 + - name: Setup system dependencies + run: | + sudo apt-get update + sudo apt-get install binutils libproj-dev gdal-bin + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + pip install -U pip setuptools wheel + SETUPTOOLS_ENABLE_FEATURES=legacy-editable pip install -r ./requirements.txt + + - name: Run stubtest + run: bash ./scripts/stubtest.sh diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index df1b99d2d..474dcc44a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -7,6 +7,7 @@ This project is open source and community driven. As such we encourage code cont 3. Improve plugin code and extend its capabilities 4. Write tests 5. Update dependencies +6. Fix and remove things from our `stubtest/allowlist_todo.txt` Type stubs in `.pyi` files should follow [coding conventions from typeshed project](https://github.com/python/typeshed/blob/main/CONTRIBUTING.md#conventions). @@ -73,6 +74,19 @@ To execute the unit tests, simply run: pytest ``` +### Testing stubs with `stubtest` + +Run `bash ./scripts/stubtest.sh` to test that stubs and sources are in-line. + +We have two special files to allow errors: +1. `scripts/stubtest/allowlist.txt` where we store things that we really don't care about: hacks, DRF internal utility modules, things that are handled by our plugin, things that are not representable by type system, etc +2. `scripts/stubtest/allowlist_todo.txt` where we store all errors there are right now. Basically, this is a TODO list: we need to work through this list and fix things (or move entries to real `allowlist.txt`). In the end, ideally we can remove this file + +You might also want to disable `incremental` mode while working on `stubtest` changes. +This mode leads to several known problems (stubs do not show up or have strange errors). + +**Important**: right now we only run `stubtest` on Python 3.12 (because it is the latest released version at the moment), any other versions might generate different outputs. Any work to create per-version allowlists is welcome. + ## Submission Guidelines The workflow for contributions is fairly simple: diff --git a/scripts/stubtest.sh b/scripts/stubtest.sh new file mode 100755 index 000000000..3c6454ccd --- /dev/null +++ b/scripts/stubtest.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash + +# Run this script as `bash ./scripts/stubtest.sh` + +set -e + +export MYPYPATH='.' + +# TODO: remove `--ignore-positional-only` when ready +stubtest rest_framework \ + --mypy-config-file mypy.ini \ + --ignore-positional-only \ + --allowlist scripts/stubtest/allowlist.txt \ + --allowlist scripts/stubtest/allowlist_todo.txt diff --git a/scripts/stubtest/allowlist.txt b/scripts/stubtest/allowlist.txt new file mode 100644 index 000000000..9ea9c8af8 --- /dev/null +++ b/scripts/stubtest/allowlist.txt @@ -0,0 +1,7 @@ +# This is a true allow list with things that we really don't care about. +# `allowlist_todo.txt` is autogenerated by `stubtest --generate-allowlist` +# and might contain actual problems and things that we *do want* to fix. +# +# Please, move things here when you are sure that they really should be ignored. +# Comments about why things are ignored are mandatory. + diff --git a/scripts/stubtest/allowlist_todo.txt b/scripts/stubtest/allowlist_todo.txt new file mode 100644 index 000000000..fa3994a82 --- /dev/null +++ b/scripts/stubtest/allowlist_todo.txt @@ -0,0 +1,207 @@ +# Autogenerated by `stubtest` +# Unsorted: there are real problems and things we can really ignore. + +rest_framework.RemovedInDRF313Warning +rest_framework.RemovedInDRF314Warning +rest_framework.RemovedInDRF315Warning +rest_framework.__license__ +rest_framework.__title__ +rest_framework.authtoken.admin.TokenAdmin +rest_framework.authtoken.admin.TokenChangeList +rest_framework.authtoken.default_app_config +rest_framework.authtoken.management.commands.drf_create_token.UserModel +rest_framework.authtoken.migrations +rest_framework.authtoken.migrations.0001_initial +rest_framework.authtoken.migrations.0002_auto_20160226_1747 +rest_framework.authtoken.migrations.0003_tokenproxy +rest_framework.authtoken.models.Token.created +rest_framework.authtoken.models.Token.get_next_by_created +rest_framework.authtoken.models.Token.get_previous_by_created +rest_framework.authtoken.models.Token.key +rest_framework.authtoken.models.Token.user +rest_framework.authtoken.models.Token.user_id +rest_framework.authtoken.models.TokenProxy.get_next_by_created +rest_framework.authtoken.models.TokenProxy.get_previous_by_created +rest_framework.authtoken.models.TokenProxy.pk +rest_framework.authtoken.models.TokenProxy.user_id +rest_framework.authtoken.views.ObtainAuthToken.get_serializer +rest_framework.authtoken.views.ObtainAuthToken.get_serializer_context +rest_framework.authtoken.views.ObtainAuthToken.serializer_class +rest_framework.authtoken.views.obtain_auth_token +rest_framework.compat.CodeBlockPreprocessor +rest_framework.compat.Preprocessor +rest_framework.compat.QuerySet +rest_framework.compat.__all__ +rest_framework.compat.apply_markdown +rest_framework.decorators.ViewSetAction +rest_framework.decorators.action +rest_framework.default_app_config +rest_framework.documentation.get_docs_view +rest_framework.documentation.get_schemajs_view +rest_framework.documentation.include_docs_urls +rest_framework.fields.BooleanField.initial +rest_framework.fields.CharField.initial +rest_framework.fields.DateField.__init__ +rest_framework.fields.DateField.to_internal_value +rest_framework.fields.DateTimeField.__init__ +rest_framework.fields.DateTimeField.to_internal_value +rest_framework.fields.DecimalField.__init__ +rest_framework.fields.DictField.__init__ +rest_framework.fields.DictField.initial +rest_framework.fields.DurationField.to_internal_value +rest_framework.fields.Field.__init__ +rest_framework.fields.FilePathField.__init__ +rest_framework.fields.HStoreField.__init__ +rest_framework.fields.HiddenField.__init__ +rest_framework.fields.JSONField.__init__ +rest_framework.fields.ListField.__init__ +rest_framework.fields.ListField.initial +rest_framework.fields.ListField.to_representation +rest_framework.fields.ModelField.get_attribute +rest_framework.fields.ModelField.to_representation +rest_framework.fields.MultipleChoiceField.__init__ +rest_framework.fields.NullBooleanField +rest_framework.fields.Option +rest_framework.fields.REGEX_TYPE +rest_framework.fields.ReadOnlyField.__init__ +rest_framework.fields.SupportsToPython +rest_framework.fields.TimeField.__init__ +rest_framework.fields.TimeField.to_internal_value +rest_framework.fields._UnvalidatedField.__init__ +rest_framework.fields.empty +rest_framework.generics.BaseFilterProtocol +rest_framework.generics.UsesQuerySet +rest_framework.negotiation.DefaultContentNegotiation.settings +rest_framework.pagination.HtmlContext +rest_framework.pagination.HtmlContextWithPageLinks +rest_framework.parsers.BaseParser.media_type +rest_framework.parsers.FileUploadParser.get_encoded_filename +rest_framework.relations.HyperlinkedIdentityField.__init__ +rest_framework.relations.HyperlinkedRelatedField.__init__ +rest_framework.relations.HyperlinkedRelatedField.get_object +rest_framework.relations.ManyRelatedField.__init__ +rest_framework.relations.ManyRelatedField.initial +rest_framework.relations.ManyRelatedField.to_representation +rest_framework.relations.PrimaryKeyRelatedField.__init__ +rest_framework.relations.RelatedField.__init__ +rest_framework.relations.SlugRelatedField.__init__ +rest_framework.relations.SlugRelatedField.to_representation +rest_framework.relations.StringRelatedField.__init__ +rest_framework.renderers.BaseRenderer.format +rest_framework.renderers.BaseRenderer.media_type +rest_framework.renderers.BrowsableAPIRenderer.get_extra_actions +rest_framework.renderers.CoreAPIJSONOpenAPIRenderer.ensure_ascii +rest_framework.renderers.CoreJSONRenderer.render +rest_framework.renderers.JSONOpenAPIRenderer.encoder_class +rest_framework.renderers.JSONOpenAPIRenderer.ensure_ascii +rest_framework.renderers._BaseOpenAPIRenderer.__init__ +rest_framework.renderers._BaseOpenAPIRenderer.render +rest_framework.request.Request.DATA +rest_framework.request.Request.QUERY_PARAMS +rest_framework.routers.BaseRouter +rest_framework.routers.BaseRouter.register +rest_framework.routers.DefaultRouter +rest_framework.routers.DefaultRouter.APIRootView +rest_framework.routers.DefaultRouter.APISchemaView +rest_framework.routers.DefaultRouter.SchemaGenerator +rest_framework.routers.RenameRouterMethods +rest_framework.routers.SimpleRouter +rest_framework.schemas.SchemaGenerator.__init__ +rest_framework.schemas.coreapi.SchemaGenerator.__init__ +rest_framework.schemas.generators.common_path +rest_framework.schemas.generators.endpoint_ordering +rest_framework.schemas.get_schema_view +rest_framework.schemas.openapi.AutoSchema.__init__ +rest_framework.schemas.openapi.AutoSchema.get_reference +rest_framework.schemas.openapi.AutoSchema.get_request_serializer +rest_framework.schemas.openapi.AutoSchema.get_response_serializer +rest_framework.schemas.openapi.DRFOpenAPIInfo +rest_framework.schemas.openapi.DRFOpenAPISchema +rest_framework.schemas.openapi.ExternalDocumentationObject +rest_framework.schemas.openapi.SchemaGenerator.get_schema +rest_framework.schemas.views.SchemaView.__init__ +rest_framework.schemas.views.SchemaView.get +rest_framework.schemas.views.SchemaView.renderer_classes +rest_framework.serializers.APIException +rest_framework.serializers.AuthenticationFailed +rest_framework.serializers.BaseSerializer.__init__ +rest_framework.serializers.BaseSerializer.is_valid +rest_framework.serializers.BooleanField.initial +rest_framework.serializers.CharField.initial +rest_framework.serializers.DateField.__init__ +rest_framework.serializers.DateField.to_internal_value +rest_framework.serializers.DateTimeField.__init__ +rest_framework.serializers.DateTimeField.to_internal_value +rest_framework.serializers.DecimalField.__init__ +rest_framework.serializers.DictField.__init__ +rest_framework.serializers.DictField.initial +rest_framework.serializers.DurationField.to_internal_value +rest_framework.serializers.Field.__init__ +rest_framework.serializers.FilePathField.__init__ +rest_framework.serializers.HStoreField.__init__ +rest_framework.serializers.HiddenField.__init__ +rest_framework.serializers.HyperlinkedIdentityField.__init__ +rest_framework.serializers.HyperlinkedRelatedField.__init__ +rest_framework.serializers.HyperlinkedRelatedField.get_object +rest_framework.serializers.JSONField.__init__ +rest_framework.serializers.ListField.__init__ +rest_framework.serializers.ListField.initial +rest_framework.serializers.ListField.to_representation +rest_framework.serializers.ListSerializer.is_valid +rest_framework.serializers.ListSerializer.to_representation +rest_framework.serializers.ManyRelatedField.__init__ +rest_framework.serializers.ManyRelatedField.initial +rest_framework.serializers.ManyRelatedField.to_representation +rest_framework.serializers.MethodNotAllowed +rest_framework.serializers.ModelField.get_attribute +rest_framework.serializers.ModelField.to_representation +rest_framework.serializers.ModelSerializer.Meta +rest_framework.serializers.ModelSerializer.__init__ +rest_framework.serializers.MultipleChoiceField.__init__ +rest_framework.serializers.NotAcceptable +rest_framework.serializers.NotAuthenticated +rest_framework.serializers.NotFound +rest_framework.serializers.NullBooleanField +rest_framework.serializers.ParseError +rest_framework.serializers.PermissionDenied +rest_framework.serializers.PrimaryKeyRelatedField.__init__ +rest_framework.serializers.ReadOnlyField.__init__ +rest_framework.serializers.RelatedField.__init__ +rest_framework.serializers.SlugRelatedField.__init__ +rest_framework.serializers.SlugRelatedField.to_representation +rest_framework.serializers.StringRelatedField.__init__ +rest_framework.serializers.Throttled +rest_framework.serializers.TimeField.__init__ +rest_framework.serializers.TimeField.to_internal_value +rest_framework.serializers.UnsupportedMediaType +rest_framework.serializers.empty +rest_framework.settings.DefaultsSettings +rest_framework.settings.api_settings +rest_framework.status.HTTP_102_PROCESSING +rest_framework.status.HTTP_103_EARLY_HINTS +rest_framework.status.HTTP_421_MISDIRECTED_REQUEST +rest_framework.status.HTTP_425_TOO_EARLY +rest_framework.templatetags.rest_framework.urlize_quoted_links +rest_framework.test.APIClient.options +rest_framework.test.APIRequestFactory.delete +rest_framework.test.APIRequestFactory.get +rest_framework.test.APIRequestFactory.options +rest_framework.test.APIRequestFactory.patch +rest_framework.test.APIRequestFactory.post +rest_framework.test.APIRequestFactory.put +rest_framework.test.RequestsClient.__init__ +rest_framework.test.cleanup_url_patterns +rest_framework.throttling.SimpleRateThrottle.cache +rest_framework.utils.encoders.JSONEncoder.default +rest_framework.utils.field_mapping.get_unique_error_message +rest_framework.utils.formatting.lazy_format.__mod__ +rest_framework.utils.json.dump +rest_framework.utils.json.dumps +rest_framework.utils.json.load +rest_framework.utils.json.loads +rest_framework.validators.BaseUniqueForValidator.message +rest_framework.validators.ContextValidator +rest_framework.validators.Validator +rest_framework.views.APIView.metadata_class +rest_framework.views.AsView +rest_framework.views.GenericView From 33ff17f9597ea047afe5fa71b29a536630358fdc Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 18 Oct 2023 11:15:03 +0000 Subject: [PATCH 3/9] [pre-commit.ci] auto fixes from pre-commit.com hooks --- scripts/stubtest/allowlist.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/stubtest/allowlist.txt b/scripts/stubtest/allowlist.txt index 9ea9c8af8..3004daa55 100644 --- a/scripts/stubtest/allowlist.txt +++ b/scripts/stubtest/allowlist.txt @@ -4,4 +4,3 @@ # # Please, move things here when you are sure that they really should be ignored. # Comments about why things are ignored are mandatory. - From a201f3206e7c895fad8a3a883bd83d6bdd6d0fdb Mon Sep 17 00:00:00 2001 From: Marti Raudsepp Date: Wed, 18 Oct 2023 14:20:58 +0300 Subject: [PATCH 4/9] Update CONTRIBUTING.md --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 474dcc44a..30bd24163 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -7,7 +7,7 @@ This project is open source and community driven. As such we encourage code cont 3. Improve plugin code and extend its capabilities 4. Write tests 5. Update dependencies -6. Fix and remove things from our `stubtest/allowlist_todo.txt` +6. Fix and remove things from our `scripts/stubtest/allowlist_todo.txt` Type stubs in `.pyi` files should follow [coding conventions from typeshed project](https://github.com/python/typeshed/blob/main/CONTRIBUTING.md#conventions). From 6625a830bd5e3a98962e5d0f944d8c457996dead Mon Sep 17 00:00:00 2001 From: Marti Raudsepp Date: Wed, 18 Oct 2023 15:04:17 +0300 Subject: [PATCH 5/9] f1 --- rest_framework-stubs/fields.pyi | 1 - rest_framework-stubs/serializers.pyi | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/rest_framework-stubs/fields.pyi b/rest_framework-stubs/fields.pyi index eeadc3664..0c92bbc3a 100644 --- a/rest_framework-stubs/fields.pyi +++ b/rest_framework-stubs/fields.pyi @@ -339,7 +339,6 @@ class DecimalField(Field[Decimal, int | float | str | Decimal, str, Any]): min_value: Decimal | int | float = ..., localize: bool = ..., rounding: str | None = ..., - normalize_output: bool = ..., *, read_only: bool = ..., write_only: bool = ..., diff --git a/rest_framework-stubs/serializers.pyi b/rest_framework-stubs/serializers.pyi index d98d7d7c6..7f51c268f 100644 --- a/rest_framework-stubs/serializers.pyi +++ b/rest_framework-stubs/serializers.pyi @@ -204,6 +204,7 @@ class ModelSerializer(Serializer, BaseSerializer[_MT]): self, instance: None | _MT | Sequence[_MT] | QuerySet[_MT] | Manager[_MT] = ..., data: Any = ..., + *, partial: bool = ..., many: bool = ..., context: dict[str, Any] = ..., From ead7cabae62b1f4ac2779244f04f964deaf6d6c4 Mon Sep 17 00:00:00 2001 From: Marti Raudsepp Date: Wed, 18 Oct 2023 15:04:54 +0300 Subject: [PATCH 6/9] f2 --- rest_framework-stubs/fields.pyi | 1 + 1 file changed, 1 insertion(+) diff --git a/rest_framework-stubs/fields.pyi b/rest_framework-stubs/fields.pyi index 0c92bbc3a..fb6a2540c 100644 --- a/rest_framework-stubs/fields.pyi +++ b/rest_framework-stubs/fields.pyi @@ -89,6 +89,7 @@ class Field(Generic[_VT, _DT, _RP, _IN]): write_only: bool def __init__( self, + *, read_only: bool = ..., write_only: bool = ..., required: bool = ..., From 696955c2e541336c0d402daf01be7aa306c92973 Mon Sep 17 00:00:00 2001 From: Marti Raudsepp Date: Wed, 18 Oct 2023 15:21:05 +0300 Subject: [PATCH 7/9] Update allowlist --- scripts/stubtest/allowlist.txt | 10 ++++++++++ scripts/stubtest/allowlist_todo.txt | 31 ----------------------------- 2 files changed, 10 insertions(+), 31 deletions(-) diff --git a/scripts/stubtest/allowlist.txt b/scripts/stubtest/allowlist.txt index 3004daa55..e93a0e365 100644 --- a/scripts/stubtest/allowlist.txt +++ b/scripts/stubtest/allowlist.txt @@ -4,3 +4,13 @@ # # Please, move things here when you are sure that they really should be ignored. # Comments about why things are ignored are mandatory. + +# Constructor arguments *appear* optional but actually throw exception +rest_framework.serializers.HyperlinkedIdentityField.__init__ +rest_framework.serializers.HyperlinkedRelatedField.__init__ +rest_framework.serializers.ManyRelatedField.__init__ +rest_framework.relations.SlugRelatedField.__init__ +rest_framework.relations.HyperlinkedIdentityField.__init__ +rest_framework.relations.HyperlinkedRelatedField.__init__ +rest_framework.relations.ManyRelatedField.__init__ +rest_framework.serializers.SlugRelatedField.__init__ diff --git a/scripts/stubtest/allowlist_todo.txt b/scripts/stubtest/allowlist_todo.txt index fa3994a82..042b848b7 100644 --- a/scripts/stubtest/allowlist_todo.txt +++ b/scripts/stubtest/allowlist_todo.txt @@ -46,28 +46,20 @@ rest_framework.fields.DateField.to_internal_value rest_framework.fields.DateTimeField.__init__ rest_framework.fields.DateTimeField.to_internal_value rest_framework.fields.DecimalField.__init__ -rest_framework.fields.DictField.__init__ rest_framework.fields.DictField.initial rest_framework.fields.DurationField.to_internal_value rest_framework.fields.Field.__init__ rest_framework.fields.FilePathField.__init__ -rest_framework.fields.HStoreField.__init__ -rest_framework.fields.HiddenField.__init__ -rest_framework.fields.JSONField.__init__ -rest_framework.fields.ListField.__init__ rest_framework.fields.ListField.initial rest_framework.fields.ListField.to_representation rest_framework.fields.ModelField.get_attribute rest_framework.fields.ModelField.to_representation -rest_framework.fields.MultipleChoiceField.__init__ rest_framework.fields.NullBooleanField rest_framework.fields.Option rest_framework.fields.REGEX_TYPE -rest_framework.fields.ReadOnlyField.__init__ rest_framework.fields.SupportsToPython rest_framework.fields.TimeField.__init__ rest_framework.fields.TimeField.to_internal_value -rest_framework.fields._UnvalidatedField.__init__ rest_framework.fields.empty rest_framework.generics.BaseFilterProtocol rest_framework.generics.UsesQuerySet @@ -76,17 +68,10 @@ rest_framework.pagination.HtmlContext rest_framework.pagination.HtmlContextWithPageLinks rest_framework.parsers.BaseParser.media_type rest_framework.parsers.FileUploadParser.get_encoded_filename -rest_framework.relations.HyperlinkedIdentityField.__init__ -rest_framework.relations.HyperlinkedRelatedField.__init__ rest_framework.relations.HyperlinkedRelatedField.get_object -rest_framework.relations.ManyRelatedField.__init__ rest_framework.relations.ManyRelatedField.initial rest_framework.relations.ManyRelatedField.to_representation -rest_framework.relations.PrimaryKeyRelatedField.__init__ -rest_framework.relations.RelatedField.__init__ -rest_framework.relations.SlugRelatedField.__init__ rest_framework.relations.SlugRelatedField.to_representation -rest_framework.relations.StringRelatedField.__init__ rest_framework.renderers.BaseRenderer.format rest_framework.renderers.BaseRenderer.media_type rest_framework.renderers.BrowsableAPIRenderer.get_extra_actions @@ -124,7 +109,6 @@ rest_framework.schemas.views.SchemaView.get rest_framework.schemas.views.SchemaView.renderer_classes rest_framework.serializers.APIException rest_framework.serializers.AuthenticationFailed -rest_framework.serializers.BaseSerializer.__init__ rest_framework.serializers.BaseSerializer.is_valid rest_framework.serializers.BooleanField.initial rest_framework.serializers.CharField.initial @@ -133,43 +117,28 @@ rest_framework.serializers.DateField.to_internal_value rest_framework.serializers.DateTimeField.__init__ rest_framework.serializers.DateTimeField.to_internal_value rest_framework.serializers.DecimalField.__init__ -rest_framework.serializers.DictField.__init__ rest_framework.serializers.DictField.initial rest_framework.serializers.DurationField.to_internal_value rest_framework.serializers.Field.__init__ rest_framework.serializers.FilePathField.__init__ -rest_framework.serializers.HStoreField.__init__ -rest_framework.serializers.HiddenField.__init__ -rest_framework.serializers.HyperlinkedIdentityField.__init__ -rest_framework.serializers.HyperlinkedRelatedField.__init__ rest_framework.serializers.HyperlinkedRelatedField.get_object -rest_framework.serializers.JSONField.__init__ -rest_framework.serializers.ListField.__init__ rest_framework.serializers.ListField.initial rest_framework.serializers.ListField.to_representation rest_framework.serializers.ListSerializer.is_valid rest_framework.serializers.ListSerializer.to_representation -rest_framework.serializers.ManyRelatedField.__init__ rest_framework.serializers.ManyRelatedField.initial rest_framework.serializers.ManyRelatedField.to_representation rest_framework.serializers.MethodNotAllowed rest_framework.serializers.ModelField.get_attribute rest_framework.serializers.ModelField.to_representation rest_framework.serializers.ModelSerializer.Meta -rest_framework.serializers.ModelSerializer.__init__ -rest_framework.serializers.MultipleChoiceField.__init__ rest_framework.serializers.NotAcceptable rest_framework.serializers.NotAuthenticated rest_framework.serializers.NotFound rest_framework.serializers.NullBooleanField rest_framework.serializers.ParseError rest_framework.serializers.PermissionDenied -rest_framework.serializers.PrimaryKeyRelatedField.__init__ -rest_framework.serializers.ReadOnlyField.__init__ -rest_framework.serializers.RelatedField.__init__ -rest_framework.serializers.SlugRelatedField.__init__ rest_framework.serializers.SlugRelatedField.to_representation -rest_framework.serializers.StringRelatedField.__init__ rest_framework.serializers.Throttled rest_framework.serializers.TimeField.__init__ rest_framework.serializers.TimeField.to_internal_value From d6646e81be2527a993b71cc86a35532f788b1700 Mon Sep 17 00:00:00 2001 From: Marti Raudsepp Date: Wed, 18 Oct 2023 15:29:08 +0300 Subject: [PATCH 8/9] Fix DecimalField test --- tests/typecheck/test_fields.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/typecheck/test_fields.yml b/tests/typecheck/test_fields.yml index 7e8b6fc90..eb3928c81 100644 --- a/tests/typecheck/test_fields.yml +++ b/tests/typecheck/test_fields.yml @@ -13,8 +13,8 @@ from django.db import models from rest_framework.fields import DecimalField, IPAddressField, SlugField, RegexField, ModelField, SerializerMethodField, ChoiceField, DateTimeField, DateField, TimeField - DecimalField(1, 1, False, 1, 1, False, None, True) - DecimalField(1, 1, False, 1, 1, False, None, True, True) # E: Too many positional arguments for "DecimalField" + DecimalField(1, 1, False, 1, 1, False, None) + DecimalField(1, 1, False, 1, 1, False, None, True) # E: Too many positional arguments for "DecimalField" IPAddressField('both') IPAddressField('both', True) # E: Too many positional arguments for "IPAddressField" From 1b0dcc747a0017766867a8bfdc28ea8e1e2ddc32 Mon Sep 17 00:00:00 2001 From: Marti Raudsepp Date: Wed, 18 Oct 2023 18:06:11 +0300 Subject: [PATCH 9/9] Sort allowlist --- scripts/stubtest/allowlist.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/stubtest/allowlist.txt b/scripts/stubtest/allowlist.txt index e93a0e365..22a9d7c4c 100644 --- a/scripts/stubtest/allowlist.txt +++ b/scripts/stubtest/allowlist.txt @@ -6,11 +6,11 @@ # Comments about why things are ignored are mandatory. # Constructor arguments *appear* optional but actually throw exception -rest_framework.serializers.HyperlinkedIdentityField.__init__ -rest_framework.serializers.HyperlinkedRelatedField.__init__ -rest_framework.serializers.ManyRelatedField.__init__ -rest_framework.relations.SlugRelatedField.__init__ rest_framework.relations.HyperlinkedIdentityField.__init__ rest_framework.relations.HyperlinkedRelatedField.__init__ rest_framework.relations.ManyRelatedField.__init__ +rest_framework.relations.SlugRelatedField.__init__ +rest_framework.serializers.HyperlinkedIdentityField.__init__ +rest_framework.serializers.HyperlinkedRelatedField.__init__ +rest_framework.serializers.ManyRelatedField.__init__ rest_framework.serializers.SlugRelatedField.__init__