TypeModel (@Nullable PyType type, boolean allowUnions) { final TypeModel evaluated = myVisited.get(type); if (evaluated != null) { return evaluated; } if (myVisited.containsKey(type)) { //already evaluating? return NamedType.nameOrAny(type); } myVisited.put(type, null); //mark as evaluating TypeModel result = null; if (type instanceof PyTypedDictType typedDictType) { if (typedDictType.isInferred()) { return build(new PyCollectionTypeImpl(typedDictType.getPyClass(), false, typedDictType.getElementTypes()), allowUnions); } else { result = NamedType.nameOrAny(type); } } else if (type instanceof PyInstantiableType && ((PyInstantiableType<?>)type).isDefinition()) { final PyInstantiableType instanceType = ((PyInstantiableType<?>)type).toInstance(); // Special case: render Type[type] as just type if (type instanceof PyClassType && instanceType.equals(PyBuiltinCache.getInstance(((PyClassType)type).getPyClass()).getTypeType())) { result = NamedType.nameOrAny(type); } else { result = new ClassObjectType(build(instanceType, allowUnions)); } } else if (type instanceof PyNamedTupleType) { result = NamedType.nameOrAny(type); } else if (type instanceof PyTupleType tupleType) { final List<PyType> elementTypes = tupleType.isHomogeneous() ? Collections.singletonList(tupleType.getIteratedItemType()) : tupleType.getElementTypes(); boolean useTypingAlias = PyiUtil.getOriginalLanguageLevel(tupleType.getPyClass()).isOlderThan(LanguageLevel.PYTHON39); final List<TypeModel> elementModels = ContainerUtil.map(elementTypes, elementType -> build(elementType, true)); result = new TupleType(elementModels, tupleType.isHomogeneous(), useTypingAlias); } else if (type instanceof PyCollectionType asCollection) { final List<TypeModel> elementModels = new ArrayList<>(); for (PyType elementType : asCollection.getElementTypes()) { elementModels.add(build(elementType, true)); } if (!elementModels.isEmpty()) { final TypeModel collectionType = build(new PyClassTypeImpl(asCollection.getPyClass(), asCollection.isDefinition()), false); boolean useTypingAlias = PyiUtil.getOriginalLanguageLevel(asCollection.getPyClass()).isOlderThan(LanguageLevel.PYTHON39); result = new CollectionOf(collectionType, elementModels, useTypingAlias); } } else if (type instanceof PyUnionType unionType && allowUnions) { final Collection<PyType> unionMembers = unionType.getMembers(); final Pair<List<PyLiteralType>, List<PyType>> literalsAndOthers = extractLiterals(unionType); final Ref<PyType> optionalType = getOptionalType(unionType); if (literalsAndOthers != null) { final OneOfLiterals oneOfLiterals = new OneOfLiterals(literalsAndOthers.first); if (!literalsAndOthers.second.isEmpty()) { final List<TypeModel> otherTypeModels = ContainerUtil.map(literalsAndOthers.second, t -> build(t, false)); result = new OneOf(ContainerUtil.prepend(otherTypeModels, oneOfLiterals), PyTypingTypeProvider.isBitwiseOrUnionAvailable(myContext)); } else { result = oneOfLiterals; } } else if (optionalType != null) { result = new OptionalType(build(optionalType.get(), true), PyTypingTypeProvider.isBitwiseOrUnionAvailable(myContext)); } else if (type instanceof PyDynamicallyEvaluatedType || PyTypeChecker.isUnknown(type, false, myContext)) { result = new UnknownType(build(unionType.excludeNull(), true), PyTypingTypeProvider.isBitwiseOrUnionAvailable(myContext)); } else if (ContainerUtil.all(unionMembers, t -> t instanceof PyClassType && ((PyClassType)t).isDefinition())) { final List<TypeModel> instanceTypes = ContainerUtil.map(unionMembers, t -> build(((PyClassType)t).toInstance(), allowUnions)); result = new ClassObjectType(new OneOf(instanceTypes, PyTypingTypeProvider.isBitwiseOrUnionAvailable(myContext))); } else { result = new OneOf(Collections2.transform(unionMembers, t -> build(t, false)), PyTypingTypeProvider.isBitwiseOrUnionAvailable(myContext)); } } else if (type instanceof PyCallableType && !(type instanceof PyClassLikeType)) { result = buildCallable((PyCallableType)type); } else if (type instanceof PyGenericType) { result = new GenericType(type.getName()); } if (result == null) { result = NamedType.nameOrAny(type); } myVisited.put(type, result); return result; }