Support mypy 0.750

This commit is contained in:
Konstantin Alekseev
2019-11-30 13:39:28 +03:00
parent df4c17a947
commit cbc7159995
13 changed files with 43 additions and 29 deletions

View File

@@ -22,6 +22,7 @@ pip install django-stubs
| django-stubs | mypy version | django version | python version | django-stubs | mypy version | django version | python version
| ------------ | ---- | ---- | ---- | | ------------ | ---- | ---- | ---- |
| 1.3.0 | 0.750 | 2.2.x | ^3.6
| 1.2.0 | 0.730 | 2.2.x | ^3.6 | 1.2.0 | 0.730 | 2.2.x | ^3.6
| 1.1.0 | 0.720 | 2.2.x | ^3.6 | 1.1.0 | 0.720 | 2.2.x | ^3.6
| 0.12.x | old semantic analyzer (<0.711), dmypy support | 2.1.x | ^3.6 | 0.12.x | old semantic analyzer (<0.711), dmypy support | 2.1.x | ^3.6

View File

@@ -1,9 +1,22 @@
import decimal import decimal
import warnings
from contextlib import contextmanager from contextlib import contextmanager
from decimal import Decimal from decimal import Decimal
from io import StringIO from io import StringIO
from typing import Any, Callable, Dict, Iterable, Iterator, List, Mapping, Optional, Set, Tuple, Type, Union from typing import (
Any,
Callable,
Dict,
Iterable,
Iterator,
List,
Mapping,
Optional,
Set,
Tuple,
Type,
Union,
ContextManager,
)
from django.apps.registry import Apps from django.apps.registry import Apps
from django.core.checks.registry import CheckRegistry from django.core.checks.registry import CheckRegistry
@@ -86,7 +99,7 @@ class ignore_warnings(TestContextDecorator):
ignore_kwargs: Dict[str, Any] = ... ignore_kwargs: Dict[str, Any] = ...
filter_func: Callable = ... filter_func: Callable = ...
def __init__(self, **kwargs: Any) -> None: ... def __init__(self, **kwargs: Any) -> None: ...
catch_warnings: warnings.catch_warnings = ... catch_warnings: ContextManager[Optional[list]] = ...
requires_tz_support: Any requires_tz_support: Any

View File

@@ -367,7 +367,7 @@ class DjangoContext:
lookup_type: MypyType = lookup_base.args[0] lookup_type: MypyType = lookup_base.args[0]
# if it's Field, consider lookup_type a __get__ of current field # if it's Field, consider lookup_type a __get__ of current field
if (isinstance(lookup_type, Instance) if (isinstance(lookup_type, Instance)
and lookup_type.type.fullname() == fullnames.FIELD_FULLNAME): and lookup_type.type.fullname == fullnames.FIELD_FULLNAME):
field_info = helpers.lookup_class_typeinfo(helpers.get_typechecker_api(ctx), field.__class__) field_info = helpers.lookup_class_typeinfo(helpers.get_typechecker_api(ctx), field.__class__)
if field_info is None: if field_info is None:
return AnyType(TypeOfAny.explicit) return AnyType(TypeOfAny.explicit)

View File

