mirror of
https://github.com/davidhalter/django-stubs.git
synced 2025-12-08 13:04:47 +08:00
@@ -22,6 +22,7 @@ pip install django-stubs
|
||||
|
||||
| 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.1.0 | 0.720 | 2.2.x | ^3.6
|
||||
| 0.12.x | old semantic analyzer (<0.711), dmypy support | 2.1.x | ^3.6
|
||||
|
||||
Submodule django-sources updated: 612c2d166c...9a17ae50c6
@@ -1,9 +1,22 @@
|
||||
import decimal
|
||||
import warnings
|
||||
from contextlib import contextmanager
|
||||
from decimal import Decimal
|
||||
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.core.checks.registry import CheckRegistry
|
||||
@@ -86,7 +99,7 @@ class ignore_warnings(TestContextDecorator):
|
||||
ignore_kwargs: Dict[str, Any] = ...
|
||||
filter_func: Callable = ...
|
||||
def __init__(self, **kwargs: Any) -> None: ...
|
||||
catch_warnings: warnings.catch_warnings = ...
|
||||
catch_warnings: ContextManager[Optional[list]] = ...
|
||||
|
||||
requires_tz_support: Any
|
||||
|
||||
|
||||
@@ -367,7 +367,7 @@ class DjangoContext:
|
||||
lookup_type: MypyType = lookup_base.args[0]
|
||||
# if it's Field, consider lookup_type a __get__ of current field
|
||||
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__)
|
||||
if field_info is None:
|
||||
return AnyType(TypeOfAny.explicit)
|
||||
|
||||
@@ -179,10 +179,10 @@ def add_new_class_for_module(module: MypyFile, name: str, bases: List[Instance],
|
||||
|
||||
# make new class expression
|
||||
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
|
||||
new_typeinfo = TypeInfo(SymbolTable(), classdef, module.fullname())
|
||||
new_typeinfo = TypeInfo(SymbolTable(), classdef, module.fullname)
|
||||
new_typeinfo.bases = bases
|
||||
calculate_mro(new_typeinfo)
|
||||
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():
|
||||
var = Var(field_name, type=field_type)
|
||||
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)
|
||||
|
||||
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:
|
||||
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))
|
||||
|
||||
|
||||
@@ -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.info: type of the object variable is bound to
|
||||
var.info = info
|
||||
var._fullname = info.fullname() + '.' + name
|
||||
var._fullname = info.fullname + '.' + name
|
||||
var.is_initialized_in_class = True
|
||||
var.is_inferred = True
|
||||
info.names[name] = SymbolTableNode(MDEF, var,
|
||||
|
||||
@@ -121,17 +121,17 @@ class NewSemanalDjangoPlugin(Plugin):
|
||||
|
||||
def get_additional_deps(self, file: MypyFile) -> List[Tuple[int, str, int]]:
|
||||
# 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)]
|
||||
|
||||
# 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')]
|
||||
|
||||
# for `get_user_model()`
|
||||
if self.django_context.settings:
|
||||
if (file.fullname() == 'django.contrib.auth'
|
||||
or file.fullname() in {'django.http', 'django.http.request'}):
|
||||
if (file.fullname == 'django.contrib.auth'
|
||||
or file.fullname in {'django.http', 'django.http.request'}):
|
||||
auth_user_model_name = self.django_context.settings.AUTH_USER_MODEL
|
||||
try:
|
||||
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)]
|
||||
|
||||
# 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:
|
||||
return []
|
||||
deps = set()
|
||||
@@ -153,13 +153,13 @@ class NewSemanalDjangoPlugin(Plugin):
|
||||
if related_model_cls is None:
|
||||
continue
|
||||
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))
|
||||
# reverse relations
|
||||
for relation in model_class._meta.related_objects:
|
||||
related_model_cls = self.django_context.get_field_related_model_cls(relation)
|
||||
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))
|
||||
return list(deps)
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ def _get_current_field_from_assignment(ctx: FunctionContext, django_context: Dja
|
||||
if field_name is 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:
|
||||
return None
|
||||
|
||||
|
||||
@@ -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:
|
||||
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)
|
||||
if model_cls is None:
|
||||
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
|
||||
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)
|
||||
if model_cls is None:
|
||||
return ctx.default_return_type
|
||||
|
||||
@@ -28,7 +28,7 @@ def return_proper_field_type_from_get_field(ctx: MethodContext, django_context:
|
||||
if not isinstance(model_type, Instance):
|
||||
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:
|
||||
return ctx.default_return_type
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from collections import OrderedDict
|
||||
from typing import Type
|
||||
from typing import List, Tuple, Type
|
||||
|
||||
from django.db.models.base import Model
|
||||
from django.db.models.fields import DateField, DateTimeField
|
||||
@@ -7,10 +7,11 @@ from django.db.models.fields.related import ForeignKey
|
||||
from django.db.models.fields.reverse_related import (
|
||||
ManyToManyRel, ManyToOneRel, OneToOneRel,
|
||||
)
|
||||
from mypy.nodes import ARG_STAR2, Argument, Context, TypeInfo, Var
|
||||
from mypy.nodes import ARG_STAR2, Argument, Context, FuncDef, TypeInfo, Var
|
||||
from mypy.plugin import ClassDefContext
|
||||
from mypy.plugins import common
|
||||
from mypy.types import AnyType, Instance
|
||||
from mypy.plugins.common import add_method
|
||||
from mypy.types import AnyType, CallableType, Instance
|
||||
from mypy.types import Type as MypyType
|
||||
from mypy.types import TypeOfAny
|
||||
|
||||
@@ -43,7 +44,7 @@ class ModelClassInitializer:
|
||||
var = Var(name=name, type=typ)
|
||||
# var.info: type of the object variable is bound to
|
||||
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_inferred = True
|
||||
return var
|
||||
@@ -126,7 +127,7 @@ class AddRelatedModelsId(ModelClassInitializer):
|
||||
|
||||
class AddManagers(ModelClassInitializer):
|
||||
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:
|
||||
for manager_name, manager in model_cls._meta.managers_map.items():
|
||||
@@ -158,18 +159,46 @@ class AddManagers(ModelClassInitializer):
|
||||
bases=bases,
|
||||
fields=OrderedDict())
|
||||
# copy fields to a new manager
|
||||
new_cls_def_context = ClassDefContext(cls=custom_manager_info.defn,
|
||||
reason=self.ctx.reason,
|
||||
api=self.api)
|
||||
custom_manager_type = Instance(custom_manager_info, [Instance(self.model_classdef.info, [])])
|
||||
|
||||
for name, sym in manager_info.names.items():
|
||||
# replace self type with new class, if copying method
|
||||
if isinstance(sym.node, FuncDef):
|
||||
arguments, return_type = self.prepare_new_method_arguments(sym.node)
|
||||
add_method(new_cls_def_context,
|
||||
name,
|
||||
args=arguments,
|
||||
return_type=return_type,
|
||||
self_type=custom_manager_type)
|
||||
continue
|
||||
|
||||
new_sym = sym.copy()
|
||||
if isinstance(new_sym.node, Var):
|
||||
new_var = Var(name, type=sym.type)
|
||||
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
|
||||
custom_manager_info.names[name] = new_sym
|
||||
|
||||
custom_manager_type = Instance(custom_manager_info, [Instance(self.model_classdef.info, [])])
|
||||
self.add_new_node_to_model_class(manager_name, custom_manager_type)
|
||||
|
||||
def prepare_new_method_arguments(self, node: FuncDef) -> Tuple[List[Argument], MypyType]:
|
||||
arguments = []
|
||||
for argument in node.arguments[1:]:
|
||||
if argument.type_annotation is None:
|
||||
argument.type_annotation = AnyType(TypeOfAny.unannotated)
|
||||
arguments.append(argument)
|
||||
|
||||
if isinstance(node.type, CallableType):
|
||||
return_type = node.type.ret_type
|
||||
else:
|
||||
return_type = AnyType(TypeOfAny.unannotated)
|
||||
|
||||
return arguments, return_type
|
||||
|
||||
|
||||
class AddDefaultManagerAttribute(ModelClassInitializer):
|
||||
def run_with_model_cls(self, model_cls: Type[Model]) -> None:
|
||||
|
||||
@@ -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):
|
||||
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)
|
||||
if model_cls is None:
|
||||
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:
|
||||
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
|
||||
return AnyType(TypeOfAny.explicit)
|
||||
|
||||
|
||||
@@ -117,7 +117,7 @@ def extract_proper_type_queryset_values_list(ctx: MethodContext, django_context:
|
||||
if model_type is None:
|
||||
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:
|
||||
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:
|
||||
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:
|
||||
return ctx.default_return_type
|
||||
|
||||
|
||||
@@ -37,7 +37,6 @@ IGNORED_ERRORS = {
|
||||
'Argument after ** must be a mapping',
|
||||
'note:',
|
||||
re.compile(r'Item "None" of "[a-zA-Z_ ,\[\]]+" has no attribute'),
|
||||
'"Optional[List[_Record]]"',
|
||||
'"Callable[..., None]" has no attribute',
|
||||
'does not return a value',
|
||||
'has no attribute "alternatives"',
|
||||
@@ -257,7 +256,6 @@ IGNORED_ERRORS = {
|
||||
'Item "Field[Any, Any]" of "Union[Field[Any, Any], ForeignObjectRel]" has no attribute',
|
||||
'Incompatible types in assignment (expression has type "Type[Person',
|
||||
'Incompatible types in assignment (expression has type "FloatModel", variable has type',
|
||||
'No overload variant of "bytes" matches argument type "Container[int]"',
|
||||
'"ImageFile" has no attribute "was_opened"',
|
||||
],
|
||||
'model_indexes': [
|
||||
@@ -278,6 +276,7 @@ IGNORED_ERRORS = {
|
||||
'Incompatible type for "department" of "Worker"',
|
||||
'"PickledModel" has no attribute',
|
||||
'"Department" has no attribute "evaluate"',
|
||||
'Unsupported target for indexed assignment',
|
||||
],
|
||||
'model_formsets_regress': [
|
||||
'Incompatible types in assignment (expression has type "int", target has type "str")',
|
||||
@@ -352,7 +351,8 @@ IGNORED_ERRORS = {
|
||||
'Argument 1 to "append" of "list" has incompatible type "Tuple[Any, Any, Optional[Any], Any]";'
|
||||
],
|
||||
'sites_framework': [
|
||||
'expression has type "CurrentSiteManager[CustomArticle]", base class "AbstractArticle"'
|
||||
'expression has type "CurrentSiteManager[CustomArticle]", base class "AbstractArticle"',
|
||||
"Name 'Optional' is not defined",
|
||||
],
|
||||
'syndication_tests': [
|
||||
'List or tuple expected as variable arguments'
|
||||
|
||||
2
setup.py
2
setup.py
@@ -21,7 +21,7 @@ with open('README.md', 'r') as f:
|
||||
readme = f.read()
|
||||
|
||||
dependencies = [
|
||||
'mypy>=0.740,<0.750',
|
||||
'mypy>=0.750,<0.760',
|
||||
'typing-extensions',
|
||||
'django',
|
||||
]
|
||||
|
||||
@@ -307,9 +307,18 @@
|
||||
- case: custom_manager_returns_proper_model_types
|
||||
main: |
|
||||
from myapp.models import User
|
||||
reveal_type(User.objects.get()) # N: Revealed type is 'myapp.models.User*'
|
||||
reveal_type(User.objects) # N: Revealed type is 'myapp.models.User_MyManager[myapp.models.User]'
|
||||
reveal_type(User.objects.select_related()) # N: Revealed type is 'myapp.models.User_MyManager[myapp.models.User]'
|
||||
reveal_type(User.objects.get()) # N: Revealed type is 'myapp.models.User*'
|
||||
reveal_type(User.objects.get_instance()) # N: Revealed type is 'builtins.int'
|
||||
reveal_type(User.objects.get_instance_untyped('hello')) # N: Revealed type is 'Any'
|
||||
|
||||
from myapp.models import ChildUser
|
||||
reveal_type(ChildUser.objects) # N: Revealed type is 'myapp.models.ChildUser_MyManager[myapp.models.ChildUser]'
|
||||
reveal_type(ChildUser.objects.select_related()) # N: Revealed type is 'myapp.models.ChildUser_MyManager[myapp.models.ChildUser]'
|
||||
reveal_type(ChildUser.objects.get()) # N: Revealed type is 'myapp.models.ChildUser*'
|
||||
reveal_type(ChildUser.objects.get_instance()) # N: Revealed type is 'builtins.int'
|
||||
reveal_type(ChildUser.objects.get_instance_untyped('hello')) # N: Revealed type is 'Any'
|
||||
installed_apps:
|
||||
- myapp
|
||||
files:
|
||||
@@ -320,5 +329,9 @@
|
||||
class MyManager(models.Manager):
|
||||
def get_instance(self) -> int:
|
||||
pass
|
||||
def get_instance_untyped(self, name):
|
||||
pass
|
||||
class User(models.Model):
|
||||
objects = MyManager()
|
||||
class ChildUser(models.Model):
|
||||
objects = MyManager()
|
||||
|
||||
Reference in New Issue
Block a user