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_