# Buggy code
def is_protocol_implementation(
    left: Instance, right: Instance, proper_subtype: bool = False
) -> bool:
    """Check whether 'left' implements the protocol 'right'.

    If 'proper_subtype' is True, then check for a proper subtype.
    Treat recursive protocols by using the 'assuming' structural subtype matrix
    (in sparse representation, i.e. as a list of pairs (subtype, supertype)),
    see also comment in nodes.TypeInfo. When we enter a check for classes
    (A, P), defined as following::

      class P(Protocol):
          def f(self) -> P: ...
      class A:
          def f(self) -> A: ...

    this results in A being a subtype of P without infinite recursion.
    On every false result, we pop the assumption, thus avoiding an infinite recursion
    as well.
    """
    assert right.type.is_protocol
    # We need to record this check to generate protocol fine-grained dependencies.
    TypeState.record_protocol_subtype_check(left.type, right.type)
    assuming = right.type.assuming_proper if proper_subtype else right.type.assuming
    for l, r in reversed(assuming):
        if mypy.sametypes.is_same_type(l, left) and mypy.sametypes.is_same_type(
            r, right
        ):
            return True
    with pop_on_exit(assuming, left, right):
        for member in right.type.protocol_members:
            # nominal subtyping currently ignores '__init__' and '__new__' signatures
            if member in ("__init__", "__new__"):
                continue
            ignore_names = member != "__call__"  # __call__ can be passed kwargs
            # The third argument below indicates to what self type is bound.
            # We always bind self to the subtype. (Similarly to nominal types).
            supertype = get_proper_type(find_member(member, right, left))
            assert supertype is not None
            subtype = get_proper_type(find_member(member, left, left))
            # Useful for debugging:
            # print(member, 'of', left, 'has type', subtype)
            # print(member, 'of', right, 'has type', supertype)
            if not subtype:
                return False
            if not proper_subtype:
                # Nominal check currently ignores arg names
                # NOTE: If we ever change this, be sure to also change the call to
                # SubtypeVisitor.build_subtype_kind(...) down below.
                is_compat = is_subtype(
                    subtype, supertype, ignore_pos_arg_names=ignore_names
                )
            else:
                is_compat = is_proper_subtype(subtype, supertype)
            if not is_compat:
                return False
            if isinstance(subtype, NoneType) and isinstance(supertype, CallableType):
                # We want __hash__ = None idiom to work even without --strict-optional
                return False
            subflags = get_member_flags(member, left.type)
            superflags = get_member_flags(member, right.type)
            if IS_SETTABLE in superflags:
                # Check opposite direction for settable attributes.
                if not is_subtype(supertype, subtype):
                    return False
            if (IS_CLASSVAR in subflags) != (IS_CLASSVAR in superflags):
                return False
            if IS_SETTABLE in superflags and IS_SETTABLE not in subflags:
                return False
            # This rule is copied from nominal check in checker.py
            if IS_CLASS_OR_STATIC in superflags and IS_CLASS_OR_STATIC not in subflags:
                return False

    if not proper_subtype:
        # Nominal check currently ignores arg names, but __call__ is special for protocols
        ignore_names = right.type.protocol_members != ["__call__"]
        subtype_kind = SubtypeVisitor.build_subtype_kind(
            ignore_pos_arg_names=ignore_names
        )
    else:
        subtype_kind = ProperSubtypeVisitor.build_subtype_kind()
    TypeState.record_subtype_cache_entry(subtype_kind, left, right)
    return True


# Traceback (RuntimeError)
/XXXXX/venv/lib/python3.7/site-packages/xarray/core/dataarray.py:2001: error: INTERNAL ERROR -- Please try using mypy master on Github:
https://mypy.rtfd.io/en/latest/common_issues.html#using-a-development-mypy-build
Please report a bug at https://github.com/python/mypy/issues
version: 0.782
Traceback (most recent call last):
File "mypy/checker.py", line 401, in accept
File "mypy/nodes.py", line 1129, in accept
File "mypy/checker.py", line 3437, in visit_for_stmt
File "mypy/checker.py", line 3470, in analyze_iterable_item_type
File "mypy/checkexpr.py", line 2345, in check_method_call_by_name
File "mypy/checkmember.py", line 126, in analyze_member_access
File "mypy/checkmember.py", line 143, in _analyze_member_access
File "mypy/checkmember.py", line 225, in analyze_instance_member_access
File "mypy/checkmember.py", line 376, in analyze_member_var_access
File "mypy/checkmember.py", line 578, in analyze_var
File "mypy/meet.py", line 50, in meet_types
File "mypy/types.py", line 794, in accept
File "mypy/meet.py", line 497, in visit_instance
File "mypy/meet.py", line 647, in meet
File "mypy/meet.py", line 50, in meet_types
File "mypy/types.py", line 1347, in accept
File "mypy/meet.py", line 584, in visit_tuple_type
File "mypy/typeops.py", line 44, in tuple_fallback
File "mypy/join.py", line 531, in join_type_list
File "mypy/join.py", line 105, in join_types
File "mypy/types.py", line 794, in accept
File "mypy/join.py", line 158, in visit_instance
File "mypy/join.py", line 371, in join_instances
File "mypy/subtypes.py", line 143, in is_subtype_ignoring_tvars
File "mypy/subtypes.py", line 93, in is_subtype
File "mypy/subtypes.py", line 135, in _is_subtype
File "mypy/types.py", line 794, in accept
File "mypy/subtypes.py", line 259, in visit_instance
File "mypy/subtypes.py", line 541, in is_protocol_implementation
File "mypy/subtypes.py", line 93, in is_subtype
File "mypy/subtypes.py", line 135, in _is_subtype
File "mypy/types.py", line 1786, in accept
File "mypy/subtypes.py", line 459, in visit_partial_type
RuntimeError:
/XXXXX/venv/lib/python3.7/site-packages/xarray/core/dataarray.py:2001: : note: use --pdb to drop into pdb

