mirror of
https://github.com/davidhalter/django-stubs.git
synced 2025-12-07 04:34:29 +08:00
split helpers into smaller files
This commit is contained in:
@@ -7,7 +7,7 @@ from mypy.types import (
|
||||
AnyType, CallableType, Instance, TupleType, Type, UnionType,
|
||||
)
|
||||
|
||||
from mypy_django_plugin import helpers
|
||||
from mypy_django_plugin.lib import metadata, fullnames, helpers
|
||||
|
||||
|
||||
def extract_referred_to_type(ctx: FunctionContext) -> Optional[Instance]:
|
||||
@@ -43,9 +43,9 @@ def extract_referred_to_type(ctx: FunctionContext) -> Optional[Instance]:
|
||||
referred_to_type = arg_type.ret_type
|
||||
if not isinstance(referred_to_type, Instance):
|
||||
return None
|
||||
if not referred_to_type.type.has_base(helpers.MODEL_CLASS_FULLNAME):
|
||||
if not referred_to_type.type.has_base(fullnames.MODEL_CLASS_FULLNAME):
|
||||
ctx.api.msg.fail(f'to= parameter value must be '
|
||||
f'a subclass of {helpers.MODEL_CLASS_FULLNAME}',
|
||||
f'a subclass of {fullnames.MODEL_CLASS_FULLNAME!r}',
|
||||
context=ctx.context)
|
||||
return None
|
||||
|
||||
@@ -118,26 +118,27 @@ def transform_into_proper_return_type(ctx: FunctionContext) -> Type:
|
||||
if not isinstance(default_return_type, Instance):
|
||||
return default_return_type
|
||||
|
||||
if helpers.has_any_of_bases(default_return_type.type, (helpers.FOREIGN_KEY_FULLNAME,
|
||||
helpers.ONETOONE_FIELD_FULLNAME,
|
||||
helpers.MANYTOMANY_FIELD_FULLNAME)):
|
||||
if helpers.has_any_of_bases(default_return_type.type, (fullnames.FOREIGN_KEY_FULLNAME,
|
||||
fullnames.ONETOONE_FIELD_FULLNAME,
|
||||
fullnames.MANYTOMANY_FIELD_FULLNAME)):
|
||||
return fill_descriptor_types_for_related_field(ctx)
|
||||
|
||||
if default_return_type.type.has_base(helpers.ARRAY_FIELD_FULLNAME):
|
||||
if default_return_type.type.has_base(fullnames.ARRAY_FIELD_FULLNAME):
|
||||
return determine_type_of_array_field(ctx)
|
||||
|
||||
return set_descriptor_types_for_field(ctx)
|
||||
|
||||
|
||||
def adjust_return_type_of_field_instantiation(ctx: FunctionContext) -> Type:
|
||||
record_field_properties_into_outer_model_class(ctx)
|
||||
def process_field_instantiation(ctx: FunctionContext) -> Type:
|
||||
# Parse __init__ parameters of field into corresponding Model's metadata
|
||||
parse_field_init_arguments_into_model_metadata(ctx)
|
||||
return transform_into_proper_return_type(ctx)
|
||||
|
||||
|
||||
def record_field_properties_into_outer_model_class(ctx: FunctionContext) -> None:
|
||||
def parse_field_init_arguments_into_model_metadata(ctx: FunctionContext) -> None:
|
||||
api = cast(TypeChecker, ctx.api)
|
||||
outer_model = api.scope.active_class()
|
||||
if outer_model is None or not outer_model.has_base(helpers.MODEL_CLASS_FULLNAME):
|
||||
if outer_model is None or not outer_model.has_base(fullnames.MODEL_CLASS_FULLNAME):
|
||||
# outside models.Model class, undetermined
|
||||
return
|
||||
|
||||
@@ -149,7 +150,7 @@ def record_field_properties_into_outer_model_class(ctx: FunctionContext) -> None
|
||||
if field_name is None:
|
||||
return
|
||||
|
||||
fields_metadata = helpers.get_fields_metadata(outer_model)
|
||||
fields_metadata = metadata.get_fields_metadata(outer_model)
|
||||
|
||||
# primary key
|
||||
is_primary_key = False
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from mypy.plugin import ClassDefContext, MethodContext
|
||||
from mypy.types import CallableType, Instance, NoneTyp, Type, TypeType
|
||||
|
||||
from mypy_django_plugin import helpers
|
||||
from mypy_django_plugin.lib import metadata, helpers
|
||||
|
||||
|
||||
def make_meta_nested_class_inherit_from_any(ctx: ClassDefContext) -> None:
|
||||
@@ -19,7 +19,7 @@ def extract_proper_type_for_get_form(ctx: MethodContext) -> Type:
|
||||
form_class_type = helpers.get_argument_type_by_name(ctx, 'form_class')
|
||||
if form_class_type is None or isinstance(form_class_type, NoneTyp):
|
||||
# extract from specified form_class in metadata
|
||||
form_class_fullname = helpers.get_django_metadata(object_type.type).get('form_class', None)
|
||||
form_class_fullname = metadata.get_django_metadata(object_type.type).get('form_class', None)
|
||||
if not form_class_fullname:
|
||||
return ctx.default_return_type
|
||||
|
||||
@@ -39,7 +39,7 @@ def extract_proper_type_for_get_form_class(ctx: MethodContext) -> Type:
|
||||
if not isinstance(object_type, Instance):
|
||||
return ctx.default_return_type
|
||||
|
||||
form_class_fullname = helpers.get_django_metadata(object_type.type).get('form_class', None)
|
||||
form_class_fullname = metadata.get_django_metadata(object_type.type).get('form_class', None)
|
||||
if not form_class_fullname:
|
||||
return ctx.default_return_type
|
||||
|
||||
|
||||
@@ -5,13 +5,13 @@ from mypy.nodes import TypeInfo, Var
|
||||
from mypy.plugin import FunctionContext, MethodContext
|
||||
from mypy.types import AnyType, Instance, Type, TypeOfAny
|
||||
|
||||
from mypy_django_plugin import helpers
|
||||
from mypy_django_plugin.lib import metadata, fullnames, helpers
|
||||
|
||||
|
||||
def extract_base_pointer_args(model: TypeInfo) -> Set[str]:
|
||||
pointer_args: Set[str] = set()
|
||||
for base in model.bases:
|
||||
if base.type.has_base(helpers.MODEL_CLASS_FULLNAME):
|
||||
if base.type.has_base(fullnames.MODEL_CLASS_FULLNAME):
|
||||
parent_name = base.type.name().lower()
|
||||
pointer_args.add(f'{parent_name}_ptr')
|
||||
pointer_args.add(f'{parent_name}_ptr_id')
|
||||
@@ -105,7 +105,7 @@ def redefine_and_typecheck_model_create(ctx: MethodContext) -> Type:
|
||||
|
||||
|
||||
def extract_choices_type(model: TypeInfo, field_name: str) -> Optional[str]:
|
||||
field_metadata = helpers.get_fields_metadata(model).get(field_name, {})
|
||||
field_metadata = metadata.get_fields_metadata(model).get(field_name, {})
|
||||
if 'choices' in field_metadata:
|
||||
return field_metadata['choices']
|
||||
return None
|
||||
@@ -146,8 +146,8 @@ def extract_expected_types(ctx: FunctionContext, model: TypeInfo,
|
||||
if field_type is None:
|
||||
continue
|
||||
|
||||
if helpers.has_any_of_bases(typ.type, (helpers.FOREIGN_KEY_FULLNAME,
|
||||
helpers.ONETOONE_FIELD_FULLNAME)):
|
||||
if helpers.has_any_of_bases(typ.type, (fullnames.FOREIGN_KEY_FULLNAME,
|
||||
fullnames.ONETOONE_FIELD_FULLNAME)):
|
||||
related_primary_key_type = AnyType(TypeOfAny.implementation_artifact)
|
||||
# in case it's optional, we need Instance type
|
||||
referred_to_model = typ.args[1]
|
||||
@@ -156,7 +156,7 @@ def extract_expected_types(ctx: FunctionContext, model: TypeInfo,
|
||||
referred_to_model = helpers.make_required(typ.args[1])
|
||||
|
||||
if isinstance(referred_to_model, Instance) and referred_to_model.type.has_base(
|
||||
helpers.MODEL_CLASS_FULLNAME):
|
||||
fullnames.MODEL_CLASS_FULLNAME):
|
||||
pk_type = helpers.extract_explicit_set_type_of_model_primary_key(referred_to_model.type)
|
||||
if not pk_type:
|
||||
# extract set type of AutoField
|
||||
@@ -170,11 +170,11 @@ def extract_expected_types(ctx: FunctionContext, model: TypeInfo,
|
||||
|
||||
expected_types[name + '_id'] = related_primary_key_type
|
||||
|
||||
field_metadata = helpers.get_fields_metadata(model).get(name, {})
|
||||
field_metadata = metadata.get_fields_metadata(model).get(name, {})
|
||||
if field_type:
|
||||
# related fields could be None in __init__ (but should be specified before save())
|
||||
if helpers.has_any_of_bases(typ.type, (helpers.FOREIGN_KEY_FULLNAME,
|
||||
helpers.ONETOONE_FIELD_FULLNAME)) and is_init:
|
||||
if helpers.has_any_of_bases(typ.type, (fullnames.FOREIGN_KEY_FULLNAME,
|
||||
fullnames.ONETOONE_FIELD_FULLNAME)) and is_init:
|
||||
field_type = helpers.make_optional(field_type)
|
||||
|
||||
# if primary_key=True and default specified
|
||||
@@ -184,7 +184,7 @@ def extract_expected_types(ctx: FunctionContext, model: TypeInfo,
|
||||
|
||||
# if CharField(blank=True,...) and not nullable, then field can be None in __init__
|
||||
elif (
|
||||
helpers.has_any_of_bases(typ.type, (helpers.CHAR_FIELD_FULLNAME,)) and is_init and
|
||||
helpers.has_any_of_bases(typ.type, (fullnames.CHAR_FIELD_FULLNAME,)) and is_init and
|
||||
field_metadata.get('blank', False) and not field_metadata.get('null', False)
|
||||
):
|
||||
field_type = helpers.make_optional(field_type)
|
||||
|
||||
@@ -5,7 +5,7 @@ from mypy.nodes import Expression, StrExpr, TypeInfo
|
||||
from mypy.plugin import MethodContext
|
||||
from mypy.types import Instance, Type, TypeType
|
||||
|
||||
from mypy_django_plugin import helpers
|
||||
from mypy_django_plugin.lib import helpers
|
||||
|
||||
|
||||
def get_string_value_from_expr(expr: Expression) -> Optional[str]:
|
||||
|
||||
@@ -11,7 +11,7 @@ from mypy.plugins.common import add_method
|
||||
from mypy.semanal import SemanticAnalyzerPass2
|
||||
from mypy.types import AnyType, Instance, NoneTyp, TypeOfAny
|
||||
|
||||
from mypy_django_plugin import helpers
|
||||
from mypy_django_plugin.lib import metadata, fullnames, helpers
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
@@ -55,8 +55,8 @@ def iter_over_one_to_n_related_fields(klass: ClassDef) -> Iterator[Tuple[NameExp
|
||||
for lvalue, rvalue in helpers.iter_call_assignments(klass):
|
||||
if (isinstance(lvalue, NameExpr)
|
||||
and isinstance(rvalue.callee, MemberExpr)):
|
||||
if rvalue.callee.fullname in {helpers.FOREIGN_KEY_FULLNAME,
|
||||
helpers.ONETOONE_FIELD_FULLNAME}:
|
||||
if rvalue.callee.fullname in {fullnames.FOREIGN_KEY_FULLNAME,
|
||||
fullnames.ONETOONE_FIELD_FULLNAME}:
|
||||
yield lvalue, rvalue
|
||||
|
||||
|
||||
@@ -97,7 +97,7 @@ class AddDefaultObjectsManager(ModelClassInitializer):
|
||||
callee_expr = callee_expr.analyzed.expr
|
||||
if isinstance(callee_expr, (MemberExpr, NameExpr)) \
|
||||
and isinstance(callee_expr.node, TypeInfo) \
|
||||
and callee_expr.node.has_base(helpers.BASE_MANAGER_CLASS_FULLNAME):
|
||||
and callee_expr.node.has_base(fullnames.BASE_MANAGER_CLASS_FULLNAME):
|
||||
managers.append((manager_name, callee_expr.node))
|
||||
return managers
|
||||
|
||||
@@ -115,7 +115,7 @@ class AddDefaultObjectsManager(ModelClassInitializer):
|
||||
# abstract models do not need 'objects' queryset
|
||||
return None
|
||||
|
||||
first_manager_type = self.api.named_type_or_none(helpers.MANAGER_CLASS_FULLNAME,
|
||||
first_manager_type = self.api.named_type_or_none(fullnames.MANAGER_CLASS_FULLNAME,
|
||||
args=[Instance(self.model_classdef.info, [])])
|
||||
self.add_new_manager('objects', manager_type=first_manager_type)
|
||||
|
||||
@@ -148,18 +148,21 @@ class AddRelatedManagers(ModelClassInitializer):
|
||||
self.add_new_node_to_model_class(manager_name, self.api.builtin_type('builtins.object'))
|
||||
|
||||
# save name in metadata for use in get_attribute_hook later
|
||||
related_managers_metadata = helpers.get_related_managers_metadata(self.model_classdef.info)
|
||||
related_managers_metadata = metadata.get_related_managers_metadata(self.model_classdef.info)
|
||||
related_managers_metadata[manager_name] = related_field_type_data
|
||||
|
||||
def run(self) -> None:
|
||||
for module_name, module_file in self.api.modules.items():
|
||||
for model_defn in helpers.iter_over_classdefs(module_file):
|
||||
for lvalue, rvalue in helpers.iter_call_assignments(model_defn):
|
||||
if is_related_field(rvalue, module_file):
|
||||
if not model_defn.info:
|
||||
self.api.defer()
|
||||
|
||||
for lvalue, field_init in helpers.iter_call_assignments(model_defn):
|
||||
if is_related_field(field_init, module_file):
|
||||
try:
|
||||
referenced_model_fullname = extract_ref_to_fullname(rvalue,
|
||||
module_file=module_file,
|
||||
all_modules=self.api.modules)
|
||||
referenced_model_fullname = extract_referenced_model_fullname(field_init,
|
||||
module_file=module_file,
|
||||
all_modules=self.api.modules)
|
||||
except helpers.SelfReference:
|
||||
referenced_model_fullname = model_defn.fullname
|
||||
|
||||
@@ -168,39 +171,33 @@ class AddRelatedManagers(ModelClassInitializer):
|
||||
|
||||
if self.model_classdef.fullname == referenced_model_fullname:
|
||||
related_name = model_defn.name.lower() + '_set'
|
||||
if 'related_name' in rvalue.arg_names:
|
||||
related_name_expr = rvalue.args[rvalue.arg_names.index('related_name')]
|
||||
if 'related_name' in field_init.arg_names:
|
||||
related_name_expr = field_init.args[field_init.arg_names.index('related_name')]
|
||||
if not isinstance(related_name_expr, StrExpr):
|
||||
# not string 'related_name=' not yet supported
|
||||
continue
|
||||
related_name = related_name_expr.value
|
||||
if related_name == '+':
|
||||
# No backwards relation is desired
|
||||
continue
|
||||
|
||||
if 'related_query_name' in rvalue.arg_names:
|
||||
related_query_name_expr = rvalue.args[rvalue.arg_names.index('related_query_name')]
|
||||
if not isinstance(related_query_name_expr, StrExpr):
|
||||
related_query_name = None
|
||||
else:
|
||||
# Default related_query_name to related_name
|
||||
related_query_name = related_name
|
||||
if 'related_query_name' in field_init.arg_names:
|
||||
related_query_name_expr = field_init.args[field_init.arg_names.index('related_query_name')]
|
||||
if isinstance(related_query_name_expr, StrExpr):
|
||||
related_query_name = related_query_name_expr.value
|
||||
else:
|
||||
# not string 'related_query_name=' is not yet supported
|
||||
related_query_name = None
|
||||
# TODO: Handle defaulting to model name if related_name is not set
|
||||
else:
|
||||
# No related_query_name specified, default to related_name
|
||||
related_query_name = related_name
|
||||
|
||||
# field_type_data = get_related_field_type(rvalue, self.api, defn.info)
|
||||
# if typ is None:
|
||||
# continue
|
||||
|
||||
# TODO: recursively serialize types, or just https://github.com/python/mypy/issues/6506
|
||||
# as long as Model is not a Generic, one level depth is fine
|
||||
if rvalue.callee.name in {'ForeignKey', 'ManyToManyField'}:
|
||||
if field_init.callee.name in {'ForeignKey', 'ManyToManyField'}:
|
||||
field_type_data = {
|
||||
'manager': helpers.RELATED_MANAGER_CLASS_FULLNAME,
|
||||
'manager': fullnames.RELATED_MANAGER_CLASS_FULLNAME,
|
||||
'of': [model_defn.info.fullname()]
|
||||
}
|
||||
# return api.named_type_or_none(helpers.RELATED_MANAGER_CLASS_FULLNAME,
|
||||
# args=[Instance(related_model_typ, [])])
|
||||
else:
|
||||
field_type_data = {
|
||||
'manager': model_defn.info.fullname(),
|
||||
@@ -211,7 +208,7 @@ class AddRelatedManagers(ModelClassInitializer):
|
||||
|
||||
if related_query_name is not None:
|
||||
# Only create related_query_name if it is a string literal
|
||||
helpers.get_lookups_metadata(self.model_classdef.info)[related_query_name] = {
|
||||
metadata.get_lookups_metadata(self.model_classdef.info)[related_query_name] = {
|
||||
'related_query_name_target': related_name
|
||||
}
|
||||
|
||||
@@ -219,20 +216,18 @@ class AddRelatedManagers(ModelClassInitializer):
|
||||
def get_related_field_type(rvalue: CallExpr, related_model_typ: TypeInfo) -> Dict[str, Any]:
|
||||
if rvalue.callee.name in {'ForeignKey', 'ManyToManyField'}:
|
||||
return {
|
||||
'manager': helpers.RELATED_MANAGER_CLASS_FULLNAME,
|
||||
'manager': fullnames.RELATED_MANAGER_CLASS_FULLNAME,
|
||||
'of': [related_model_typ.fullname()]
|
||||
}
|
||||
# return api.named_type_or_none(helpers.RELATED_MANAGER_CLASS_FULLNAME,
|
||||
# args=[Instance(related_model_typ, [])])
|
||||
else:
|
||||
return {
|
||||
'manager': related_model_typ.fullname(),
|
||||
'of': []
|
||||
}
|
||||
# return Instance(related_model_typ, [])
|
||||
|
||||
|
||||
def is_related_field(expr: CallExpr, module_file: MypyFile) -> bool:
|
||||
""" Checks whether current CallExpr represents any supported RelatedField subclass"""
|
||||
if isinstance(expr.callee, MemberExpr) and isinstance(expr.callee.expr, NameExpr):
|
||||
module = module_file.names.get(expr.callee.expr.name)
|
||||
if module \
|
||||
@@ -244,12 +239,15 @@ def is_related_field(expr: CallExpr, module_file: MypyFile) -> bool:
|
||||
return False
|
||||
|
||||
|
||||
def extract_ref_to_fullname(rvalue_expr: CallExpr,
|
||||
module_file: MypyFile, all_modules: Dict[str, MypyFile]) -> Optional[str]:
|
||||
def extract_referenced_model_fullname(rvalue_expr: CallExpr,
|
||||
module_file: MypyFile,
|
||||
all_modules: Dict[str, MypyFile]) -> Optional[str]:
|
||||
""" Returns fullname of a Model referenced in "to=" argument of the CallExpr"""
|
||||
if 'to' in rvalue_expr.arg_names:
|
||||
to_expr = rvalue_expr.args[rvalue_expr.arg_names.index('to')]
|
||||
else:
|
||||
to_expr = rvalue_expr.args[0]
|
||||
|
||||
if isinstance(to_expr, NameExpr):
|
||||
return module_file.names[to_expr.name].fullname
|
||||
elif isinstance(to_expr, StrExpr):
|
||||
|
||||
@@ -8,8 +8,8 @@ from mypy.plugin import (
|
||||
)
|
||||
from mypy.types import AnyType, Instance, Type, TypeOfAny
|
||||
|
||||
from mypy_django_plugin import helpers
|
||||
from mypy_django_plugin.lookups import (
|
||||
from mypy_django_plugin.lib import helpers
|
||||
from mypy_django_plugin.lib.lookups import (
|
||||
LookupException, RelatedModelNode, resolve_lookup,
|
||||
)
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ from mypy.checkmember import AttributeContext
|
||||
from mypy.nodes import TypeInfo
|
||||
from mypy.types import AnyType, Instance, Type, TypeOfAny, UnionType
|
||||
|
||||
from mypy_django_plugin import helpers
|
||||
from mypy_django_plugin.lib import fullnames, helpers
|
||||
|
||||
|
||||
def _extract_referred_to_type_info(typ: Union[UnionType, Instance]) -> Optional[TypeInfo]:
|
||||
@@ -22,7 +22,7 @@ def extract_and_return_primary_key_of_bound_related_field_parameter(ctx: Attribu
|
||||
if not isinstance(ctx.default_attr_type, Instance) or not (ctx.default_attr_type.type.fullname() == 'builtins.int'):
|
||||
return ctx.default_attr_type
|
||||
|
||||
if not isinstance(ctx.type, Instance) or not ctx.type.type.has_base(helpers.MODEL_CLASS_FULLNAME):
|
||||
if not isinstance(ctx.type, Instance) or not ctx.type.type.has_base(fullnames.MODEL_CLASS_FULLNAME):
|
||||
return ctx.default_attr_type
|
||||
|
||||
field_name = ctx.context.name.split('_')[0]
|
||||
|
||||
@@ -5,7 +5,7 @@ from mypy.checkmember import AttributeContext
|
||||
from mypy.nodes import NameExpr, StrExpr, SymbolTableNode, TypeInfo
|
||||
from mypy.types import AnyType, Instance, Type, TypeOfAny, TypeType
|
||||
|
||||
from mypy_django_plugin import helpers
|
||||
from mypy_django_plugin.lib import helpers
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from mypy.checker import TypeChecker
|
||||
|
||||
Reference in New Issue
Block a user