@@ -179,10 +179,10 @@ def add_new_class_for_module(module: MypyFile, name: str, bases: List[Instance],
# make new class expression # make new class expression
classdef = ClassDef(new_class_unique_name, Block([])) classdef = ClassDef(new_class_unique_name, Block([]))
classdef.fullname = module.fullname() + '.' + new_class_unique_name classdef.fullname = module.fullname + '.' + new_class_unique_name
# make new TypeInfo # make new TypeInfo
new_typeinfo = TypeInfo(SymbolTable(), classdef, module.fullname()) new_typeinfo = TypeInfo(SymbolTable(), classdef, module.fullname)
new_typeinfo.bases = bases new_typeinfo.bases = bases
calculate_mro(new_typeinfo) calculate_mro(new_typeinfo)
new_typeinfo.calculate_metaclass_type() new_typeinfo.calculate_metaclass_type()
@@ -191,7 +191,7 @@ def add_new_class_for_module(module: MypyFile, name: str, bases: List[Instance],
for field_name, field_type in fields.items(): for field_name, field_type in fields.items():
var = Var(field_name, type=field_type) var = Var(field_name, type=field_type)
var.info = new_typeinfo var.info = new_typeinfo
var._fullname = new_typeinfo.fullname() + '.' + field_name var._fullname = new_typeinfo.fullname + '.' + field_name
new_typeinfo.names[field_name] = SymbolTableNode(MDEF, var, plugin_generated=True) new_typeinfo.names[field_name] = SymbolTableNode(MDEF, var, plugin_generated=True)
classdef.info = new_typeinfo classdef.info = new_typeinfo
@@ -276,7 +276,7 @@ def get_typechecker_api(ctx: Union[AttributeContext, MethodContext, FunctionCont
def is_model_subclass_info(info: TypeInfo, django_context: 'DjangoContext') -> bool: def is_model_subclass_info(info: TypeInfo, django_context: 'DjangoContext') -> bool:
return (info.fullname() in django_context.all_registered_model_class_fullnames return (info.fullname in django_context.all_registered_model_class_fullnames
or info.has_base(fullnames.MODEL_CLASS_FULLNAME)) or info.has_base(fullnames.MODEL_CLASS_FULLNAME))
@@ -293,7 +293,7 @@ def add_new_sym_for_info(info: TypeInfo, *, name: str, sym_type: MypyType) -> No
var = Var(name=name, type=sym_type) var = Var(name=name, type=sym_type)
# var.info: type of the object variable is bound to # var.info: type of the object variable is bound to
var.info = info var.info = info
var._fullname = info.fullname() + '.' + name var._fullname = info.fullname + '.' + name
var.is_initialized_in_class = True var.is_initialized_in_class = True
var.is_inferred = True var.is_inferred = True
info.names[name] = SymbolTableNode(MDEF, var, info.names[name] = SymbolTableNode(MDEF, var,

View File

@@ -121,17 +121,17 @@ class NewSemanalDjangoPlugin(Plugin):
def get_additional_deps(self, file: MypyFile) -> List[Tuple[int, str, int]]: def get_additional_deps(self, file: MypyFile) -> List[Tuple[int, str, int]]:
# for settings # for settings
if file.fullname() == 'django.conf' and self.django_context.django_settings_module: if file.fullname == 'django.conf' and self.django_context.django_settings_module:
return [self._new_dependency(self.django_context.django_settings_module)] return [self._new_dependency(self.django_context.django_settings_module)]
# for values / values_list # for values / values_list
if file.fullname() == 'django.db.models': if file.fullname == 'django.db.models':
return [self._new_dependency('mypy_extensions'), self._new_dependency('typing')] return [self._new_dependency('mypy_extensions'), self._new_dependency('typing')]
# for `get_user_model()` # for `get_user_model()`
if self.django_context.settings: if self.django_context.settings:
if (file.fullname() == 'django.contrib.auth' if (file.fullname == 'django.contrib.auth'
or file.fullname() in {'django.http', 'django.http.request'}): or file.fullname in {'django.http', 'django.http.request'}):
auth_user_model_name = self.django_context.settings.AUTH_USER_MODEL auth_user_model_name = self.django_context.settings.AUTH_USER_MODEL
try: try:
auth_user_module = self.django_context.apps_registry.get_model(auth_user_model_name).__module__ auth_user_module = self.django_context.apps_registry.get_model(auth_user_model_name).__module__
@@ -141,7 +141,7 @@ class NewSemanalDjangoPlugin(Plugin):
return [self._new_dependency(auth_user_module)] return [self._new_dependency(auth_user_module)]
# ensure that all mentioned to='someapp.SomeModel' are loaded with corresponding related Fields # ensure that all mentioned to='someapp.SomeModel' are loaded with corresponding related Fields
defined_model_classes = self.django_context.model_modules.get(file.fullname()) defined_model_classes = self.django_context.model_modules.get(file.fullname)
if not defined_model_classes: if not defined_model_classes:
return [] return []
deps = set() deps = set()
@@ -153,13 +153,13 @@ class NewSemanalDjangoPlugin(Plugin):
if related_model_cls is None: if related_model_cls is None:
continue continue
related_model_module = related_model_cls.__module__ related_model_module = related_model_cls.__module__
if related_model_module != file.fullname(): if related_model_module != file.fullname:
deps.add(self._new_dependency(related_model_module)) deps.add(self._new_dependency(related_model_module))
# reverse relations # reverse relations
for relation in model_class._meta.related_objects: for relation in model_class._meta.related_objects:
related_model_cls = self.django_context.get_field_related_model_cls(relation) related_model_cls = self.django_context.get_field_related_model_cls(relation)
related_model_module = related_model_cls.__module__ related_model_module = related_model_cls.__module__
if related_model_module != file.fullname(): if related_model_module != file.fullname:
deps.add(self._new_dependency(related_model_module)) deps.add(self._new_dependency(related_model_module))
return list(deps) return list(deps)

View File

@@ -29,7 +29,7 @@ def _get_current_field_from_assignment(ctx: FunctionContext, django_context: Dja
if field_name is None: if field_name is None:
return None return None
model_cls = django_context.get_model_class_by_fullname(outer_model_info.fullname()) model_cls = django_context.get_model_class_by_fullname(outer_model_info.fullname)
if model_cls is None: if model_cls is None:
return None return None

View File

@@ -54,7 +54,7 @@ def typecheck_model_method(ctx: Union[FunctionContext, MethodContext], django_co
def redefine_and_typecheck_model_init(ctx: FunctionContext, django_context: DjangoContext) -> MypyType: def redefine_and_typecheck_model_init(ctx: FunctionContext, django_context: DjangoContext) -> MypyType:
assert isinstance(ctx.default_return_type, Instance) assert isinstance(ctx.default_return_type, Instance)
model_fullname = ctx.default_return_type.type.fullname() model_fullname = ctx.default_return_type.type.fullname
model_cls = django_context.get_model_class_by_fullname(model_fullname) model_cls = django_context.get_model_class_by_fullname(model_fullname)
if model_cls is None: if model_cls is None:
return ctx.default_return_type return ctx.default_return_type
@@ -67,7 +67,7 @@ def redefine_and_typecheck_model_create(ctx: MethodContext, django_context: Djan
# only work with ctx.default_return_type = model Instance # only work with ctx.default_return_type = model Instance
return ctx.default_return_type return ctx.default_return_type
model_fullname = ctx.default_return_type.type.fullname() model_fullname = ctx.default_return_type.type.fullname
model_cls = django_context.get_model_class_by_fullname(model_fullname) model_cls = django_context.get_model_class_by_fullname(model_fullname)
if model_cls is None: if model_cls is None:
return ctx.default_return_type return ctx.default_return_type

View File

@@ -28,7 +28,7 @@ def return_proper_field_type_from_get_field(ctx: MethodContext, django_context:
if not isinstance(model_type, Instance): if not isinstance(model_type, Instance):
return ctx.default_return_type return ctx.default_return_type
model_cls = django_context.get_model_class_by_fullname(model_type.type.fullname()) model_cls = django_context.get_model_class_by_fullname(model_type.type.fullname)
if model_cls is None: if model_cls is None:
return ctx.default_return_type return ctx.default_return_type

View File

@@ -43,7 +43,7 @@ class ModelClassInitializer:
var = Var(name=name, type=typ) var = Var(name=name, type=typ)
# var.info: type of the object variable is bound to # var.info: type of the object variable is bound to
var.info = self.model_classdef.info var.info = self.model_classdef.info
var._fullname = self.model_classdef.info.fullname() + '.' + name var._fullname = self.model_classdef.info.fullname + '.' + name
var.is_initialized_in_class = True var.is_initialized_in_class = True
var.is_inferred = True var.is_inferred = True
return var return var
@@ -126,7 +126,7 @@ class AddRelatedModelsId(ModelClassInitializer):
class AddManagers(ModelClassInitializer): class AddManagers(ModelClassInitializer):
def _is_manager_any(self, typ: Instance) -> bool: def _is_manager_any(self, typ: Instance) -> bool:
return typ.type.fullname() == fullnames.MANAGER_CLASS_FULLNAME and type(typ.args[0]) == AnyType return typ.type.fullname == fullnames.MANAGER_CLASS_FULLNAME and type(typ.args[0]) == AnyType
def run_with_model_cls(self, model_cls: Type[Model]) -> None: def run_with_model_cls(self, model_cls: Type[Model]) -> None:
for manager_name, manager in model_cls._meta.managers_map.items(): for manager_name, manager in model_cls._meta.managers_map.items():
@@ -163,7 +163,7 @@ class AddManagers(ModelClassInitializer):
if isinstance(new_sym.node, Var): if isinstance(new_sym.node, Var):
new_var = Var(name, type=sym.type) new_var = Var(name, type=sym.type)
new_var.info = custom_manager_info new_var.info = custom_manager_info
new_var._fullname = custom_manager_info.fullname() + '.' + name new_var._fullname = custom_manager_info.fullname + '.' + name
new_sym.node = new_var new_sym.node = new_var
custom_manager_info.names[name] = new_sym custom_manager_info.names[name] = new_sym

View File

@@ -16,7 +16,7 @@ def typecheck_queryset_filter(ctx: MethodContext, django_context: DjangoContext)
if not ctx.type.args or not isinstance(ctx.type.args[0], Instance): if not ctx.type.args or not isinstance(ctx.type.args[0], Instance):
return ctx.default_return_type return ctx.default_return_type
model_cls_fullname = ctx.type.args[0].type.fullname() model_cls_fullname = ctx.type.args[0].type.fullname
model_cls = django_context.get_model_class_by_fullname(model_cls_fullname) model_cls = django_context.get_model_class_by_fullname(model_cls_fullname)
if model_cls is None: if model_cls is None:
return ctx.default_return_type return ctx.default_return_type
@@ -44,7 +44,7 @@ def typecheck_queryset_filter(ctx: MethodContext, django_context: DjangoContext)
def resolve_combinable_type(combinable_type: Instance, django_context: DjangoContext) -> MypyType: def resolve_combinable_type(combinable_type: Instance, django_context: DjangoContext) -> MypyType:
if combinable_type.type.fullname() != fullnames.F_EXPRESSION_FULLNAME: if combinable_type.type.fullname != fullnames.F_EXPRESSION_FULLNAME:
# Combinables aside from F expressions are unsupported # Combinables aside from F expressions are unsupported
return AnyType(TypeOfAny.explicit) return AnyType(TypeOfAny.explicit)

View File

@@ -117,7 +117,7 @@ def extract_proper_type_queryset_values_list(ctx: MethodContext, django_context:
if model_type is None: if model_type is None:
return AnyType(TypeOfAny.from_omitted_generics) return AnyType(TypeOfAny.from_omitted_generics)
model_cls = django_context.get_model_class_by_fullname(model_type.type.fullname()) model_cls = django_context.get_model_class_by_fullname(model_type.type.fullname)
if model_cls is None: if model_cls is None:
return ctx.default_return_type return ctx.default_return_type
@@ -166,7 +166,7 @@ def extract_proper_type_queryset_values(ctx: MethodContext, django_context: Djan
if model_type is None: if model_type is None:
return AnyType(TypeOfAny.from_omitted_generics) return AnyType(TypeOfAny.from_omitted_generics)
model_cls = django_context.get_model_class_by_fullname(model_type.type.fullname()) model_cls = django_context.get_model_class_by_fullname(model_type.type.fullname)
if model_cls is None: if model_cls is None:
return ctx.default_return_type return ctx.default_return_type

View File

@@ -21,7 +21,7 @@ with open('README.md', 'r') as f:
readme = f.read() readme = f.read()
dependencies = [ dependencies = [
'mypy>=0.740,<0.750', 'mypy>=0.750,<0.760',
'typing-extensions', 'typing-extensions',
'django', 'django',
] ]

View File

@@ -318,7 +318,7 @@
content: | content: |
from django.db import models from django.db import models
class MyManager(models.Manager): class MyManager(models.Manager):
def get_instance(self) -> int: def get_instance(self: "models.Manager[User]") -> int:
pass pass
class User(models.Model): class User(models.Model):
objects = MyManager() objects = MyManager()