Example #1
0
def transform_into_proper_return_type(ctx: FunctionContext) -> Type:
    default_return_type = ctx.default_return_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)):
        return fill_descriptor_types_for_related_field(ctx)

    if default_return_type.type.has_base(helpers.ARRAY_FIELD_FULLNAME):
        return determine_type_of_array_field(ctx)

    return set_descriptor_types_for_field(ctx)
Example #2
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