@ApiStatus.Internal
  @Nullable
  public static PyType parameterizeType(@NotNull PyType genericType,
                                        @NotNull List<PyType> actualTypeParams,
                                        @NotNull TypeEvalContext context) {
    Generics typeParams = collectGenerics(genericType, context);
    if (!typeParams.isEmpty()) {
      var substitutions = new GenericSubstitutions();
      List<PyType> expectedTypeParams = new ArrayList<>(new LinkedHashSet<>(typeParams.getAllTypeParameters()));
      PyTypeParameterMapping mapping = PyTypeParameterMapping.mapByShape(expectedTypeParams, actualTypeParams, Option.MAP_UNMATCHED_EXPECTED_TYPES_TO_ANY);
      if (mapping != null) {
        for (Couple<PyType> pair : mapping.getMappedTypes()) {
          if (pair.getFirst() instanceof PyTypeVarType typeVar) {
            substitutions.typeVars.put(typeVar, pair.getSecond());
          }
          else if (pair.getFirst() instanceof PyTypeVarTupleType typeVarTuple) {
            assert pair.getSecond() instanceof PyVariadicType;
            substitutions.typeVarTuples.put(typeVarTuple, (PyVariadicType)pair.getSecond());
          }
        }
      }
      return substitute(genericType, substitutions, context);
    }
    // An already parameterized type, don't override existing values for type parameters
    else if (genericType instanceof PyCollectionType) {
      return genericType;
    }
    else if (genericType instanceof PyClassType) {
      PyClass cls = ((PyClassType)genericType).getPyClass();
      return new PyCollectionTypeImpl(cls, false, actualTypeParams);
    }
    else {
      return null;
    }
  }
@ApiStatus.Internal
  @Nullable
  public static PyType parameterizeType(@NotNull PyType genericType,
                                        @NotNull List<PyType> actualTypeParams,
                                        @NotNull TypeEvalContext context)