microsoft / pyright

Static Type Checker for Python
Other
13.16k stars 1.41k forks source link

'Argument of type "bool" cannot be assigned to parameter "X"' when passing kwargs to Django model #7783

Closed shubb30 closed 5 months ago

shubb30 commented 5 months ago

Describe the bug When passing in **kwargs for a Django Model field, Pyright is throwing a bunch of errors for every parameter of django.db.models.fields.Field that is not included in the kwargs.

Code or Screenshots The below code works perfectly fine with Django.

from django.db import models

NULL_BLANK = {'null': True, 'blank': True}

class Thing(models.Model):

    char_field1 = models.CharField(max_length=50, null=True, blank=True)
    char_field2 = models.CharField(max_length=50, **NULL_BLANK)

Pyright throws the following errors:

pyright-testing/app/pyright_django_app/models.py
  pyright-testing/app/pyright_django_app/models.py:9:53 - error: Argument of type "bool" cannot be assigned to parameter "verbose_name" of type "_StrOrPromise | None" in function "__init__"
    Type "bool" is incompatible with type "_StrOrPromise | None"
      "bool" is incompatible with "str"
      "bool" is incompatible with "_StrPromise"
      "bool" is incompatible with "None" (reportArgumentType)

It also errors on the fields verbose_name, name, unique_for_date, unique_for_month, unique_for_year, choices, help_text, db_column, db_comment, db_tablespace, validators, error_messages, db_collation.

Fields coded as the first example work fine, but the second throws the errors.

Installed packages.

asgiref==3.8.1
Django==4.2.11
django-stubs==4.2.7
django-stubs-ext==4.2.7
nodeenv==1.8.0
pyright==1.1.360
ruff==0.3.7
setuptools==69.5.1
sqlparse==0.5.0
types-pytz==2024.1.0.20240417
types-PyYAML==6.0.12.20240311
typing_extensions==4.11.0
erictraut commented 5 months ago

Pyright's behavior here is correct. For char_field1, you're passing values to known keyword parameters. For char_field2, you're using a dictionary unpack operator on a dict[str, bool] which could contain any number of items that would violate type safety for the call.

If you want to package your parameters into a dict that can safely be used for passing arguments, you'd need to define a TypedDict like this:

class NullBlank(TypedDict):
    null: bool
    blank: bool

NULL_BLANK: NullBlank = {"null": True, "blank": True}

I'll note that mypy also generates the same error as pyright here.