def get_related_manager_type_from_metadata(model_info: TypeInfo, related_manager_name: str, api: CheckerPluginInterface) -> Optional[Instance]: related_manager_metadata = get_related_managers_metadata(model_info) if not related_manager_metadata: return None if related_manager_name not in related_manager_metadata: return None manager_class_name = related_manager_metadata[related_manager_name]['manager'] of = related_manager_metadata[related_manager_name]['of'] of_types = [] for of_type_name in of: if of_type_name == 'any': of_types.append(AnyType(TypeOfAny.implementation_artifact)) else: try: of_type = api.named_generic_type(of_type_name, []) except AssertionError: # Internal error: attempted lookup of unknown name of_type = AnyType(TypeOfAny.implementation_artifact) of_types.append(of_type) return api.named_generic_type(manager_class_name, of_types)
def _analyze_open_signature( arg_types: List[List[Type]], args: List[List[Expression]], mode_arg_index: int, default_return_type: Type, api: CheckerPluginInterface, ) -> Type: """A helper for analyzing any function that has approximately the same signature as the builtin 'open(...)' function. Currently, the only thing the caller can customize is the index of the 'mode' argument. If the mode argument is omitted or is a string literal, we refine the return type to either 'TextIO' or 'BinaryIO' as appropriate. """ mode = None if not arg_types or len(arg_types[mode_arg_index]) != 1: mode = 'r' else: mode_expr = args[mode_arg_index][0] if isinstance(mode_expr, StrExpr): mode = mode_expr.value if mode is not None: assert isinstance(default_return_type, Instance) # type: ignore if 'b' in mode: return api.named_generic_type('typing.BinaryIO', []) else: return api.named_generic_type('typing.TextIO', []) return default_return_type
def make_typeddict(api: CheckerPluginInterface, fields: "OrderedDict[str, MypyType]", required_keys: Set[str]) -> TypedDictType: 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 _str_to_int(api: CheckerPluginInterface, typ: Type) -> Type: if isinstance(typ, Instance): if typ.type.fullname() == 'builtins.str': return api.named_generic_type('builtins.int', []) elif typ.args: return typ.copy_modified(args=[_str_to_int(api, t) for t in typ.args]) return typ
def _float_to_int(api: CheckerPluginInterface, typ: Type) -> Type: if isinstance(typ, Instance): if typ.type.fullname() == 'builtins.float': return api.named_generic_type('builtins.int', []) elif typ.args: return typ.copy_modified( args=[_float_to_int(api, t) for t in typ.args]) return typ
def _build_vals_dict(typ: Instance, api: CheckerPluginInterface) -> "OrderedDict[str, Type]": return OrderedDict({ name: (api.named_generic_type("odoo.models._RecordId", [stn.type]) if isinstance(stn.type, Instance) and stn.type.type.fullname.startswith("odoo.models.") else stn.type) for name, stn in typ.type.names.items() if stn.type })
def open_return_type(api: CheckerPluginInterface, args: List[List[Expression]]) -> Optional[Type]: def return_type(word: Literal["Text", "Buffered", "Raw"]) -> Type: return api.named_generic_type( "typing.Awaitable", [api.named_generic_type("trio._Async{}IOBase".format(word), [])], ) if len(args) < 2 or len(args[1]) == 0: # If mode is unspecified, the default is text return return_type("Text") if len(args[1]) == 1: # Mode was specified mode_arg = args[1][0] if isinstance(mode_arg, StrExpr): # Mode is a string constant if "b" not in mode_arg.value: # If there's no "b" in it, it's a text mode return return_type("Text") # Otherwise it's binary -- determine whether buffered or not if len(args) >= 3 and len(args[2]) == 1: # Buffering was specified buffering_arg = args[2][0] if isinstance(buffering_arg, IntExpr): # Buffering is a constant -- zero means # unbuffered, otherwise buffered if buffering_arg.value == 0: return return_type("Raw") return return_type("Buffered") # Not a constant, so we're not sure which it is. options = [ api.named_generic_type("trio._AsyncRawIOBase", []), api.named_generic_type("trio._AsyncBufferedIOBase", []), ] # type: List[Type] return api.named_generic_type( "typing.Awaitable", [UnionType.make_simplified_union(options)]) else: # Buffering is default if not specified return return_type("Buffered") # Mode wasn't a constant or we couldn't make sense of it return None
def make_fake_register_class_instance(api: CheckerPluginInterface, type_args: Sequence[Type] ) -> Instance: defn = ClassDef(REGISTER_RETURN_CLASS, Block([])) defn.fullname = f'functools.{REGISTER_RETURN_CLASS}' info = TypeInfo(SymbolTable(), defn, "functools") obj_type = api.named_generic_type('builtins.object', []).type info.bases = [Instance(obj_type, [])] info.mro = [info, obj_type] defn.info = info func_arg = Argument(Var('name'), AnyType(TypeOfAny.implementation_artifact), None, ARG_POS) add_method_to_class(api, defn, '__call__', [func_arg], NoneType()) return Instance(info, type_args)
def make_simple_type( fieldtype: str, arg_names: List[List[Optional[str]]], args: List[List[Expression]], api: CheckerPluginInterface, ) -> Optional[Type]: typename = SIMPLE_FIELD_TO_TYPE.get(fieldtype) if not typename: return None stdtype = api.named_generic_type(typename, []) for nameset, argset in zip(arg_names, args): for name, arg in zip(nameset, argset): if name == "required" and is_false_literal(arg): nonetype = NoneTyp() optionaltype = UnionType([stdtype, nonetype]) return optionaltype return stdtype
def resolve_model_lookup(api: CheckerPluginInterface, model_type_info: TypeInfo, lookup: str) -> LookupNode: """Resolve a lookup on the given model.""" if lookup == 'pk': # Primary keys are special-cased primary_key_type = helpers.extract_primary_key_type_for_get( model_type_info) if primary_key_type: return FieldNode(primary_key_type) else: # No PK, use the get type for AutoField as PK type. autofield_info = api.lookup_typeinfo( 'django.db.models.fields.AutoField') pk_type = helpers.get_private_descriptor_type( autofield_info, '_pyi_private_get_type', is_nullable=False) return FieldNode(pk_type) field_name = get_actual_field_name_for_lookup_field( lookup, model_type_info) field_node = model_type_info.get(field_name) if not field_node: raise LookupException( f'When resolving lookup "{lookup}", field "{field_name}" was not found in model {model_type_info.name()}' ) if field_name.endswith('_id'): field_name_without_id = field_name.rstrip('_id') foreign_key_field = model_type_info.get(field_name_without_id) if foreign_key_field is not None and helpers.is_foreign_key( foreign_key_field.type): # Hack: If field ends with '_id' and there is a model field without the '_id' suffix, then use that field. field_node = foreign_key_field field_name = field_name_without_id field_node_type = field_node.type if field_node_type is None or not isinstance(field_node_type, Instance): raise LookupException( f'When resolving lookup "{lookup}", could not determine type for {model_type_info.name()}.{field_name}' ) if helpers.is_foreign_key(field_node_type): field_type = helpers.extract_field_getter_type(field_node_type) is_nullable = helpers.is_optional(field_type) if is_nullable: field_type = helpers.make_required(field_type) if isinstance(field_type, Instance): return RelatedModelNode(typ=field_type, is_nullable=is_nullable) else: raise LookupException( f"Not an instance for field {field_type} lookup {lookup}") field_type = helpers.extract_field_getter_type(field_node_type) if field_type: return FieldNode(typ=field_type) else: # Not a Field if field_name == 'id': # If no 'id' field was fouond, use an int return FieldNode(api.named_generic_type('builtins.int', [])) related_manager_arg = None if field_node_type.type.has_base( helpers.RELATED_MANAGER_CLASS_FULLNAME): related_manager_arg = field_node_type.args[0] if related_manager_arg is not None: # Reverse relation return RelatedModelNode(typ=related_manager_arg, is_nullable=True) raise LookupException( f'When resolving lookup "{lookup}", could not determine type for {model_type_info.name()}.{field_name}' )
def resolve_model_lookup(api: CheckerPluginInterface, model_type_info: TypeInfo, lookup: str) -> LookupNode: """Resolve a lookup on the given model.""" if lookup == 'pk': return resolve_model_pk_lookup(api, model_type_info) field_name = get_actual_field_name_for_lookup_field( lookup, model_type_info) field_node = model_type_info.get(field_name) if not field_node: raise LookupException( f'When resolving lookup "{lookup}", field "{field_name}" was not found in model {model_type_info.name()}' ) if field_name.endswith('_id'): field_name_without_id = field_name.rstrip('_id') foreign_key_field = model_type_info.get(field_name_without_id) if foreign_key_field is not None and helpers.is_foreign_key_like( foreign_key_field.type): # Hack: If field ends with '_id' and there is a model field without the '_id' suffix, then use that field. field_node = foreign_key_field field_name = field_name_without_id field_node_type = field_node.type if field_node_type is None or not isinstance(field_node_type, Instance): raise LookupException( f'When resolving lookup "{lookup}", could not determine type for {model_type_info.name()}.{field_name}' ) if field_node_type.type.fullname() == 'builtins.object': # could be related manager related_manager_type = helpers.get_related_manager_type_from_metadata( model_type_info, field_name, api) if related_manager_type: model_arg = related_manager_type.args[0] if not isinstance(model_arg, Instance): raise LookupException( f'When resolving lookup "{lookup}", could not determine type ' f'for {model_type_info.name()}.{field_name}') return RelatedModelNode(typ=model_arg, is_nullable=False) if helpers.is_foreign_key_like(field_node_type): field_type = helpers.extract_field_getter_type(field_node_type) is_nullable = helpers.is_optional(field_type) if is_nullable: # type is always non-optional field_type = helpers.make_required(field_type) if isinstance(field_type, Instance): return RelatedModelNode(typ=field_type, is_nullable=is_nullable) else: raise LookupException( f"Not an instance for field {field_type} lookup {lookup}") field_type = helpers.extract_field_getter_type(field_node_type) if field_type: return FieldNode(typ=field_type) # Not a Field if field_name == 'id': # If no 'id' field was found, use an int return FieldNode(api.named_generic_type('builtins.int', [])) raise LookupException( f'When resolving lookup {lookup!r}, could not determine type for {model_type_info.name()}.{field_name}' )