Skip to content

Fix error with null=True and Any type param for Field types #2492

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jan 23, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions mypy_django_plugin/transformers/fields.py
Original file line number Diff line number Diff line change
@@ -166,11 +166,11 @@ def set_descriptor_types_for_field(
if not (isinstance(mapped_set_type, UninhabitedType) or isinstance(mapped_get_type, UninhabitedType)):
# always replace set_type and get_type with (non-Any) mapped types
set_type = helpers.convert_any_to_type(mapped_set_type, set_type)
get_type = helpers.convert_any_to_type(mapped_get_type, get_type)
get_type = get_proper_type(helpers.convert_any_to_type(mapped_get_type, get_type))

# the get_type must be optional if the field is nullable
if (is_get_nullable or is_nullable) and not (
isinstance(get_proper_type(get_type), NoneType) or helpers.is_optional(get_type)
isinstance(get_type, NoneType) or helpers.is_optional(get_type) or isinstance(get_type, AnyType)
):
ctx.api.fail(
f"{default_return_type.type.name} is nullable but its generic get type parameter is not optional",
30 changes: 19 additions & 11 deletions tests/typecheck/fields/test_custom_fields.yml
Original file line number Diff line number Diff line change
@@ -18,13 +18,12 @@
reveal_type(user.my_custom_field13) # N: Revealed type is "Union[myapp.models.CustomFieldValue, None]"
reveal_type(user.my_custom_field14) # N: Revealed type is "Union[builtins.bool, None]"
reveal_type(user.my_custom_field15) # N: Revealed type is "None"

reveal_type(user.my_custom_field_any1) # N: Revealed type is "Any"
reveal_type(user.my_custom_field_any2) # N: Revealed type is "Any"
reveal_type(user.my_custom_field_any3) # N: Revealed type is "Any"
reveal_type(user.my_custom_field_any4) # N: Revealed type is "Any"
monkeypatch: true
out: |
myapp/models:31: error: GenericField is nullable but its generic get type parameter is not optional [misc]
myapp/models:32: error: CustomValueField is nullable but its generic get type parameter is not optional [misc]
myapp/models:33: error: SingleTypeField is nullable but its generic get type parameter is not optional [misc]
myapp/models:34: error: AdditionalTypeVarField is nullable but its generic get type parameter is not optional [misc]
myapp/models:35: error: Field is nullable but its generic get type parameter is not optional [misc]
installed_apps:
- myapp
files:
@@ -53,19 +52,24 @@

class CustomSmallIntegerField(fields.SmallIntegerField[_ST, _GT]): ...

class FieldImplicitAny(fields.Field): ...
class FieldExplicitAny(fields.Field[Any, Any]): ...

class User(models.Model):
id = models.AutoField(primary_key=True)
my_custom_field1 = GenericField[Union[CustomFieldValue, int], CustomFieldValue]()
my_custom_field2 = CustomValueField()
my_custom_field3 = SingleTypeField[bool]()
my_custom_field4 = AdditionalTypeVarField[Union[CustomFieldValue, int], CustomFieldValue, bool]()
my_custom_field_any1 = FieldImplicitAny()
my_custom_field_any2 = FieldExplicitAny()

# test null=True on fields with non-optional generic types throw error
my_custom_field5 = GenericField[Union[CustomFieldValue, int], CustomFieldValue](null=True)
my_custom_field6 = CustomValueField(null=True)
my_custom_field7 = SingleTypeField[bool](null=True)
my_custom_field8 = AdditionalTypeVarField[Union[CustomFieldValue, int], CustomFieldValue, bool](null=True)
my_custom_field9 = fields.Field[Union[CustomFieldValue, int], CustomFieldValue](null=True)
my_custom_field5 = GenericField[Union[CustomFieldValue, int], CustomFieldValue](null=True) # E: GenericField is nullable but its generic get type parameter is not optional [misc]
my_custom_field6 = CustomValueField(null=True) # E: CustomValueField is nullable but its generic get type parameter is not optional [misc]
my_custom_field7 = SingleTypeField[bool](null=True) # E: SingleTypeField is nullable but its generic get type parameter is not optional [misc]
my_custom_field8 = AdditionalTypeVarField[Union[CustomFieldValue, int], CustomFieldValue, bool](null=True) # E: AdditionalTypeVarField is nullable but its generic get type parameter is not optional [misc]
my_custom_field9 = fields.Field[Union[CustomFieldValue, int], CustomFieldValue](null=True) # E: Field is nullable but its generic get type parameter is not optional [misc]

# test overriding fields that set _pyi_private_set_type or _pyi_private_get_type
my_custom_field10 = fields.SmallIntegerField[bool, bool]()
@@ -76,3 +80,7 @@
my_custom_field13 = GenericField[Union[CustomFieldValue, int], Union[CustomFieldValue, None]](null=True)
my_custom_field14 = SingleTypeField[Union[bool, None]](null=True)
my_custom_field15 = fields.Field[None, None](null=True)

# test null=True on Any does not raise
my_custom_field_any3 = FieldImplicitAny(null=True)
my_custom_field_any4 = FieldExplicitAny(null=True)