Fix manager types scope (#991)

* Fix manager types scope

* Restore incremental mode and mention in developer docs

* Separate dev mypy config and regular one

* Document config files usage
This commit is contained in:
sterliakov
2022-06-14 21:30:13 +04:00
committed by GitHub
parent ae5b1a4edf
commit 32e13c37a6
8 changed files with 73 additions and 8 deletions

View File

@@ -46,7 +46,7 @@ jobs:
pip install -r ./requirements.txt
- name: Run tests
run: pytest
run: pytest --mypy-ini-file=mypy.ini
typecheck:
runs-on: ubuntu-latest

View File

@@ -70,6 +70,12 @@ To execute the unit tests, simply run:
pytest
```
If you get some unexpected results or want to be sure that tests run is not affected by previous one, remove `mypy` cache:
```bash
rm -r .mypy_cache
```
We also test the stubs against Django's own test suite. This is done in CI but you can also do this locally.
To execute the script run:

View File

@@ -1,3 +1,10 @@
# Regular configuration file (can be used as base in other projects, runs in CI)
# NOTE: this config file is not used by pytest locally.
# See comment in mypy.ini.dev for explanation.
# WARNING: when changing this file, consider doing the same with mypy.ini.dev
[mypy]
allow_redefinition = True
check_untyped_defs = True

30
mypy.ini.dev Normal file
View File

@@ -0,0 +1,30 @@
# Configuration file to use during development
# Changes of plugin code are not detected by mypy, so
# incremental mode can be harmful during development
# (mypy cache is not always invalidated).
# However, incremental mode has to be supported by django-stubs,
# thus another config (mypy.ini) is used in CI.
# Incremental mode is recommended for regular usage.
# WARNING: when changing this file, consider doing the same with mypy.ini
[mypy]
allow_redefinition = True
check_untyped_defs = True
ignore_missing_imports = True
# Avoid caching between test runs
incremental = False
strict_optional = True
show_traceback = True
warn_no_return = False
warn_unused_ignores = True
warn_redundant_casts = True
warn_unused_configs = True
warn_unreachable = True
plugins =
mypy_django_plugin.main
[mypy.plugins.django-stubs]
django_settings_module = scripts.django_tests_settings

View File

@@ -369,10 +369,9 @@ def bind_or_analyze_type(t: MypyType, api: SemanticAnalyzer, module_name: Option
That should hopefully give a bound type."""
if isinstance(t, UnboundType) and module_name is not None:
node = api.lookup_fully_qualified_or_none(module_name + "." + t.name)
if node is None:
return None
if node is not None and node.type is not None:
return node.type
else:
return api.anal_type(t)

View File

@@ -212,7 +212,11 @@ class AddManagers(ModelClassInitializer):
# replace self type with new class, if copying method
if isinstance(sym.node, FuncDef):
helpers.copy_method_to_another_class(
new_cls_def_context, self_type=custom_manager_type, new_method_name=name, method_node=sym.node
new_cls_def_context,
self_type=custom_manager_type,
new_method_name=name,
method_node=sym.node,
original_module_name=base_manager_info.module_name,
)
continue

View File

@@ -7,5 +7,5 @@ addopts =
-s
-v
--cache-clear
--mypy-ini-file=./mypy.ini
--mypy-ini-file=./mypy.ini.dev
--mypy-extension-hook=scripts.tests_extension_hook.django_plugin_hook

View File

@@ -423,3 +423,22 @@
class MyModel(models.Model):
objects = MyModelManager()
- case: regression_manager_scope_foreign
main: |
from myapp.models import MyModel
reveal_type(MyModel.on_site) # N: Revealed type is "myapp.models.MyModel_CurrentSiteManager[myapp.models.MyModel]"
installed_apps:
- myapp
- django.contrib.sites
files:
- path: myapp/__init__.py
- path: myapp/models.py
content: |
from django.db import models
from django.contrib.sites.models import Site
from django.contrib.sites.managers import CurrentSiteManager
class MyModel(models.Model):
site = models.ForeignKey(Site, on_delete=models.CASCADE)
on_site = CurrentSiteManager()