public static boolean isSubclass(@NotNull PyClass subClass, @NotNull String superClassName, boolean inherited, @Nullable TypeEvalContext context) {
    if (PyNames.CALLABLE.equals(superClassName)) {
      return hasMethod(subClass, PyNames.CALL, inherited, context);
    }
    if (PyNames.HASHABLE.equals(superClassName)) {
      return hasMethod(subClass, PyNames.HASH, inherited, context);
    }
    final boolean hasIter = hasMethod(subClass, PyNames.ITER, inherited, context);
    final boolean hasGetItem = hasMethod(subClass, PyNames.GETITEM, inherited, context);
    if (PyNames.ITERABLE.equals(superClassName)) {
      return hasIter || hasGetItem;
    }
    if (PyNames.ITERATOR.equals(superClassName)) {
      return (hasIter && (hasMethod(subClass, PyNames.NEXT, inherited, context) || hasMethod(subClass,
                                                                                          PyNames.DUNDER_NEXT, inherited, context))) || hasGetItem;
    }
    final boolean isSized = hasMethod(subClass, PyNames.LEN, inherited, context);
    if (PyNames.SIZED.equals(superClassName)) {
      return isSized;
    }
    final boolean isContainer = hasMethod(subClass, PyNames.CONTAINS, inherited, context);
    if (PyNames.CONTAINER.equals(superClassName)) {
      return isContainer;
    }
    if (PyNames.SEQUENCE.equals(superClassName)) {
      return isSized && hasIter && isContainer && hasGetItem;
    }
    if (PyNames.MAPPING.equals(superClassName)) {
      return isSized && hasIter && isContainer && hasGetItem && hasMethod(subClass, PyNames.KEYS, inherited, context);
    }
    if (PyNames.MUTABLE_MAPPING.equals(superClassName)) {
      final boolean hasSetItem = hasMethod(subClass, PyNames.SETITEM, inherited, context);
      final boolean hasUpdate = hasMethod(subClass, PyNames.UPDATE, inherited, context);
      return isSized && hasIter && isContainer && hasGetItem && hasSetItem && hasUpdate;
    }
    if (PyNames.ABC_SET.equals(superClassName)) {
      return isSized && hasIter && isContainer;
    }
    if (PyNames.ABC_MUTABLE_SET.equals(superClassName)) {
      return isSized && hasIter && isContainer &&
             hasMethod(subClass, "discard", inherited, context) &&
             hasMethod(subClass, "add", inherited, context);
    }
    if (PyNames.ABC_COMPLEX.equals(superClassName)) {
      return hasMethod(subClass, PyNames.COMPLEX, inherited, context);
    }
    if (PyNames.ABC_REAL.equals(superClassName)) {
      return hasMethod(subClass, PyNames.FLOAT, inherited, context);
    }
    if (PyNames.ABC_INTEGRAL.equals(superClassName)) {
      return hasMethod(subClass, PyNames.INT, inherited, context);
    }
    if (PyNames.ABC_NUMBER.equals(superClassName) && "Decimal".equals(subClass.getName())) {
      return true;
    }
    if (PyNames.ASYNC_ITERABLE.equals(superClassName)) {
      return hasMethod(subClass, PyNames.AITER, inherited, context);
    }
    if (PyNames.AWAITABLE.equals(superClassName)) {
      return hasMethod(subClass, PyNames.DUNDER_AWAIT, inherited, context);
    }
    return false;
  }
public static boolean isSubclass(@NotNull PyClass subClass, @NotNull String superClassName, boolean inherited, @Nullable TypeEvalContext context)