diff --git a/mypy_django_plugin/helpers.py b/mypy_django_plugin/helpers.py index 1ec3e31..d4b06b8 100644 --- a/mypy_django_plugin/helpers.py +++ b/mypy_django_plugin/helpers.py @@ -2,7 +2,6 @@ import typing from collections import OrderedDict from typing import Dict, Optional, cast -from mypy.checker import TypeChecker, gen_unique_name from mypy.mro import calculate_mro from mypy.nodes import ( AssignmentStmt, ClassDef, Expression, ImportedName, Lvalue, MypyFile, NameExpr, SymbolNode, TypeInfo, @@ -12,6 +11,9 @@ from mypy.types import ( AnyType, Instance, NoneTyp, Type, TypeOfAny, TypeVarType, UnionType, TupleType, TypedDictType) +if typing.TYPE_CHECKING: + from mypy.checker import TypeChecker + MODEL_CLASS_FULLNAME = 'django.db.models.base.Model' FIELD_FULLNAME = 'django.db.models.fields.Field' CHAR_FIELD_FULLNAME = 'django.db.models.fields.CharField' @@ -165,7 +167,7 @@ def get_argument_type_by_name(ctx: typing.Union[FunctionContext, MethodContext], return arg_types[0] -def get_setting_expr(api: TypeChecker, setting_name: str) -> Optional[Expression]: +def get_setting_expr(api: 'TypeChecker', setting_name: str) -> Optional[Expression]: try: settings_sym = api.modules['django.conf'].names['settings'] except KeyError: @@ -314,11 +316,12 @@ def is_foreign_key(t: Type) -> bool: return has_any_of_bases(t.type, (FOREIGN_KEY_FULLNAME, ONETOONE_FIELD_FULLNAME)) -def build_class_with_annotated_fields(api: TypeChecker, base: Type, fields: 'OrderedDict[str, Type]', +def build_class_with_annotated_fields(api: 'TypeChecker', base: Type, fields: 'OrderedDict[str, Type]', name: str) -> Instance: """Build an Instance with `name` that contains the specified `fields` as attributes and extends `base`.""" # Credit: This code is largely copied/modified from TypeChecker.intersect_instance_callable and # NamedTupleAnalyzer.build_namedtuple_typeinfo + from mypy.checker import gen_unique_name cur_module = cast(MypyFile, api.scope.stack[0]) gen_name = gen_unique_name(name, cur_module.names) @@ -348,7 +351,7 @@ def build_class_with_annotated_fields(api: TypeChecker, base: Type, fields: 'Ord return Instance(info, []) -def make_named_tuple(api: TypeChecker, fields: 'OrderedDict[str, Type]', name: str) -> Type: +def make_named_tuple(api: 'TypeChecker', fields: 'OrderedDict[str, Type]', name: str) -> Type: if not fields: # No fields specified, so fallback to a subclass of NamedTuple that allows # __getattr__ / __setattr__ for any attribute name. @@ -363,13 +366,13 @@ def make_named_tuple(api: TypeChecker, fields: 'OrderedDict[str, Type]', name: s return TupleType(list(fields.values()), fallback=fallback) -def make_typeddict(api: TypeChecker, fields: 'OrderedDict[str, Type]', required_keys: typing.Set[str]) -> Type: +def make_typeddict(api: 'TypeChecker', fields: 'OrderedDict[str, Type]', required_keys: typing.Set[str]) -> Type: object_type = api.named_generic_type('mypy_extensions._TypedDict', []) typed_dict_type = TypedDictType(fields, required_keys=required_keys, fallback=object_type) return typed_dict_type -def make_tuple(api: TypeChecker, fields: typing.List[Type]) -> Type: +def make_tuple(api: 'TypeChecker', fields: typing.List[Type]) -> Type: implicit_any = AnyType(TypeOfAny.special_form) fallback = api.named_generic_type('builtins.tuple', [implicit_any]) return TupleType(fields, fallback=fallback) diff --git a/mypy_django_plugin/main.py b/mypy_django_plugin/main.py index f004dfa..00e5d8e 100644 --- a/mypy_django_plugin/main.py +++ b/mypy_django_plugin/main.py @@ -2,7 +2,6 @@ import os from functools import partial from typing import Callable, Dict, Optional, Union, cast -from mypy.checker import TypeChecker from mypy.nodes import MemberExpr, NameExpr, TypeInfo from mypy.options import Options from mypy.plugin import ( @@ -55,6 +54,8 @@ def transform_form_class(ctx: ClassDefContext) -> None: def determine_proper_manager_type(ctx: FunctionContext) -> Type: + from mypy.checker import TypeChecker + api = cast(TypeChecker, ctx.api) ret = ctx.default_return_type if not api.tscope.classes: @@ -104,6 +105,8 @@ def set_first_generic_param_as_default_for_second(fullname: str, ctx: AnalyzeTyp def return_user_model_hook(ctx: FunctionContext) -> Type: + from mypy.checker import TypeChecker + api = cast(TypeChecker, ctx.api) setting_expr = helpers.get_setting_expr(api, 'AUTH_USER_MODEL') if setting_expr is None: @@ -183,6 +186,8 @@ class ExtractSettingType: self.module_fullname = module_fullname def __call__(self, ctx: AttributeContext) -> Type: + from mypy.checker import TypeChecker + api = cast(TypeChecker, ctx.api) original_module = api.modules.get(self.module_fullname) if original_module is None: