def get_transformed_type( cls_node: TypeInfo, ans_type: Optional[Type], prop_node: StrExpr, api: SemanticAnalyzerPluginInterface) -> Optional[Type]: transformer_name = f'_transform_{prop_node.value}' transformer = cls_node.get(transformer_name) if transformer is None: return ans_type transformer_type: Optional[Type] if isinstance(transformer.node, Decorator): transformer_type = transformer.node.func.type elif isinstance(transformer.node, FuncDef): transformer_type = transformer.node.type elif (isinstance(transformer.node, Var) and (transformer.node.is_ready or transformer.node.is_final)): if transformer.node.is_ready: transformer_type = transformer.node.type else: transformer_type = find_redef_origin(cls_node, transformer_name) if transformer_type is None: # api.fail( # f'Cannot resolve type of `{transformer_name}`', # transformer.node, code=MISC) return None else: api.fail( f'Cannot handle transformer `{transformer_name}` of type ' + transformer.node.__class__.__name__, transformer.node if transformer.node is not None else cls_node, code=MISC) return None if not isinstance(transformer_type, CallableType): api.fail(f'Cannot infer type of `{transformer_name}`', transformer.node, code=MISC) return None if len(transformer_type.arg_types) != 2: api.fail( f'Expected exactly 2 arguments for {transformer_name}:' 'self and source object', transformer.node, code=CALL_ARG) return None transformer_type = api.anal_type(transformer_type) if not isinstance(transformer_type, CallableType): return None ret_type = bind_type_var(cls_node, transformer_type.ret_type) return api.anal_type(ret_type)
def add_property(cls_node: TypeInfo, ans_cls_node: TypeInfo, prop_node: Expression, api: SemanticAnalyzerPluginInterface) -> None: """Add a property.""" if not isinstance(prop_node, StrExpr): api.fail('Keyword must be a string literal', prop_node, code=VALID_TYPE) return prop_name = prop_node.value try: ans_type = ans_cls_node[prop_name].type except KeyError: api.fail( f'Attribute `{prop_name}` does not exist in ' f'{ans_cls_node.name} or its parents', prop_node, code=ATTR_DEFINED) return prop_type = get_transformed_type(cls_node, ans_type, prop_node, api) if prop_type is None: return if not has_default(cls_node, prop_node, api) and prop_type is not None: prop_type = make_optional(prop_type) new_prop = Var(prop_name, api.anal_type(prop_type)) new_prop.info = cls_node new_prop.is_initialized_in_class = True new_prop.is_property = True cls_node.names[prop_name] = SymbolTableNode(MDEF, new_prop)
def for_argument(cls, semanal: SemanticAnalyzerPluginInterface, argument: Argument) -> 'ResolverArgumentInfo': type_annotation = semanal.anal_type( argument.type_annotation) if argument.type_annotation else None return cls( name=argument.variable.name, type=type_annotation or AnyType(TypeOfAny.unannotated), context=argument, )
def _get_func_def_ret_type(semanal: SemanticAnalyzerPluginInterface, funcdef: FuncDef) -> Type: """ Given a `FuncDef`, return its return-type (or `Any`) """ ret_type = None type_ = funcdef.type if isinstance(type_, CallableType): ret_type = type_.ret_type if isinstance(ret_type, UnboundType): ret_type = semanal.anal_type(ret_type) return ret_type or AnyType(TypeOfAny.unannotated)
def _get_python_type_from_graphene_field_first_argument( semanal: SemanticAnalyzerPluginInterface, argument: Expression, *, covariant: bool, nullable: bool) -> Type: """ Given the first argument to a `Field()`/`Argument()`, return the corresponding runtime (python) type. E.g.: * `String` => `Union[builtins.str, None]` * `NonNull(String)` => `builtins.str` * `NonNull(List(Integer))` => `builtins.list[Union[builtins.int, None]]` """ type_: Optional[Type] = None if isinstance(argument, RefExpr) and isinstance(argument.node, TypeInfo): # This is just a plain graphene type that doesn't wrap anything (i.e `String`, `MyObjectType`, # **not** `List(String)`, `NonNull(MyObjectType), etc.)`` is_scalar = _type_is_a(argument.node, GRAPHENE_SCALAR_NAME) is_object = _type_is_a( argument.node, (GRAPHENE_OBJECTTYPE_NAME, GRAPHENE_INTERFACE_NAME)) is_enum = _type_is_a(argument.node, GRAPHENE_ENUM_NAME) if is_scalar: # This is some scalar (either a builtin one like `String` or a user-defined one) parse_value_type = argument.node.names.get('parse_value') ret_type: Optional[Type] = None # Figure out the runtime type of the scalar by looking at the return value of its `parse_value` method if parse_value_type and isinstance(parse_value_type.type, CallableType): ret_type = parse_value_type.type.ret_type elif (parse_value_type and isinstance(parse_value_type.node, Decorator) and isinstance(parse_value_type.node.func.type, CallableType)): ret_type = parse_value_type.node.func.type.ret_type if ret_type: type_ = ret_type elif is_object: # This is an `ObjectType`/`Interface` child-class, so get the runtime type by looking at the type arg passed # to `ObjectType[]`/`Interface[]` type_ = _get_graphene_subclass_runtime_type(argument.node) elif is_enum: # This is an `Enum` child-class, which means its value will just be a `str` at runtime symbol_table_node = semanal.lookup_fully_qualified('builtins.str') assert isinstance(symbol_table_node.node, TypeInfo) type_ = Instance(symbol_table_node.node, []) elif isinstance(argument, CallExpr) and isinstance( argument.callee, RefExpr) and argument.args: # This is something being called (e.g. `List()`/`NonNull()`) if argument.callee.fullname == GRAPHENE_LIST_NAME: # This is a `List()` # Use a `Sequence` if we want type-checking to be covariant iterable_type_name = 'typing.Sequence' if covariant else 'builtins.list' # Recursively call to figure out the runtime type of the first arg to `List()` and wrap the result # in a `builtins.list`/`typing.Sequence`. symbol_table_node = semanal.lookup_fully_qualified( iterable_type_name) assert isinstance(symbol_table_node.node, TypeInfo) type_ = Instance( symbol_table_node.node, [ _get_python_type_from_graphene_field_first_argument( semanal, argument.args[0], covariant=covariant, nullable=True) ], ) elif argument.callee.fullname == GRAPHENE_NONNULL_NAME: # This is a `NonNull()` # Recursively call to figure out the runtime type of the first arg to `NonNull()` but set the # `nullable` flag to `False` so that the resulting type will **not** be wrapped in a # `Union[X, None]` return _get_python_type_from_graphene_field_first_argument( semanal, argument.args[0], covariant=covariant, nullable=False) if isinstance(type_, UnboundType): type_ = semanal.anal_type(type_) if not type_: return AnyType(TypeOfAny.unannotated) if nullable: return UnionType((type_, NoneType())) return type_