# Fixed code
def is_protocol_implementation(
    left: Instance, right: Instance, proper_subtype: bool = False
) -> bool:
    """Check whether 'left' implements the protocol 'right'.

    If 'proper_subtype' is True, then check for a proper subtype.
    Treat recursive protocols by using the 'assuming' structural subtype matrix
    (in sparse representation, i.e. as a list of pairs (subtype, supertype)),
    see also comment in nodes.TypeInfo. When we enter a check for classes
    (A, P), defined as following::

      class P(Protocol):
          def f(self) -> P: ...
      class A:
          def f(self) -> A: ...

    this results in A being a subtype of P without infinite recursion.
    On every false result, we pop the assumption, thus avoiding an infinite recursion
    as well.
    """
    assert right.type.is_protocol
    # We need to record this check to generate protocol fine-grained dependencies.
    TypeState.record_protocol_subtype_check(left.type, right.type)
    assuming = right.type.assuming_proper if proper_subtype else right.type.assuming
    for l, r in reversed(assuming):
        if mypy.sametypes.is_same_type(l, left) and mypy.sametypes.is_same_type(
            r, right
        ):
            return True
    with pop_on_exit(assuming, left, right):
        for member in right.type.protocol_members:
            # nominal subtyping currently ignores '__init__' and '__new__' signatures
            if member in ("__init__", "__new__"):
                continue
            ignore_names = member != "__call__"  # __call__ can be passed kwargs
            # The third argument below indicates to what self type is bound.
            # We always bind self to the subtype. (Similarly to nominal types).
            supertype = get_proper_type(find_member(member, right, left))
            assert supertype is not None
            subtype = get_proper_type(find_member(member, left, left))
            # Useful for debugging:
            # print(member, 'of', left, 'has type', subtype)
            # print(member, 'of', right, 'has type', supertype)
            if not subtype:
                return False
            if isinstance(subtype, PartialType):
                subtype = (
                    NoneType()
                    if subtype.type is None
                    else Instance(
                        subtype.type,
                        [AnyType(TypeOfAny.unannotated)] * len(subtype.type.type_vars),
                    )
                )
            if not proper_subtype:
                # Nominal check currently ignores arg names
                # NOTE: If we ever change this, be sure to also change the call to
                # SubtypeVisitor.build_subtype_kind(...) down below.
                is_compat = is_subtype(
                    subtype, supertype, ignore_pos_arg_names=ignore_names
                )
            else:
                is_compat = is_proper_subtype(subtype, supertype)
            if not is_compat:
                return False
            if isinstance(subtype, NoneType) and isinstance(supertype, CallableType):
                # We want __hash__ = None idiom to work even without --strict-optional
                return False
            subflags = get_member_flags(member, left.type)
            superflags = get_member_flags(member, right.type)
            if IS_SETTABLE in superflags:
                # Check opposite direction for settable attributes.
                if not is_subtype(supertype, subtype):
                    return False
            if (IS_CLASSVAR in subflags) != (IS_CLASSVAR in superflags):
                return False
            if IS_SETTABLE in superflags and IS_SETTABLE not in subflags:
                return False
            # This rule is copied from nominal check in checker.py
            if IS_CLASS_OR_STATIC in superflags and IS_CLASS_OR_STATIC not in subflags:
                return False

    if not proper_subtype:
        # Nominal check currently ignores arg names, but __call__ is special for protocols
        ignore_names = right.type.protocol_members != ["__call__"]
        subtype_kind = SubtypeVisitor.build_subtype_kind(
            ignore_pos_arg_names=ignore_names
        )
    else:
        subtype_kind = ProperSubtypeVisitor.build_subtype_kind()
    TypeState.record_subtype_cache_entry(subtype_kind, left, right)
    return True