Earlier quoted context omitted.
Pyright doesn't work with Django, as Django's so dynamic that it requires a plugin to infer all types correctly. Sadly, even mypy with plugins is a mess to get set up in vscode, especially if you want it to use the same config as you use for ci checks from the command line. We use mypy + [django-stubs]( https://github.com/typeddjango/django-stubs ) (in a huge Django + drf project at day job) which includes a plugin f…
Did you manage to get MyPy + Django to work together usefully? I tried the plugin you mentioned but it still seemed stymied by the dynamic nature of Django (reverse relations, etc), so I gave up on it. If it’s actually possible to live in typed nirvana with Django I’ll bang my head against the wall some more. We’re using Django 3.2 btw + FactoryBoy. Actually there’s a number of annoyingly dynamic Python libraries out…
Here's our plugin setup in mypy.ini in case this helps (Django 4.2 + drf 3.14)
[mypy]
plugins =
mypy_django_plugin.main,
mypy_drf_plugin.main
ignore_missing_imports = True
follow_imports = silent
no_implicit_optional = True
warn_unused_ignores = True
check_untyped_defs = True
disallow_untyped_defs = True
disallow_untyped_calls = True
warn_unreachable = True
strict_equality = True
allow_redefinition = True
show_error_codes = True
mypy_path = stubs
[mypy.plugins.django-stubs]
django_settings_module = 'mycompany.settings'
with packages: mypy==1.6.0
django-stubs==4.2.4
django-stubs-ext==4.2.2
djangorestframework-stubs==3.14.3
with this horror of a regex in make (because you'll get drowned in wrong type errors in all of the untype files, and errors get shown from imports even if you don't care about that imported file), add more file targets as necessary: FILES_TO_MYPY = $(shell ls mycompany/\*/validators.py mycompany/\*/services.py mycompany/\*/selectors.py mycompany/\*/managers.py | sort | uniq)
# 1)We have to grep out django-manager-missing like this until the following bug
# is fixed: https://github.com/python/mypy/issues/12987.
# 2) We grep out the line of `Found 95 errors in 16 files (checked 83 source
# files)` that now appears as we use follow-imports: silent, because there's a
# bug where errors from imported modules are counted against the total even
# though they aren't emitted. If any real errors appear we get them as a
# separate line anyways.
.PHONY: mypy
mypy:
@{ MYPY_FORCE_COLOR=${NOT_CI} $(VENV)/bin/mypy --config-file mypy.ini $(FILES_TO_MYPY) 2>&3 | grep -v 'django-manager-missing\|errors in'; } 3>&1 | tee $(HYRE_TESTS_OUTPUT_PATH)/mypy.stdout.txt
This allows you to get proper errors for things like model = MyModel.objects.get()
othermodel = model.othermodel_set.first()
reveal_type(othermodel) # correctly revealed to note: Revealed type is "Union[mycompany.importpath.models.OtherModel None]"
and even errors on typos like model = MyModel.objects.get()
othermodel = model.ooooothermodel_set.first() # revealed as MyModel has no attribute ooooothermodel_set, perhaps you ment othermodel_set
.