Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor common code in analyze_member_access and find_member #3832

Open
ilevkivskyi opened this issue Aug 15, 2017 · 3 comments
Open

Refactor common code in analyze_member_access and find_member #3832

ilevkivskyi opened this issue Aug 15, 2017 · 3 comments

Comments

@ilevkivskyi
Copy link
Member

Currently, checkmember.analyze_member_access and subtypes.find_member share quite a bit of logic. We could refactor this to some common place. In addition, bind_self that is used in both modules creates an import cycle, so that it makes sense to move it to a separate location.

This is a follow-up of #3132

@ilevkivskyi
Copy link
Member Author

Raising priority to normal, since there are couple of things that are not done by the code in subtypes.find_member such as e.g. descriptor access. And this causes problems with ORMs.

@sergab11
Copy link

sergab11 commented Jun 5, 2023

Hey, guys!
Can we unite those three functions checkmember.analyze_member_access, checkmember._analyze_member_access and subtypes.find_member into one checkmember.analyze_member_access? If that's ok, I can change every occurrence of find_member to this standalone function:

def analyze_member_access(
    name: str,
    typ: Type,
    context: Context,
    is_lvalue: bool,
    is_super: bool,
    is_operator: bool,
    msg: MessageBuilder,
    *,
    original_type: Type,
    chk: mypy.checker.TypeChecker,
    override_info: TypeInfo | None = None,
    in_literal_context: bool = False,
    self_type: Type | None = None,
    module_symbol_table: SymbolTable | None = None,
    no_deferral: bool = False,
    is_self: bool = False,
) -> Type:
    """Return the type of attribute 'name' of 'typ'.

    The actual implementation is in '_analyze_member_access' and this docstring
    also applies to it.

    This is a general operation that supports various different variations:

      1. lvalue or non-lvalue access (setter or getter access)
      2. supertype access when using super() (is_super == True and
         'override_info' should refer to the supertype)

    'original_type' is the most precise inferred or declared type of the base object
    that we have available. When looking for an attribute of 'typ', we may perform
    recursive calls targeting the fallback type, and 'typ' may become some supertype
    of 'original_type'. 'original_type' is always preserved as the 'typ' type used in
    the initial, non-recursive call. The 'self_type' is a component of 'original_type'
    to which generic self should be bound (a narrower type that has a fallback to instance).
    Currently this is used only for union types.

    'module_symbol_table' is passed to this function if 'typ' is actually a module
    and we want to keep track of the available attributes of the module (since they
    are not available via the type object directly)
    """
    mx = MemberContext(
        is_lvalue,
        is_super,
        is_operator,
        original_type,
        context,
        msg,
        chk=chk,
        self_type=self_type,
        module_symbol_table=module_symbol_table,
        no_deferral=no_deferral,
        is_self=is_self,
    )
    typ = get_proper_type(typ)
    # find_member logic
    if isinstance(typ, Instance):
        info = typ.type
        method = info.get_method(name)
        if method:
            if isinstance(method, Decorator):
                return find_node_type(method.var, typ, typ, class_obj=is_super)
            if method.is_property:
                assert isinstance(method, OverloadedFuncDef)
                dec = method.items[0]
                assert isinstance(dec, Decorator)
                return find_node_type(dec.var, typ, typ, class_obj=is_super)
            return find_node_type(method, typ, typ, class_obj=is_super)
        else:
            node = info.get(name)
            v = node.node if node else None
            if isinstance(v, Var):
                return find_node_type(v, typ, typ, class_obj=is_super)
            if (
                not v
                and name not in ["__getattr__", "__setattr__", "__getattribute__"]
                and not is_operator
                and not is_super
                and not is_self
                and typ.extra_attrs is None
            ):
                for method_name in ["__getattribute__", "__getattr__"]:
                    method = info.get_method(method_name)
                    if method and method.info.fullname != "builtins.object":
                        if isinstance(method, Decorator):
                            getattr_type = get_proper_type(find_node_type(method.var, typ, typ))
                        else:
                            getattr_type = get_proper_type(find_node_type(method, typ, typ))
                        if isinstance(getattr_type, CallableType):
                            return getattr_type.ret_type
                        return getattr_type
            if typ.fallback_to_any or (is_super and typ.meta_fallback_to_any):
                return AnyType(TypeOfAny.special_form)
            if isinstance(v, TypeInfo):
                return TypeType(fill_typevars_with_any(v))
        if typ.extra_attrs and name in typ.extra_attrs.attrs:
            return typ.extra_attrs.attrs[name]
        # end of find_member logic
    elif isinstance(typ, AnyType):
        return AnyType(TypeOfAny.from_another_any, source_any=typ)
    elif isinstance(typ, UnionType):
        return analyze_union_member_access(name, typ, mx)
    elif isinstance(typ, FunctionLike) and typ.is_type_obj():
        return analyze_type_callable_member_access(name, typ, mx)
    elif isinstance(typ, TypeType):
        return analyze_type_type_member_access(name, typ, mx, override_info)
    elif isinstance(typ, TupleType):
        return analyze_member_access(name, tuple_fallback(typ), context, is_lvalue, is_super, is_operator, msg, original_type=original_type, chk=chk, override_info=override_info, in_literal_context=in_literal_context, self_type=self_type, module_symbol_table=module_symbol_table, no_deferral=no_deferral, is_self=is_self)
    elif isinstance(typ, (LiteralType, FunctionLike)):
        return analyze_member_access(name, typ.fallback, context, is_lvalue, is_super, is_operator, msg, original_type=original_type, chk=chk, override_info=override_info, in_literal_context=in_literal_context, self_type=self_type, module_symbol_table=module_symbol_table, no_deferral=no_deferral, is_self=is_self)
    elif isinstance(typ, TypedDictType):
        return analyze_typeddict_access(name, typ, mx, override_info)
    elif isinstance(typ, NoneType):
        return analyze_none_member_access(name, typ, mx)
    elif isinstance(typ, TypeVarLikeType):
        if isinstance(typ, TypeVarType) and typ.values:
            return analyze_member_access(name, make_simplified_union(typ.values), context, is_lvalue, is_super, is_operator, msg, original_type=original_type, chk=chk, override_info=override_info, in_literal_context=in_literal_context, self_type=self_type, module_symbol_table=module_symbol_table, no_deferral=no_deferral, is_self=is_self)
        return analyze_member_access(name, typ.upper_bound, context, is_lvalue, is_super, is_operator, msg, original_type=original_type, chk=chk, override_info=override_info, in_literal_context=in_literal_context, self_type=self_type, module_symbol_table=module_symbol_table, no_deferral=no_deferral, is_self=is_self)
    elif isinstance(typ, DeletedType):
        msg.deleted_as_rvalue(typ, context)
        return AnyType(TypeOfAny.from_error)
    else:
        return report_missing_attribute(original_type, typ, name, context)

@sobolevn
Copy link
Member

sobolevn commented Jun 5, 2023

@sergab11 find_member is called where our code does not have access to chk, that's the main problem.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants