Example #1
0
def extract_and_return_primary_key_of_bound_related_field_parameter(ctx: AttributeContext) -> Type:
    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):
        return ctx.default_attr_type

    field_name = ctx.context.name.split('_')[0]
    sym = ctx.type.type.get(field_name)
    if sym and isinstance(sym.type, Instance) and len(sym.type.args) > 0:
        referred_to = sym.type.args[1]
        if isinstance(referred_to, AnyType):
            return AnyType(TypeOfAny.implementation_artifact)

        model_type = _extract_referred_to_type_info(referred_to)
        if model_type is None:
            return AnyType(TypeOfAny.implementation_artifact)

        primary_key_type = helpers.extract_primary_key_type_for_get(model_type)
        if primary_key_type:
            return primary_key_type

    is_nullable = helpers.get_fields_metadata(ctx.type.type).get(field_name, {}).get('null', False)
    if is_nullable:
        return helpers.make_optional(ctx.default_attr_type)

    return ctx.default_attr_type
Example #2
0
def resolve_values_lookup(api: CheckerPluginInterface,
                          model_type_info: TypeInfo,
                          lookup: str) -> Optional[Type]:
    """Resolves a values/values_list lookup if possible, to a Type."""
    try:
        nodes = resolve_lookup(api, model_type_info, lookup)
    except LookupException:
        nodes = []

    if not nodes:
        return None

    make_optional = False

    for node in nodes:
        if isinstance(node, RelatedModelNode) and node.is_nullable:
            # All lookups following a relation which is nullable should be optional
            make_optional = True

    node = nodes[-1]

    node_type = node.typ
    if isinstance(node, RelatedModelNode):
        # Related models used in values/values_list get resolved to the primary key of the related model.
        # So, we lookup the pk of that model.
        pk_lookup_nodes = resolve_lookup(api, node_type.type, "pk")
        if not pk_lookup_nodes:
            return None
        node_type = pk_lookup_nodes[0].typ
    if make_optional:
        return helpers.make_optional(node_type)
    else:
        return node_type
Example #3
0
def get_private_descriptor_type(type_info: TypeInfo, private_field_name: str, is_nullable: bool) -> Type:
    node = type_info.get(private_field_name).node
    if isinstance(node, Var):
        descriptor_type = node.type
        if is_nullable:
            descriptor_type = helpers.make_optional(descriptor_type)
        return descriptor_type
    return AnyType(TypeOfAny.unannotated)
Example #4
0
def extract_expected_types(ctx: FunctionContext,
                           model: TypeInfo,
                           is_init: bool = False) -> Dict[str, Type]:
    api = cast(TypeChecker, ctx.api)

    expected_types: Dict[str, Type] = {}
    primary_key_type = helpers.extract_explicit_set_type_of_model_primary_key(
        model)
    if not primary_key_type:
        # no explicit primary key, set pk to Any and add id
        primary_key_type = AnyType(TypeOfAny.special_form)
        if is_init:
            expected_types['id'] = helpers.make_optional(
                ctx.api.named_generic_type('builtins.int', []))
        else:
            expected_types['id'] = ctx.api.named_generic_type(
                'builtins.int', [])

    expected_types['pk'] = primary_key_type

    for base in model.mro:
        # extract all fields for all models in MRO
        for name, sym in base.names.items():
            # do not redefine special attrs
            if name in {'_meta', 'pk'}:
                continue

            if isinstance(sym.node, Var):
                typ = sym.node.type
                if typ is None or isinstance(typ, AnyType):
                    # types are not ready, fallback to Any
                    expected_types[name] = AnyType(
                        TypeOfAny.from_unimported_type)
                    expected_types[name + '_id'] = AnyType(
                        TypeOfAny.from_unimported_type)

                elif isinstance(typ, Instance):
                    field_type = helpers.extract_field_setter_type(typ)
                    if field_type is None:
                        continue

                    if helpers.has_any_of_bases(
                            typ.type, (helpers.FOREIGN_KEY_FULLNAME,
                                       helpers.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]
                        is_nullable = helpers.is_optional(referred_to_model)
                        if is_nullable:
                            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):
                            pk_type = helpers.extract_explicit_set_type_of_model_primary_key(
                                referred_to_model.type)
                            if not pk_type:
                                # extract set type of AutoField
                                autofield_info = api.lookup_typeinfo(
                                    'django.db.models.fields.AutoField')
                                pk_type = helpers.get_private_descriptor_type(
                                    autofield_info,
                                    '_pyi_private_set_type',
                                    is_nullable=is_nullable)
                            related_primary_key_type = pk_type

                        if is_init:
                            related_primary_key_type = helpers.make_optional(
                                related_primary_key_type)

                        expected_types[name + '_id'] = related_primary_key_type

                    field_metadata = helpers.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:
                            field_type = helpers.make_optional(field_type)

                        # if primary_key=True and default specified
                        elif field_metadata.get(
                                'primary_key', False) and field_metadata.get(
                                    'default_specified', False):
                            field_type = helpers.make_optional(field_type)

                        # 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 field_metadata.get('blank', False)
                              and not field_metadata.get('null', False)):
                            field_type = helpers.make_optional(field_type)

                        expected_types[name] = field_type

    return expected_types