PY-81288 Incorrect type inferred for a function with positional only parameters

GitOrigin-RevId: 3bfea7064073b32743d58bbf5030769d1565a61e
This commit is contained in:
Petr
2025-06-28 13:01:14 +00:00
committed by intellij-monorepo-bot
parent 71a48c9ee1
commit 7135789b0f
32 changed files with 278 additions and 222 deletions
@@ -1,6 +1,7 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.python.community.plugin.java.psi.impl;
import com.intellij.openapi.util.NlsSafe;
import com.intellij.psi.*;
import com.intellij.util.ProcessingContext;
import com.intellij.util.Processor;
@@ -116,7 +117,7 @@ public class PyJavaClassType implements PyClassLikeType {
}
@Override
public @Nullable String getClassQName() {
public @Nullable @NlsSafe String getClassQName() {
return myClass.getQualifiedName();
}
@@ -91,7 +91,7 @@ public final @NonNls class PyNames {
public static final String SUPER = "super";
public static final String OBJECT = "object";
public static final String NONE = "None";
public static final @NlsSafe String NONE = "None";
public static final String TRUE = "True";
public static final String FALSE = "False";
public static final String ELLIPSIS = "...";
@@ -4,6 +4,7 @@ package com.jetbrains.python.psi.types;
import com.intellij.psi.PsiElement;
import com.jetbrains.python.psi.PyExpression;
import com.jetbrains.python.psi.PyParameter;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -50,6 +51,12 @@ public interface PyCallableParameter {
boolean isSelf();
@ApiStatus.Experimental
boolean isPositionOnlySeparator();
@ApiStatus.Experimental
boolean isKeywordOnlySeparator();
/**
* @param includeDefaultValue if true, include the default value after an "=".
* @return canonical representation of parameter.
@@ -1,6 +1,7 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.jetbrains.python.psi.types;
import com.intellij.openapi.util.NlsSafe;
import com.intellij.psi.PsiElement;
import com.intellij.util.Processor;
import com.jetbrains.python.psi.AccessDirection;
@@ -17,6 +18,7 @@ import java.util.Set;
public interface PyClassLikeType extends PyCallableType, PyWithAncestors, PyInstantiableType<PyClassLikeType> {
@Nullable
@NlsSafe
String getClassQName();
@NotNull
@@ -18,6 +18,7 @@ package com.jetbrains.python;
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.openapi.util.NlsSafe;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.QualifiedName;
@@ -109,7 +110,7 @@ public final class PyCustomType implements PyClassLikeType {
}
@Override
public @Nullable String getClassQName() {
public @Nullable @NlsSafe String getClassQName() {
return myQualifiedName;
}
@@ -217,11 +217,12 @@ public final class PyAnnotateVariableTypeIntention extends PyBaseIntentionAction
final PyType inferredType = getInferredTypeOrObject(target, context);
PyTypeHintGenerationUtil.checkPep484Compatibility(inferredType, context);
final String annotationText = PythonDocumentationProvider.getTypeHint(inferredType, context);
final AnnotationInfo info = new AnnotationInfo(annotationText, inferredType);
final String fqnTypeExr = PythonDocumentationProvider.getFullyQualifiedTypeHint(inferredType, context);
final AnnotationInfo info = new AnnotationInfo(annotationText, fqnTypeExr);
if (isInstanceAttribute(target, context)) {
final List<PyTargetExpression> classLevelAttrs = findClassLevelDefinitions(target, context);
if (classLevelAttrs.isEmpty()) {
PyTypeHintGenerationUtil.insertStandaloneAttributeAnnotation(target, context, info, true);
PyTypeHintGenerationUtil.insertStandaloneAttributeAnnotation(target, info, true);
}
else {
PyTypeHintGenerationUtil.insertVariableAnnotation(classLevelAttrs.get(0), context, info, true);
@@ -238,15 +239,15 @@ public final class PyAnnotateVariableTypeIntention extends PyBaseIntentionAction
if (isInstanceAttribute(target, context)) {
final List<PyTargetExpression> classLevelAttrs = findClassLevelDefinitions(target, context);
if (classLevelAttrs.isEmpty()) {
PyTypeHintGenerationUtil.insertStandaloneAttributeTypeComment(target, context, info, true);
PyTypeHintGenerationUtil.insertStandaloneAttributeTypeComment(target, info, true);
}
else {
// Use existing class level definition (say, assignment of the default value) for annotation
PyTypeHintGenerationUtil.insertVariableTypeComment(classLevelAttrs.get(0), context, info, true);
PyTypeHintGenerationUtil.insertVariableTypeComment(classLevelAttrs.get(0), info, true);
}
}
else {
PyTypeHintGenerationUtil.insertVariableTypeComment(target, context, info, true);
PyTypeHintGenerationUtil.insertVariableTypeComment(target, info, true);
}
}
@@ -255,21 +256,21 @@ public final class PyAnnotateVariableTypeIntention extends PyBaseIntentionAction
assert validTargetParent != null;
final PsiElement topmostTarget = PsiTreeUtil.findPrevParent(validTargetParent, target);
final StringBuilder builder = new StringBuilder();
final List<PyType> types = new ArrayList<>();
final List<String> fullyQualifiedTypeHints = new ArrayList<>();
final ArrayList<TextRange> typeRanges = new ArrayList<>();
generateNestedTypeHint(topmostTarget, context, builder, types, typeRanges);
return new AnnotationInfo(builder.toString(), types, typeRanges);
generateNestedTypeHint(topmostTarget, context, builder, fullyQualifiedTypeHints, typeRanges);
return new AnnotationInfo(builder.toString(), fullyQualifiedTypeHints, typeRanges);
}
private static void generateNestedTypeHint(@NotNull PsiElement target,
@NotNull TypeEvalContext context,
@NotNull StringBuilder builder,
@NotNull List<PyType> types,
@NotNull List<String> fullyQualifiedTypeHints,
@NotNull List<TextRange> typeRanges) {
if (target instanceof PyParenthesizedExpression) {
final PyExpression contained = ((PyParenthesizedExpression)target).getContainedExpression();
if (contained != null) {
generateNestedTypeHint(contained, context, builder, types, typeRanges);
generateNestedTypeHint(contained, context, builder, fullyQualifiedTypeHints, typeRanges);
}
}
else if (target instanceof PyTupleExpression) {
@@ -279,7 +280,7 @@ public final class PyAnnotateVariableTypeIntention extends PyBaseIntentionAction
if (i > 0) {
builder.append(", ");
}
generateNestedTypeHint(elements[i], context, builder, types, typeRanges);
generateNestedTypeHint(elements[i], context, builder, fullyQualifiedTypeHints, typeRanges);
}
builder.append(")");
}
@@ -287,7 +288,7 @@ public final class PyAnnotateVariableTypeIntention extends PyBaseIntentionAction
final PyType singleTargetType = getInferredTypeOrObject((PyTypedElement)target, context);
PyTypeHintGenerationUtil.checkPep484Compatibility(singleTargetType, context);
final String singleTargetAnnotation = PythonDocumentationProvider.getTypeHint(singleTargetType, context);
types.add(singleTargetType);
fullyQualifiedTypeHints.add(PythonDocumentationProvider.getFullyQualifiedTypeHint(singleTargetType, context));
typeRanges.add(TextRange.from(builder.length(), singleTargetAnnotation.length()));
builder.append(singleTargetAnnotation);
}
@@ -14,19 +14,17 @@ import com.intellij.openapi.util.ThrowableComputable;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.QualifiedName;
import com.intellij.util.ThrowableRunnable;
import com.intellij.util.containers.ContainerUtil;
import com.jetbrains.python.PyPsiBundle;
import com.jetbrains.python.PythonUiService;
import com.jetbrains.python.ast.impl.PyUtilCore;
import com.jetbrains.python.codeInsight.imports.AddImportHelper;
import com.jetbrains.python.codeInsight.imports.AddImportHelper.ImportPriority;
import com.jetbrains.python.codeInsight.typing.PyTypingTypeProvider;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.impl.PyPsiUtils;
import com.jetbrains.python.psi.resolve.PyQualifiedNameResolveContext;
import com.jetbrains.python.psi.resolve.PyResolveImportUtil;
import com.jetbrains.python.psi.types.*;
import com.jetbrains.python.psi.types.PyRecursiveTypeVisitor.PyTypeTraverser;
import com.jetbrains.python.psi.types.PyRecursiveTypeVisitor.Traversal;
import com.jetbrains.python.refactoring.PyPsiRefactoringUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -46,7 +44,6 @@ public final class PyTypeHintGenerationUtil {
private PyTypeHintGenerationUtil() { }
public static void insertStandaloneAttributeTypeComment(@NotNull PyTargetExpression target,
@NotNull TypeEvalContext context,
AnnotationInfo info,
boolean startTemplate) {
@@ -69,7 +66,7 @@ public final class PyTypeHintGenerationUtil {
PsiComment insertedComment = as(inserted.getLastChild(), PsiComment.class);
if (insertedComment == null) return;
addImportsForTypeAnnotations(info.getTypes(), context, target.getContainingFile());
addImportsForTypeAnnotations(info.getFullyQualifiedTypeHints(), target);
insertedComment = CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(insertedComment);
if (startTemplate && insertedComment != null) {
@@ -79,7 +76,6 @@ public final class PyTypeHintGenerationUtil {
}
public static void insertStandaloneAttributeAnnotation(@NotNull PyTargetExpression target,
@NotNull TypeEvalContext context,
@NotNull AnnotationInfo info,
boolean startTemplate) {
final LanguageLevel langLevel = LanguageLevel.forElement(target);
@@ -102,7 +98,7 @@ public final class PyTypeHintGenerationUtil {
WriteAction.run(() -> {
PyTypeDeclarationStatement inserted = (PyTypeDeclarationStatement)pyClass.getStatementList().addAfter(declaration, anchorBefore);
addImportsForTypeAnnotations(info.getTypes(), context, target.getContainingFile());
addImportsForTypeAnnotations(info.getFullyQualifiedTypeHints(), target);
inserted = CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(inserted);
if (startTemplate && inserted != null) {
@@ -158,7 +154,7 @@ public final class PyTypeHintGenerationUtil {
if (createdAnnotationOwner == null) return;
if (context != null) {
addImportsForTypeAnnotations(info.getTypes(), context, target.getContainingFile());
addImportsForTypeAnnotations(info.getFullyQualifiedTypeHints(), target);
}
createdAnnotationOwner = CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(createdAnnotationOwner);
@@ -197,7 +193,6 @@ public final class PyTypeHintGenerationUtil {
}
public static void insertVariableTypeComment(@NotNull PyTargetExpression target,
TypeEvalContext context,
@NotNull AnnotationInfo info,
boolean startTemplate) {
if (!FileModificationService.getInstance().preparePsiElementForWrite(target)) return;
@@ -244,7 +239,7 @@ public final class PyTypeHintGenerationUtil {
PsiComment insertedComment = target.getTypeComment();
if (insertedComment == null) return;
addImportsForTypeAnnotations(info.getTypes(), context, target.getContainingFile());
addImportsForTypeAnnotations(info.getFullyQualifiedTypeHints(), target);
insertedComment = CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(insertedComment);
if (startTemplate && insertedComment != null) {
@@ -277,124 +272,39 @@ public final class PyTypeHintGenerationUtil {
}
}
public static void addImportsForTypeAnnotations(@NotNull List<PyType> types,
@NotNull TypeEvalContext context,
@NotNull PsiFile file) {
public static void addImportsForTypeAnnotations(@NotNull List<String> types, @NotNull PsiElement anchor) {
final Set<PsiNamedElement> symbols = new LinkedHashSet<>();
final Set<String> namesFromTyping = new LinkedHashSet<>();
for (PyType type : types) {
collectImportTargetsFromType(type, context, symbols, namesFromTyping);
}
final boolean builtinTyping = LanguageLevel.forElement(file).isAtLeast(LanguageLevel.PYTHON35);
final ImportPriority priority = builtinTyping ? ImportPriority.BUILTIN : ImportPriority.THIRD_PARTY;
for (String name : namesFromTyping) {
AddImportHelper.addOrUpdateFromImportStatement(file, "typing", name, null, priority, null);
for (String type : types) {
collectImportTargetsFromTypeExpression(type, anchor, symbols);
}
PsiFile file = anchor.getContainingFile();
for (PsiNamedElement symbol : symbols) {
PyPsiRefactoringUtil.insertImport(file, symbol, null, true);
}
}
private static void collectImportTargetsFromType(@Nullable PyType type,
@NotNull TypeEvalContext context,
@NotNull Set<PsiNamedElement> symbols,
@NotNull Set<String> typingTypes) {
boolean useGenericAliasFromTyping =
context.getOrigin() != null && LanguageLevel.forElement(context.getOrigin()).isOlderThan(LanguageLevel.PYTHON39);
PyRecursiveTypeVisitor.traverse(type, context, new PyTypeTraverser() {
private static void collectImportTargetsFromTypeExpression(@NotNull String typeExpressionText,
@NotNull PsiElement anchor,
@NotNull Set<PsiNamedElement> symbols) {
PyExpression typeExpression = PyUtil.createExpressionFromFragment(typeExpressionText, anchor);
assert typeExpression != null;
PyQualifiedNameResolveContext qNameResolveContext = PyResolveImportUtil.fromFoothold(anchor);
typeExpression.accept(new PyRecursiveElementVisitor() {
@Override
public @NotNull Traversal visitUnknownType() {
typingTypes.add("Any");
return Traversal.CONTINUE;
}
@Override
public @NotNull Traversal visitPyUnionType(@NotNull PyUnionType unionType) {
final Collection<PyType> members = unionType.getMembers();
final boolean isOptional = members.size() == 2 && ContainerUtil.exists(members, member -> isNoneType(member));
if (!PyTypingTypeProvider.isBitwiseOrUnionAvailable(context)) {
typingTypes.add(isOptional ? "Optional" : "Union");
}
return Traversal.CONTINUE;
}
@Override
public @NotNull Traversal visitPyNeverType(@NotNull PyNeverType neverType) {
typingTypes.add(neverType.getName());
return Traversal.CONTINUE;
}
@Override
public @NotNull Traversal visitPyNamedTupleType(@NotNull PyNamedTupleType namedTupleType) {
final PyQualifiedNameOwner element = namedTupleType.getDeclarationElement();
if (element instanceof PsiNamedElement) {
symbols.add((PsiNamedElement)element);
}
addTypingTypeIfNeeded(namedTupleType);
return Traversal.CONTINUE;
}
@Override
public @NotNull Traversal visitPyGenericType(@NotNull PyCollectionType genericType) {
final PyClass pyClass = genericType.getPyClass();
final String typingCollectionName = PyTypingTypeProvider.TYPING_COLLECTION_CLASSES.get(pyClass.getQualifiedName());
if (typingCollectionName != null && genericType.isBuiltin() && useGenericAliasFromTyping) {
typingTypes.add(typingCollectionName);
}
else {
symbols.add(pyClass);
}
addTypingTypeIfNeeded(genericType);
return Traversal.CONTINUE;
}
@Override
public @NotNull Traversal visitPyTupleType(@NotNull PyTupleType tupleType) {
if (useGenericAliasFromTyping) {
typingTypes.add("Tuple");
}
return Traversal.CONTINUE;
}
@Override
public @NotNull Traversal visitPyTypedDictType(@NotNull PyTypedDictType typedDictType) {
symbols.add((PsiNamedElement)typedDictType.getDeclarationElement());
// Don't go through its type arguments
return Traversal.PRUNE;
}
@Override
public @NotNull Traversal visitPyClassType(@NotNull PyClassType classType) {
if (!isNoneType(classType)) {
symbols.add(classType.getPyClass());
addTypingTypeIfNeeded(classType);
}
return Traversal.CONTINUE;
}
@Override
public @NotNull Traversal visitPyCallableType(@NotNull PyCallableType callableType) {
typingTypes.add("Callable");
return Traversal.CONTINUE;
}
@Override
public @NotNull Traversal visitPyTypeParameterType(@NotNull PyTypeParameterType typeParameterType) {
final PyTargetExpression target = as(typeParameterType.getDeclarationElement(), PyTargetExpression.class);
if (target != null) {
symbols.add(target);
}
addTypingTypeIfNeeded(typeParameterType);
return Traversal.PRUNE;
}
private void addTypingTypeIfNeeded(@NotNull PyType type) {
if (useGenericAliasFromTyping && type instanceof PyInstantiableType<?> instantiableType && instantiableType.isDefinition()) {
typingTypes.add("Type");
public void visitPyReferenceExpression(@NotNull PyReferenceExpression node) {
if (node.isQualified()) {
QualifiedName qualifiedName = node.asQualifiedName();
if (qualifiedName != null) {
PsiElement element = PyResolveImportUtil.resolveTopLevelMember(qualifiedName, qNameResolveContext);
if (element instanceof PsiNamedElement namedElement) {
symbols.add(namedElement);
return;
}
}
}
super.visitPyReferenceExpression(node);
}
});
}
@@ -442,20 +352,22 @@ public final class PyTypeHintGenerationUtil {
public static final class AnnotationInfo {
private final String myAnnotationText;
private final List<PyType> myTypes;
private final List<String> myFullyQualifiedTypeHints;
private final List<TextRange> myTypeRanges;
public AnnotationInfo(@NotNull String annotationText) {
this(annotationText, Collections.emptyList(), Collections.singletonList(TextRange.allOf(annotationText)));
}
public AnnotationInfo(@NotNull String annotationText, @Nullable PyType type) {
this(annotationText, Collections.singletonList(type), Collections.singletonList(TextRange.allOf(annotationText)));
public AnnotationInfo(@NotNull String annotationText, @NotNull String fullyQualifiedTypeHint) {
this(annotationText, Collections.singletonList(fullyQualifiedTypeHint), Collections.singletonList(TextRange.allOf(annotationText)));
}
public AnnotationInfo(@NotNull String annotationText, @NotNull List<PyType> types, @NotNull List<TextRange> typeRanges) {
public AnnotationInfo(@NotNull String annotationText,
@NotNull List<String> fullyQualifiedTypeHints,
@NotNull List<TextRange> typeRanges) {
myAnnotationText = annotationText;
myTypes = types;
myFullyQualifiedTypeHints = fullyQualifiedTypeHints;
myTypeRanges = typeRanges;
}
@@ -463,8 +375,8 @@ public final class PyTypeHintGenerationUtil {
return myAnnotationText;
}
public @NotNull List<PyType> getTypes() {
return myTypes;
public @NotNull List<String> getFullyQualifiedTypeHints() {
return myFullyQualifiedTypeHints;
}
public @NotNull List<TextRange> getTypeRanges() {
@@ -165,7 +165,10 @@ public final class SpecifyTypeInPy3AnnotationsIntention extends TypeIntention {
if (function.isAsync()) {
inferredType = Ref.deref(PyTypingTypeProvider.unwrapCoroutineReturnType(inferredType));
}
return new AnnotationInfo(PythonDocumentationProvider.getTypeHint(inferredType, context), inferredType);
return new AnnotationInfo(
PythonDocumentationProvider.getTypeHint(inferredType, context),
PythonDocumentationProvider.getFullyQualifiedTypeHint(inferredType, context)
);
}
public static PyExpression annotateReturnType(Project project, PyFunction function, boolean createTemplate) {
@@ -174,9 +177,7 @@ public final class SpecifyTypeInPy3AnnotationsIntention extends TypeIntention {
final String returnTypeText = returnTypeAnnotation.getAnnotationText();
final String annotationText = "-> " + returnTypeText;
final PsiFile file = function.getContainingFile();
final TypeEvalContext context = TypeEvalContext.userInitiated(project, file);
PyTypeHintGenerationUtil.addImportsForTypeAnnotations(returnTypeAnnotation.getTypes(), context, file);
PyTypeHintGenerationUtil.addImportsForTypeAnnotations(returnTypeAnnotation.getFullyQualifiedTypeHints(), function);
PyFunction annotatedFunction = PyUtil.updateDocumentUnblockedAndCommitted(function, document -> {
final PyAnnotation oldAnnotation = function.getAnnotation();
@@ -58,7 +58,7 @@ import static com.jetbrains.python.psi.types.PyNoneTypeKt.isNoneType;
public final class PyTypingTypeProvider extends PyTypeProviderWithCustomContext<PyTypingTypeProvider.Context> {
public static final String TYPING = "typing";
public static final @NlsSafe String TYPING = "typing";
public static final String GENERATOR = "typing.Generator";
public static final String ASYNC_GENERATOR = "typing.AsyncGenerator";
@@ -163,7 +163,7 @@ public final class PyTypingTypeProvider extends PyTypeProviderWithCustomContext<
.put(CHAIN_MAP, "collections.ChainMap")
.build();
public static final ImmutableMap<String, String> TYPING_COLLECTION_CLASSES = ImmutableMap.<String, String>builder()
public static final ImmutableMap<String, @NlsSafe String> TYPING_COLLECTION_CLASSES = ImmutableMap.<String, String>builder()
.put("list", "List")
.put("dict", "Dict")
.put("set", "Set")
@@ -1,6 +1,7 @@
package com.jetbrains.python.documentation;
import com.intellij.openapi.editor.colors.TextAttributesKey;
import com.intellij.openapi.util.NlsSafe;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.text.HtmlBuilder;
import com.intellij.openapi.util.text.HtmlChunk;
@@ -8,10 +9,14 @@ import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.util.containers.ContainerUtil;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.ast.PyAstSingleStarParameter;
import com.jetbrains.python.ast.PyAstSlashParameter;
import com.jetbrains.python.codeInsight.typing.PyTypingTypeProvider;
import com.jetbrains.python.highlighting.PyHighlighter;
import com.jetbrains.python.psi.LanguageLevel;
import com.jetbrains.python.psi.PyExpression;
import com.jetbrains.python.psi.PyQualifiedNameOwner;
import com.jetbrains.python.psi.impl.PythonLanguageLevelPusher;
import com.jetbrains.python.psi.types.*;
import one.util.streamex.StreamEx;
@@ -32,16 +37,18 @@ public abstract class PyTypeRenderer extends PyTypeVisitorExt<@NotNull HtmlChunk
protected int myDepth = 0;
protected final @NotNull TypeEvalContext myTypeEvalContext;
protected final boolean myUseFqNames;
private PyTypeRenderer(@NotNull TypeEvalContext typeEvalContext) {
private PyTypeRenderer(@NotNull TypeEvalContext typeEvalContext, boolean useFqNames) {
myTypeEvalContext = typeEvalContext;
myUseFqNames = useFqNames;
}
static abstract class HtmlRenderer extends PyTypeRenderer {
private final @NotNull PsiElement myAnchor;
private HtmlRenderer(@NotNull TypeEvalContext typeEvalContext, @NotNull PsiElement anchor) {
super(typeEvalContext);
private HtmlRenderer(@NotNull TypeEvalContext typeEvalContext, @NotNull PsiElement anchor, boolean useFqNames) {
super(typeEvalContext, useFqNames);
myAnchor = anchor;
}
@@ -68,19 +75,19 @@ public abstract class PyTypeRenderer extends PyTypeVisitorExt<@NotNull HtmlChunk
static final class RichDocumentation extends HtmlRenderer {
RichDocumentation(@NotNull TypeEvalContext typeEvalContext, @NotNull PsiElement anchor) {
super(typeEvalContext, anchor);
super(typeEvalContext, anchor, false);
}
}
static final class Documentation extends PyTypeRenderer {
Documentation(@NotNull TypeEvalContext typeEvalContext) {
super(typeEvalContext);
super(typeEvalContext, false);
}
}
static final class VerboseDocumentation extends PyTypeRenderer {
VerboseDocumentation(@NotNull TypeEvalContext typeEvalContext) {
super(typeEvalContext);
super(typeEvalContext, false);
}
@Override
@@ -98,8 +105,8 @@ public abstract class PyTypeRenderer extends PyTypeVisitorExt<@NotNull HtmlChunk
}
public static final class TypeHint extends PyTypeRenderer {
public TypeHint(@NotNull TypeEvalContext typeEvalContext) {
super(typeEvalContext);
public TypeHint(@NotNull TypeEvalContext typeEvalContext, boolean useFqNames) {
super(typeEvalContext, useFqNames);
}
@Override
@@ -110,23 +117,55 @@ public abstract class PyTypeRenderer extends PyTypeVisitorExt<@NotNull HtmlChunk
@Override
public HtmlChunk visitPyCallableType(@NotNull PyCallableType callableType) {
HtmlBuilder result = new HtmlBuilder();
result.append(HtmlChunk.raw("Callable")); //NON-NLS
result.append(HtmlChunk.raw(myUseFqNames ? "typing.Callable" : "Callable")); //NON-NLS
result.append(styled("[", PyHighlighter.PY_BRACKETS));
result.append(styled("[", PyHighlighter.PY_BRACKETS));
List<PyCallableParameter> parameters = callableType.getParameters(myTypeEvalContext);
List<PyCallableParameter> parameters = getParameters(callableType);
if (parameters != null) {
result.append(styled("[", PyHighlighter.PY_BRACKETS));
result.append(renderList(ContainerUtil.map(parameters, this::visitPyCallableParameter)));
result.append(styled("]", PyHighlighter.PY_BRACKETS));
}
else {
result.append(styled("...", PyHighlighter.PY_DOT));
}
result.append(styled("]", PyHighlighter.PY_BRACKETS));
result.append(HtmlChunk.raw(", "));
result.append(render(callableType.getReturnType(myTypeEvalContext)));
result.append(styled("]", PyHighlighter.PY_BRACKETS));
return result.toFragment();
}
// Returns `callableType`'s parameter list (excluding terminating '/') if it can be expressed using typing.Callable, `null` otherwise.
// Parameters specified using typing.Callable are assumed to be positional-only (a parameter list must be terminated with '/'). There
// is no way to specify keyword-only or variadic parameters.
private @Nullable List<PyCallableParameter> getParameters(@NotNull PyCallableType callableType) {
List<PyCallableParameter> parameters = callableType.getParameters(myTypeEvalContext);
if (parameters == null) return null;
if (parameters.isEmpty()) return List.of();
for (int i = 0; i < parameters.size(); i++) {
PyCallableParameter parameter = parameters.get(i);
if (parameter.isPositionalContainer()) {
return null;
}
if (parameter.isKeywordOnlySeparator()) {
return null;
}
if (parameter.isPositionOnlySeparator()) {
if (i == parameters.size() - 1) {
return parameters.subList(0, i);
}
return null;
}
}
// TODO If CallableType is inferred from a 'Callable[]' type hint, there is no terminating '/' parameter.
// Check whether all parameters have no name then.
if (ContainerUtil.all(parameters, parameter -> parameter.getName() == null)) {
return parameters;
}
return null;
}
@Override
protected @NotNull HtmlChunk visitPyCallableParameter(@NotNull PyCallableParameter param) {
return render(param.getType(myTypeEvalContext));
@@ -184,12 +223,16 @@ public abstract class PyTypeRenderer extends PyTypeVisitorExt<@NotNull HtmlChunk
private @NotNull HtmlChunk renderGenericType(@NotNull PyCollectionType genericType) {
HtmlBuilder result = new HtmlBuilder();
boolean renderTypeArgumentList = !genericType.getElementTypes().isEmpty();
String className = genericType.getPyClass().getName();
String className = genericType.getName();
if (renderTypeArgumentList && !isGenericBuiltinsAvailable() && PyTypingTypeProvider.TYPING_COLLECTION_CLASSES.containsKey(className)) {
result.append(className(PyTypingTypeProvider.TYPING_COLLECTION_CLASSES.get(className))); // NON-NLS
className = PyTypingTypeProvider.TYPING_COLLECTION_CLASSES.get(className);
if (myUseFqNames) {
className = PyTypingTypeProvider.TYPING + "." + className;
}
result.append(className(className));
}
else {
result.append(className(className));
result.append(className(myUseFqNames ? genericType.getClassQName() : className));
}
if (renderTypeArgumentList) {
result.append(styled("[", PyHighlighter.PY_BRACKETS));
@@ -201,7 +244,8 @@ public abstract class PyTypeRenderer extends PyTypeVisitorExt<@NotNull HtmlChunk
protected @NotNull HtmlChunk wrapInTypingType(@NotNull HtmlChunk instanceTypeRender) {
return new HtmlBuilder()
.append(isGenericBuiltinsAvailable() ? styled("type", PyHighlighter.PY_BUILTIN_NAME) : escaped("Type")) //NON-NLS
.append(isGenericBuiltinsAvailable() ? styled("type", PyHighlighter.PY_BUILTIN_NAME) : //NON-NLS
escaped(myUseFqNames ? "typing.Type" : "Type")) //NON-NLS
.append(styled("[", PyHighlighter.PY_BRACKETS))
.append(instanceTypeRender)
.append(styled("]", PyHighlighter.PY_BRACKETS))
@@ -210,7 +254,7 @@ public abstract class PyTypeRenderer extends PyTypeVisitorExt<@NotNull HtmlChunk
@Override
public @NotNull HtmlChunk visitPyClassLikeType(@NotNull PyClassLikeType classLikeType) {
HtmlChunk classTypeRender = className(classLikeType.getName());
HtmlChunk classTypeRender = className(getTypeName(classLikeType));
return classLikeType.isDefinition() ? wrapInTypingType(classTypeRender) : classTypeRender;
}
@@ -218,10 +262,10 @@ public abstract class PyTypeRenderer extends PyTypeVisitorExt<@NotNull HtmlChunk
public HtmlChunk visitPyNarrowedType(@NotNull PyNarrowedType narrowedType) {
HtmlBuilder result = new HtmlBuilder();
if (narrowedType.getTypeIs()) {
result.append(styled("TypeIs", PyHighlighter.PY_CLASS_DEFINITION));
result.append(styled(myUseFqNames ? "typing.TypeIs" : "TypeIs", PyHighlighter.PY_CLASS_DEFINITION));
}
else {
result.append(styled("TypeGuard", PyHighlighter.PY_CLASS_DEFINITION));
result.append(styled(myUseFqNames ? "typing.TypeGuard" : "TypeGuard", PyHighlighter.PY_CLASS_DEFINITION));
}
result.append(styled("[", PyHighlighter.PY_BRACKETS));
result.append(render(narrowedType.getNarrowedType()));
@@ -262,7 +306,7 @@ public abstract class PyTypeRenderer extends PyTypeVisitorExt<@NotNull HtmlChunk
private @NotNull HtmlChunk renderUnionOfLiterals(@NotNull List<PyLiteralType> literals) {
return new HtmlBuilder()
.append(escaped("Literal")) //NON-NLS
.append(escaped(myUseFqNames ? "typing.Literal" : "Literal")) //NON-NLS
.append(styled("[", PyHighlighter.PY_BRACKETS))
.append(StreamEx
.of(literals)
@@ -279,7 +323,7 @@ public abstract class PyTypeRenderer extends PyTypeVisitorExt<@NotNull HtmlChunk
result.append(renderList(renderedUnionMembers, " | "));
}
else {
result.append(escaped("Union")); //NON-NLS
result.append(escaped(myUseFqNames ? "typing.Union" : "Union")); //NON-NLS
result.append(styled("[", PyHighlighter.PY_BRACKETS));
result.append(renderList(renderedUnionMembers));
result.append(styled("]", PyHighlighter.PY_BRACKETS));
@@ -296,7 +340,7 @@ public abstract class PyTypeRenderer extends PyTypeVisitorExt<@NotNull HtmlChunk
result.append(render(ContainerUtil.find(type.getMembers(), t -> isNoneType(t)))); //NON-NLS
}
else {
result.append(escaped("Optional")); //NON-NLS
result.append(escaped(myUseFqNames ? "typing.Optional" : "Optional")); //NON-NLS
result.append(styled("[", PyHighlighter.PY_BRACKETS));
result.append(render(ContainerUtil.find(type.getMembers(), t -> !isNoneType(t))));
result.append(styled("]", PyHighlighter.PY_BRACKETS));
@@ -326,7 +370,7 @@ public abstract class PyTypeRenderer extends PyTypeVisitorExt<@NotNull HtmlChunk
result.append(styled("tuple", PyHighlighter.PY_BUILTIN_NAME)); //NON-NLS
}
else {
result.append(escaped("Tuple")); //NON-NLS
result.append(escaped(myUseFqNames ? "typing.Tuple" : "Tuple")); //NON-NLS
}
result.append(styled("[", PyHighlighter.PY_BRACKETS));
if (!tupleType.getElementTypes().isEmpty()) {
@@ -345,7 +389,7 @@ public abstract class PyTypeRenderer extends PyTypeVisitorExt<@NotNull HtmlChunk
@Override
public HtmlChunk visitUnknownType() {
return HtmlChunk.raw("Any"); //NON-NLS
return HtmlChunk.raw(myUseFqNames ? "typing.Any" : "Any"); //NON-NLS
}
@Override
@@ -373,15 +417,23 @@ public abstract class PyTypeRenderer extends PyTypeVisitorExt<@NotNull HtmlChunk
@NotNull
protected HtmlChunk visitPyCallableParameter(@NotNull PyCallableParameter param) {
HtmlBuilder result = new HtmlBuilder();
PyType type = param.getType(myTypeEvalContext);
// TODO remove that
if (!(type instanceof PyParamSpecType) && !(type instanceof PyConcatenateType)) {
if (param.getName() != null) {
result.append(styled(param.getName(), PyHighlighter.PY_PARAMETER));
result.append(styled(": ", PyHighlighter.PY_OPERATION_SIGN));
if (param.isPositionOnlySeparator()) {
result.append(escaped(PyAstSlashParameter.TEXT));
}
else if (param.isKeywordOnlySeparator()) {
result.append(escaped(PyAstSingleStarParameter.TEXT));
}
else {
PyType type = param.getType(myTypeEvalContext);
// TODO remove that
if (!(type instanceof PyParamSpecType) && !(type instanceof PyConcatenateType)) {
if (param.getName() != null) {
result.append(styled(param.getName(), PyHighlighter.PY_PARAMETER));
result.append(styled(": ", PyHighlighter.PY_OPERATION_SIGN));
}
}
result.append(render(type));
}
result.append(render(type));
return result.toFragment();
}
@@ -405,7 +457,7 @@ public abstract class PyTypeRenderer extends PyTypeVisitorExt<@NotNull HtmlChunk
@Override
public @NotNull HtmlChunk visitPyTypeVarType(@NotNull PyTypeVarType typeVarType) {
HtmlChunk typeVarTypeRender = escaped(typeVarType.getName());
HtmlChunk typeVarTypeRender = escaped(getTypeName(typeVarType));
return typeVarType.isDefinition() ? wrapInTypingType(typeVarTypeRender) : typeVarTypeRender;
}
@@ -428,4 +480,15 @@ public abstract class PyTypeRenderer extends PyTypeVisitorExt<@NotNull HtmlChunk
result.append("]");
return result.toFragment();
}
protected final @Nullable @NlsSafe String getTypeName(@NotNull PyType type) {
if (isNoneType(type)) {
return PyNames.NONE;
}
if (myUseFqNames) {
PyQualifiedNameOwner declarationElement = type.getDeclarationElement();
return declarationElement != null ? declarationElement.getQualifiedName() : null;
}
return type.getName();
}
}
@@ -30,6 +30,7 @@ import com.jetbrains.python.psi.resolve.PyResolveContext;
import com.jetbrains.python.psi.resolve.QualifiedNameFinder;
import com.jetbrains.python.psi.types.*;
import one.util.streamex.StreamEx;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -341,7 +342,12 @@ public class PythonDocumentationProvider implements DocumentationProvider {
* Returns the provided type in PEP 484 compliant format.
*/
public static @NotNull String getTypeHint(@Nullable PyType type, @NotNull TypeEvalContext context) {
return PyTypeVisitor.visit(type, new PyTypeRenderer.TypeHint(context)).toString();
return PyTypeVisitor.visit(type, new PyTypeRenderer.TypeHint(context, false)).toString();
}
@ApiStatus.Experimental
public static @NotNull String getFullyQualifiedTypeHint(@Nullable PyType type, @NotNull TypeEvalContext context) {
return PyTypeVisitor.visit(type, new PyTypeRenderer.TypeHint(context, true)).toString();
}
/**
@@ -20,7 +20,6 @@ import com.intellij.modcommand.ModPsiUpdater;
import com.intellij.modcommand.Presentation;
import com.intellij.modcommand.PsiUpdateModCommandAction;
import com.intellij.psi.PsiComment;
import com.intellij.psi.PsiFile;
import com.jetbrains.python.PyPsiBundle;
import com.jetbrains.python.codeInsight.intentions.PyTypeHintGenerationUtil;
import com.jetbrains.python.codeInsight.typing.PyTypingTypeProvider;
@@ -36,13 +35,16 @@ import java.util.List;
public class PyMakeFunctionReturnTypeQuickFix extends PsiUpdateModCommandAction<PyFunction> {
private final String myReturnTypeName;
private final String myReturnTypeFqName;
public PyMakeFunctionReturnTypeQuickFix(@NotNull PyFunction function, @NotNull TypeEvalContext context) {
super(function);
myReturnTypeName = getReturnTypeName(function, context);
PyType type = getReturnType(function, context);
myReturnTypeName = PythonDocumentationProvider.getTypeHint(type, context);
myReturnTypeFqName = PythonDocumentationProvider.getFullyQualifiedTypeHint(type, context);
}
private static @NotNull String getReturnTypeName(@NotNull PyFunction function, @NotNull TypeEvalContext context) {
private static @Nullable PyType getReturnType(@NotNull PyFunction function, @NotNull TypeEvalContext context) {
PyType type = function.getInferredReturnType(context);
if (function.isAsync()) {
var unwrappedType = PyTypingTypeProvider.unwrapCoroutineReturnType(type);
@@ -50,7 +52,7 @@ public class PyMakeFunctionReturnTypeQuickFix extends PsiUpdateModCommandAction<
type = unwrappedType.get();
}
}
return PythonDocumentationProvider.getTypeHint(type, context);
return type;
}
@Override
@@ -67,14 +69,12 @@ public class PyMakeFunctionReturnTypeQuickFix extends PsiUpdateModCommandAction<
protected void invoke(@NotNull ActionContext context, @NotNull PyFunction function, @NotNull ModPsiUpdater updater) {
final PyElementGenerator elementGenerator = PyElementGenerator.getInstance(function.getProject());
boolean shouldAddImports = false;
PyAnnotation annotation = function.getAnnotation();
if (annotation != null) {
PyExpression annotationExpr = annotation.getValue();
if (annotationExpr != null) {
annotationExpr.replace(elementGenerator.createExpressionFromText(LanguageLevel.PYTHON34, myReturnTypeName));
shouldAddImports = true;
PyTypeHintGenerationUtil.addImportsForTypeAnnotations(List.of(myReturnTypeFqName), function);
}
}
@@ -85,22 +85,7 @@ public class PyMakeFunctionReturnTypeQuickFix extends PsiUpdateModCommandAction<
typeCommentAnnotation.append("-> ").append(myReturnTypeName);
typeComment.replace(
elementGenerator.createFromText(LanguageLevel.PYTHON27, PsiComment.class, typeCommentAnnotation.toString()));
shouldAddImports = true;
}
if (shouldAddImports) {
addImportsForTypeAnnotations(function);
}
}
private static void addImportsForTypeAnnotations(@NotNull PyFunction function) {
PsiFile file = function.getContainingFile();
if (file == null) return;
TypeEvalContext context = TypeEvalContext.userInitiated(function.getProject(), file);
PyType typeForImports = function.getInferredReturnType(context);
if (typeForImports != null) {
PyTypeHintGenerationUtil.addImportsForTypeAnnotations(List.of(typeForImports), context, file);
PyTypeHintGenerationUtil.addImportsForTypeAnnotations(List.of(myReturnTypeFqName), function);
}
}
}
@@ -21,10 +21,7 @@ import com.intellij.psi.PsiElement;
import com.intellij.util.ObjectUtils;
import com.intellij.util.containers.ContainerUtil;
import com.jetbrains.python.documentation.PythonDocumentationProvider;
import com.jetbrains.python.psi.PyExpression;
import com.jetbrains.python.psi.PyNamedParameter;
import com.jetbrains.python.psi.PyParameter;
import com.jetbrains.python.psi.PyUtil;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.impl.ParamHelper;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
@@ -161,6 +158,16 @@ public final class PyCallableParameterImpl implements PyCallableParameter {
return myElement != null && myElement.isSelf();
}
@Override
public boolean isPositionOnlySeparator() {
return myElement instanceof PySlashParameter;
}
@Override
public boolean isKeywordOnlySeparator() {
return myElement instanceof PySingleStarParameter;
}
@Override
public @NotNull String getPresentableText(boolean includeDefaultValue, @Nullable TypeEvalContext context) {
return getPresentableText(includeDefaultValue, context, Objects::isNull);
@@ -105,7 +105,7 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType {
}
@Override
public @Nullable String getClassQName() {
public @Nullable @NlsSafe String getClassQName() {
return myClass.getQualifiedName();
}
@@ -0,0 +1,4 @@
def func(x, *args, y, /) -> None: ...
va<caret>r = func
@@ -0,0 +1,7 @@
from typing import Callable
def func(x, *args, y, /) -> None: ...
var: [Callable[..., None]] = func
@@ -0,0 +1,5 @@
from typing import Callable
def foo(c: Callable[[int, str], int]):
va<caret>r = c
@@ -0,0 +1,5 @@
from typing import Callable
def foo(c: Callable[[int, str], int]):
var: [Callable[[int, str], int]] = c
@@ -0,0 +1,5 @@
def func():
pass
va<caret>r = func
@@ -0,0 +1,8 @@
from typing import Callable
def func():
pass
var: [Callable[[], None]] = func
@@ -0,0 +1,4 @@
def func(x: int, /) -> None:
pass
va<caret>r = func
@@ -0,0 +1,7 @@
from typing import Callable
def func(x: int, /) -> None:
pass
var: [Callable[[int], None]] = func
@@ -1,8 +1,8 @@
from typing import Callable, Any
from typing import Callable
def func(x):
pass
var: [Callable[[Any], None]] = func
var: [Callable[..., None]] = func
@@ -1,4 +1,4 @@
from typing import Literal, Set, Union
from typing import Literal, Set
HTML_ESCAPES: set[Literal["<", ">", "&"]] = {"<", ">", "&"}
var: [Set[Literal["<", ">", "&"]]] = HTML_ESCAPES
@@ -1,4 +1,4 @@
from typing import Any, Coroutine
from typing import Coroutine, Any
async def bar() -> int:
@@ -1,5 +1,5 @@
from typing import Callable, Any
from typing import Callable
def func() -> Callable[[Any], int]:
def func() -> Callable[..., int]:
return lambda x: 42<caret>
@@ -1,4 +1,4 @@
from typing import Any, AsyncGenerator
from typing import AsyncGenerator, Any
async def gen() -> AsyncGenerator[str | float, Any]:
@@ -1 +1 @@
<html><body><div class="bottom"><icon src="AllIcons.Nodes.Package"/>&nbsp;<code><a href="psi_element://#module#InferredCallableParameterListRendering">InferredCallableParameterListRendering</a></code></div><div class="definition"><pre><span style="color:#000000;">expr</span><span style="">: </span><span style="color:#000000;"><a href="psi_element://#typename#InferredCallableParameterListRendering.MyCallable">MyCallable</a><span style="">[</span>[<span style="color:#000000;">x</span><span style="">: </span><span style="color:#000080;"><a href="psi_element://#typename#int">int</a></span><span style="">, </span>Any<span style="">, </span><span style="color:#000000;">y</span><span style="">: </span><span style="color:#000080;"><a href="psi_element://#typename#list">list</a></span><span style="">[</span><span style="color:#000080;"><a href="psi_element://#typename#str">str</a></span><span style="">]</span><span style="">, </span><span style="color:#000000;">args</span><span style="">: </span><span style="color:#000080;">tuple</span><span style="">[</span>*Ts<span style="">]</span><span style="">, </span><span style="color:#000000;">z</span><span style="">: </span><span style="color:#000080;"><a href="psi_element://#typename#str">str</a></span><span style="">, </span><span style="color:#000000;">kwargs</span><span style="">: </span><span style="color:#000080;"><a href="psi_element://#typename#dict">dict</a></span><span style="">[</span><span style="color:#000080;"><a href="psi_element://#typename#str">str</a></span><span style="">, </span><span style="color:#000080;"><a href="psi_element://#typename#str">str</a></span><span style="">]</span>]<span style="">, </span>Any<span style="">]</span></span><span style=""> = </span><span style="">g(f)</span></pre></div></body></html>
<html><body><div class="bottom"><icon src="AllIcons.Nodes.Package"/>&nbsp;<code><a href="psi_element://#module#InferredCallableParameterListRendering">InferredCallableParameterListRendering</a></code></div><div class="definition"><pre><span style="color:#000000;">expr</span><span style="">: </span><span style="color:#000000;"><a href="psi_element://#typename#InferredCallableParameterListRendering.MyCallable">MyCallable</a><span style="">[</span>[<span style="color:#000000;">x</span><span style="">: </span><span style="color:#000080;"><a href="psi_element://#typename#int">int</a></span><span style="">, </span>/<span style="">, </span><span style="color:#000000;">y</span><span style="">: </span><span style="color:#000080;"><a href="psi_element://#typename#list">list</a></span><span style="">[</span><span style="color:#000080;"><a href="psi_element://#typename#str">str</a></span><span style="">]</span><span style="">, </span><span style="color:#000000;">args</span><span style="">: </span><span style="color:#000080;">tuple</span><span style="">[</span>*Ts<span style="">]</span><span style="">, </span><span style="color:#000000;">z</span><span style="">: </span><span style="color:#000080;"><a href="psi_element://#typename#str">str</a></span><span style="">, </span><span style="color:#000000;">kwargs</span><span style="">: </span><span style="color:#000080;"><a href="psi_element://#typename#dict">dict</a></span><span style="">[</span><span style="color:#000080;"><a href="psi_element://#typename#str">str</a></span><span style="">, </span><span style="color:#000080;"><a href="psi_element://#typename#str">str</a></span><span style="">]</span>]<span style="">, </span>Any<span style="">]</span></span><span style=""> = </span><span style="">g(f)</span></pre></div></body></html>
@@ -952,6 +952,15 @@ public class Py3TypeTest extends PyTestCase {
expr = f()""");
}
// PY-81606
public void testCallable() {
doTest("(x: int, /, s: str, *, k: bytes) -> None",
"""
def func(x: int, /, s: str, *, k: bytes) -> None:
pass
expr = func""");
}
// PY-24445
public void testIsSubclassInsideListComprehension() {
doTest("list[type[A]]",
@@ -5838,7 +5838,7 @@ public class PyTypingTest extends PyTestCase {
}
public void testDataclassTransformOwnKwOnlyOmittedAndTakenFromKwOnlyDefault() {
doTestExpressionUnderCaret("(Any, id: int, name: str) -> MyClass", """
doTestExpressionUnderCaret("(*, id: int, name: str) -> MyClass", """
from typing import dataclass_transform, Callable
@@ -5858,7 +5858,7 @@ public class PyTypingTest extends PyTestCase {
}
public void testDataclassTransformFieldSpecifierKwOnlyDefaultOverridesDecoratorsKwOnly() {
doTestExpressionUnderCaret("(id: str, Any, addr: list[str]) -> Order", """
doTestExpressionUnderCaret("(id: str, *, addr: list[str]) -> Order", """
from typing import Callable, dataclass_transform
def my_field(kw_only=False):
@@ -5878,7 +5878,7 @@ public class PyTypingTest extends PyTestCase {
}
public void testDataclassTransformFieldSpecifierKwOnlyDefaultOverridesDecoratorsKwOnlyDefault() {
doTestExpressionUnderCaret("(id: str, Any, addr: list[str]) -> Order", """
doTestExpressionUnderCaret("(id: str, *, addr: list[str]) -> Order", """
from typing import Callable, dataclass_transform
def my_field(kw_only=False):
@@ -5898,7 +5898,7 @@ public class PyTypingTest extends PyTestCase {
}
public void testDataclassTransformFieldSpecifierKwOnlyOverridesDecoratorsKwOnly() {
doTestExpressionUnderCaret("(id: str, Any, addr: list[str]) -> Order", """
doTestExpressionUnderCaret("(id: str, *, addr: list[str]) -> Order", """
from typing import Callable, dataclass_transform
def my_field(kw_only=False):
@@ -5918,7 +5918,7 @@ public class PyTypingTest extends PyTestCase {
}
public void testDataclassTransformFieldSpecifierKwOnlyOverridesDecoratorsKwOnlyDefault() {
doTestExpressionUnderCaret("(id: str, Any, addr: list[str]) -> Order", """
doTestExpressionUnderCaret("(id: str, *, addr: list[str]) -> Order", """
from typing import Callable, dataclass_transform
def my_field(kw_only=False):
@@ -236,6 +236,22 @@ public class PyAnnotateVariableTypeIntentionTest extends PyIntentionTestCase {
doAnnotationTest();
}
public void testAnnotationCallableTypeWithEmptyParameterList() {
doAnnotationTest();
}
public void testAnnotationCallableTypeWithPositionalOnlyParameters() {
doAnnotationTest();
}
public void testAnnotationCallableTypeInferredFromTypeHint() {
doAnnotationTest();
}
public void testAnnotationCallableTypeInferredFromFunctionWithIllegalSignature() {
doAnnotationTest();
}
public void testAnnotationTypingNamedTupleInOtherFile() {
doMultiFileAnnotationTest();
}
@@ -61,7 +61,7 @@ public class PyMakeFunctionReturnTypeQuickFixTest extends PyQuickFixTestCase {
// PY-27128 PY-48466
public void testLambda() {
doQuickFixTest(PyTypeCheckerInspection.class,
PyPsiBundle.message("QFIX.make.function.return.type", "func", "Callable[[Any], int]"),
PyPsiBundle.message("QFIX.make.function.return.type", "func", "Callable[..., int]"),
LanguageLevel.getLatest());
}