@Nullable
  @Override
  public PyType getCallType(
      @NotNull TypeEvalContext context, @NotNull PyCallSiteExpression callSite) {
    for (PyTypeProvider typeProvider : Extensions.getExtensions(PyTypeProvider.EP_NAME)) {
      final Ref<PyType> typeRef = typeProvider.getCallType(this, callSite, context);
      if (typeRef != null) {
        return derefType(typeRef, typeProvider);
      }
    }

    final PyExpression receiver = PyTypeChecker.getReceiver(callSite, this);
    final Map<PyExpression, PyNamedParameter> mapping =
        PyCallExpressionHelper.mapArguments(callSite, this, context);
    return getCallType(receiver, mapping, context);
  }
 @Nullable
 @Override
 public PyType getCallType(
     @NotNull TypeEvalContext context, @NotNull PyCallSiteExpression callSite) {
   for (PyTypeProvider typeProvider : Extensions.getExtensions(PyTypeProvider.EP_NAME)) {
     final PyType type = typeProvider.getCallType(this, callSite, context);
     if (type != null) {
       type.assertValid(typeProvider.toString());
       return type;
     }
   }
   final PyExpression receiver = PyTypeChecker.getReceiver(callSite, this);
   final List<PyExpression> arguments = PyTypeChecker.getArguments(callSite, this);
   final List<PyParameter> parameters = PyUtil.getParameters(this, context);
   final PyResolveContext resolveContext =
       PyResolveContext.noImplicits().withTypeEvalContext(context);
   final List<PyParameter> explicitParameters =
       PyTypeChecker.filterExplicitParameters(parameters, this, callSite, resolveContext);
   final Map<PyExpression, PyNamedParameter> mapping =
       PyCallExpressionHelper.mapArguments(arguments, explicitParameters);
   return getCallType(receiver, mapping, context);
 }
 @Nullable
 private PsiElement resolveToDocStringOwner() {
   // here the ^Q target is already resolved; the resolved element may point to intermediate
   // assignments
   if (myElement instanceof PyTargetExpression) {
     final String targetName = myElement.getText();
     myReassignmentChain.addWith(
         TagSmall, $(PyBundle.message("QDOC.assigned.to.$0", targetName)).addItem(BR));
     final PyExpression assignedValue = ((PyTargetExpression) myElement).findAssignedValue();
     if (assignedValue instanceof PyReferenceExpression) {
       final PsiElement resolved = resolveWithoutImplicits((PyReferenceExpression) assignedValue);
       if (resolved != null) {
         return resolved;
       }
     }
     return assignedValue;
   }
   if (myElement instanceof PyReferenceExpression) {
     myReassignmentChain.addWith(
         TagSmall, $(PyBundle.message("QDOC.assigned.to.$0", myElement.getText())).addItem(BR));
     return resolveWithoutImplicits((PyReferenceExpression) myElement);
   }
   // it may be a call to a standard wrapper
   if (myElement instanceof PyCallExpression) {
     final PyCallExpression call = (PyCallExpression) myElement;
     final Pair<String, PyFunction> wrapInfo =
         PyCallExpressionHelper.interpretAsModifierWrappingCall(call, myOriginalElement);
     if (wrapInfo != null) {
       String wrapperName = wrapInfo.getFirst();
       PyFunction wrappedFunction = wrapInfo.getSecond();
       myReassignmentChain.addWith(
           TagSmall, $(PyBundle.message("QDOC.wrapped.in.$0", wrapperName)).addItem(BR));
       return wrappedFunction;
     }
   }
   return myElement;
 }