PY-79201 reveal inferred variance of type var

GitOrigin-RevId: 300ca43057147f83290e2e232c4b8fa91d643876
This commit is contained in:
Marcus Mews
2026-02-16 21:55:31 +00:00
committed by intellij-monorepo-bot
parent 15702a130d
commit 4ffced1811
30 changed files with 240 additions and 21 deletions
@@ -185,7 +185,7 @@ public final class PyDocumentationBuilder {
if (myBody.isEmpty() && myContent.isEmpty()) {
return null; // got nothing substantial to say!
return null; // got nothing significant to say!
}
else {
final HtmlBuilder result = new HtmlBuilder();
@@ -217,7 +217,7 @@ public final class PyDocumentationBuilder {
if (elementType == PyTokenTypes.FROM_KEYWORD) {
// We want to show yield doc in 'yield from ...' expressions when hover to 'from',
// but there is no particular PyStatement for 'yield' keyword, therefore we make such a check.
// but there is no particular PyStatement for 'yield' keyword; therefore, we make such a check.
if (parentStatement.getFirstChild() instanceof PyYieldExpression) {
return PyNames.YIELD;
}
@@ -302,7 +302,7 @@ public final class PyDocumentationBuilder {
}
if (link != null && typeParamName != null) {
myBody.appendRaw(PyPsiBundle.message("QDOC.type.parameter.name.of.link", HtmlChunk.text(typeParamName).bold(), link)).br();
myBody.append(PythonDocumentationProvider.describeTypeParameter(typeParameter, true, myContext));
myBody.append(PythonDocumentationProvider.describeTypeParameter(typeParameter, myOriginalElement, true, myContext));
}
}
@@ -476,7 +476,7 @@ public final class PyDocumentationBuilder {
}
if (effectiveDocstring != null) {
addFunctionSpecificSections(effectiveDocstring, pyFunction);
// if function is init without doc we will take attributes from the class doc
// if the function is init without a doc, we will take attributes from the class doc
if (effectiveDocstring != ownDocstring && PyUtil.isInitOrNewMethod(pyFunction)) {
addAttributesSection(effectiveDocstring);
}
@@ -516,7 +516,7 @@ public final class PyDocumentationBuilder {
if (owner instanceof PyClass pyClass) {
final PyFunction init = pyClass.findMethodByName(PyNames.INIT, false, myContext);
// if class doesn't have any doc return init doc
// if the class doesn't have any doc return init doc
if (init != null) {
return getEffectiveDocStringExpression(init);
}
@@ -560,7 +560,7 @@ public final class PyDocumentationBuilder {
}
myBody.br();
}
// if there is no separate doc for attribute we will try to take it from class doc
// if there is no separate doc for attribute, we will try to take it from class doc
if (getEffectiveDocStringExpression(target) == null) {
final PyStringLiteralExpression docString = getEffectiveDocStringExpression(containingClass);
if (docString != null) {
@@ -572,7 +572,7 @@ public final class PyDocumentationBuilder {
}
}
}
myBody.append(PythonDocumentationProvider.describeTarget(target, myContext));
myBody.append(PythonDocumentationProvider.describeTarget(target, myOriginalElement, myContext));
}
private void addAttributesSection(@NotNull PyStringLiteralExpression docstring) {
@@ -635,7 +635,7 @@ public final class PyDocumentationBuilder {
}
}
}
// Reference expression can be passed as the target element in Python console
// Reference expression can be passed as the target element in the Python console
if (myElement instanceof PyReferenceExpression) {
final PsiElement resolved = resolve((PyReferenceExpression)myElement);
if (resolved != null) {
@@ -669,7 +669,7 @@ public final class PyDocumentationBuilder {
final boolean isConstructor = PyUtil.isInitOrNewMethod(pyFunction);
List<PyClass> classes = pyClass.getAncestorClasses(myContext);
if (isConstructor) {
// look at our own class again and maybe inherit class's doc
// look at our own class again and maybe inherit the class's doc
classes = ContainerUtil.prepend(classes, pyClass);
}
for (PyClass ancestor : classes) {
@@ -737,7 +737,7 @@ public final class PyDocumentationBuilder {
.takeWhile(line -> !line.startsWith(">>>")) //TODO: PyConsoleUtil.ORDINARY_PROMPT
.toList();
final HtmlBuilder result = new HtmlBuilder();
// reconstruct back, dropping first empty fragment as needed
// reconstruct back, dropping the first empty fragment as needed
boolean isFirstLine = true;
final int tabSize = PythonCodeStyleService.getInstance().getTabSize(element.getContainingFile());
for (@NlsSafe String line : updatedLines) {
@@ -821,7 +821,7 @@ public final class PyDocumentationBuilder {
private static @Nullable HtmlChunk getLinkToFunction(@NotNull PyFunction function, boolean preferQualifiedName) {
final String qualifiedName = function.getQualifiedName();
final PyClass pyClass = function.getContainingClass();
// Preserve name of a containing class even if the whole qualified name can't be constructed
// Preserve the name of a containing class even if the whole qualified name can't be constructed
final String shortName = pyClass == null ? function.getName() : pyClass.getName() + "." + function.getName();
final String linkText = preferQualifiedName && qualifiedName != null ? qualifiedName : shortName;
@@ -6,6 +6,7 @@ import com.intellij.openapi.editor.DefaultLanguageHighlighterColors;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.util.NlsSafe;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.text.HtmlBuilder;
import com.intellij.openapi.util.text.HtmlChunk;
import com.intellij.openapi.util.text.StringUtil;
@@ -62,9 +63,12 @@ import com.jetbrains.python.psi.impl.PyClassImpl;
import com.jetbrains.python.psi.resolve.PyResolveContext;
import com.jetbrains.python.psi.resolve.QualifiedNameFinder;
import com.jetbrains.python.psi.types.PyCallableParameter;
import com.jetbrains.python.psi.types.PyClassType;
import com.jetbrains.python.psi.types.PyCollectionType;
import com.jetbrains.python.psi.types.PyInferredVarianceJudgment;
import com.jetbrains.python.psi.types.PyTupleType;
import com.jetbrains.python.psi.types.PyType;
import com.jetbrains.python.psi.types.PyTypeVarType;
import com.jetbrains.python.psi.types.PyTypeVisitor;
import com.jetbrains.python.psi.types.TypeEvalContext;
import one.util.streamex.StreamEx;
@@ -77,6 +81,7 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Locale;
import static com.jetbrains.python.documentation.PyDocSignaturesHighlighterKt.functionNameTextAttribute;
import static com.jetbrains.python.documentation.PyDocSignaturesHighlighterKt.highlightExpressionText;
@@ -84,6 +89,7 @@ import static com.jetbrains.python.documentation.PyDocSignaturesHighlighterKt.pa
import static com.jetbrains.python.documentation.PyDocSignaturesHighlighterKt.styledReference;
import static com.jetbrains.python.documentation.PyDocSignaturesHighlighterKt.styledSpan;
import static com.jetbrains.python.psi.PyUtil.as;
import static com.jetbrains.python.psi.types.PyInferredVarianceJudgment.isEffectivelyInvariant;
/**
* Provides quick docs for classes, methods, and functions.
@@ -144,7 +150,7 @@ public class PythonDocumentationProvider implements DocumentationProvider {
return describeExpression((PyExpression)element, referenceElement, context);
}
else if (element instanceof PyTypeParameter typeParameter) {
return PyPsiBundle.message("QDOC.type.parameter.name", describeTypeParameter(typeParameter, true, context));
return PyPsiBundle.message("QDOC.type.parameter.name", describeTypeParameter(typeParameter, originalElement, true, context));
}
else if (element instanceof PyTypeAliasStatement typeAliasStatement) {
return describeTypeAlias(typeAliasStatement, context).toString();
@@ -158,11 +164,14 @@ public class PythonDocumentationProvider implements DocumentationProvider {
return HtmlChunk.raw(describeFunctionWithTypes(function, context, forTooltip));
}
static @NotNull HtmlChunk describeTarget(@NotNull PyTargetExpression target, @NotNull TypeEvalContext context) {
static @NotNull HtmlChunk describeTarget(@NotNull PyTargetExpression target,
@NotNull PsiElement originalElement,
@NotNull TypeEvalContext context) {
final HtmlBuilder result = new HtmlBuilder();
result.append(styledSpan(StringUtil.notNullize(target.getName()), DefaultLanguageHighlighterColors.IDENTIFIER));
result.append(styledSpan(": ", PyHighlighter.PY_OPERATION_SIGN));
result.append(styledSpan(formatTypeWithLinks(context.getType(target), target, target, context), PyHighlighter.PY_ANNOTATION));
PyType type = context.getType(target);
result.append(styledSpan(formatTypeWithLinks(type, target, target, context), PyHighlighter.PY_ANNOTATION));
// Can return not physical elements such as foo()[0] for assignments like x, _ = foo()
final PyExpression value = target.findAssignedValue();
@@ -177,6 +186,27 @@ public class PythonDocumentationProvider implements DocumentationProvider {
result.append(highlightExpressionText(initializerText.substring(0, index), value));
result.append(styledSpan("...", PyHighlighter.PY_DOT));
}
if (type instanceof PyClassType classType && PyTypingTypeProvider.TYPE_VAR.equals(classType.getClassQName())) {
Ref<PyType> typeRef = PyTypingTypeProvider.getType(value, context);
if (typeRef != null && typeRef.get() instanceof PyTypeVarType valueType) {
PsiElement refExpr = findReferenceOrTypeParameter(originalElement);
if (refExpr != null) {
if (isEffectivelyInvariant(refExpr, context)) {
if (valueType.getVariance() != PyTypeVarType.Variance.INVARIANT) {
String varianceStr = PyTypeVarType.Variance.INVARIANT.name().toLowerCase(Locale.ROOT); //NON-NLS
result.append(styledSpan(" # treated as " + varianceStr, PyHighlighter.PY_LINE_COMMENT)); //NON-NLS
}
}
else if (valueType.getVariance() == PyTypeVarType.Variance.INFER_VARIANCE) {
PyTypeVarType.Variance inferredVariance = PyInferredVarianceJudgment.getInferredVariance(refExpr, context);
if (inferredVariance != null) {
String varianceStr = inferredVariance.name().toLowerCase(Locale.ROOT); //NON-NLS
result.append(styledSpan(" # inferred as " + varianceStr, PyHighlighter.PY_LINE_COMMENT)); //NON-NLS
}
}
}
}
}
}
return result.toFragment();
}
@@ -190,14 +220,39 @@ public class PythonDocumentationProvider implements DocumentationProvider {
}
static @NotNull HtmlChunk describeTypeParameter(@NotNull PyTypeParameter typeParameter,
@Nullable PsiElement originalElement,
boolean showKind,
@NotNull TypeEvalContext context) {
HtmlBuilder result = new HtmlBuilder();
PsiElement refExpr = findReferenceOrTypeParameter(originalElement);
boolean effectivelyInvariant = isEffectivelyInvariant(refExpr, context);
PyTypeVarType.Variance variance = effectivelyInvariant
? PyTypeVarType.Variance.INVARIANT
: PyInferredVarianceJudgment.getInferredVariance(refExpr, context);
String varianceStr = switch (variance) {
case null -> null;
case INFER_VARIANCE -> null;
case COVARIANT -> "out "; //NON-NLS
case CONTRAVARIANT -> "in "; //NON-NLS
case INVARIANT -> "invariant "; //NON-NLS
case BIVARIANT -> "bivariant "; //NON-NLS
};
if (varianceStr != null) {
result.append(styledSpan(varianceStr, PyHighlighter.PY_KEYWORD));
}
result.append(styledSpan(StringUtil.notNullize(typeParameter.getName()), PyHighlighter.PY_TYPE_PARAMETER));
PyExpression boundExpression = typeParameter.getBoundExpression();
if (boundExpression != null && typeParameter.getBoundExpressionText() != null) {
Ref<PyType> boundExprTR = boundExpression == null ? null : PyTypingTypeProvider.getType(boundExpression, context);
if (boundExprTR != null && boundExprTR.get() != null) {
result.append(styledSpan(": ", PyHighlighter.PY_OPERATION_SIGN));
result.append(highlightExpressionText(typeParameter.getBoundExpressionText(), typeParameter.getBoundExpression()));
result.append(styledSpan(formatTypeWithLinks(boundExprTR.get(), boundExpression, boundExpression, context),
PyHighlighter.PY_ANNOTATION));
}
PyExpression defaultExpr = typeParameter.getDefaultExpression();
Ref<PyType> defaultExprTR = defaultExpr == null ? null : PyTypingTypeProvider.getType(defaultExpr, context);
if (defaultExprTR != null && defaultExprTR.get() != null) {
result.append(styledSpan(" = ", PyHighlighter.PY_OPERATION_SIGN));
result.append(styledSpan(formatTypeWithLinks(defaultExprTR.get(), defaultExpr, defaultExpr, context), PyHighlighter.PY_ANNOTATION));
}
if (showKind) {
result
@@ -210,6 +265,19 @@ public class PythonDocumentationProvider implements DocumentationProvider {
return result.toFragment();
}
private static @Nullable PsiElement findReferenceOrTypeParameter(@Nullable PsiElement originalElement) {
if (originalElement == null) return originalElement;
if (originalElement instanceof PyReferenceExpression refExpr) return refExpr;
if (originalElement.getParent() instanceof PyTypeParameter typeParam) return typeParam;
if (originalElement.getParent() instanceof PyReferenceExpression refExpr) return refExpr;
PsiElement prevElement = PsiTreeUtil.prevLeaf(originalElement, true);
if (prevElement == null) return originalElement;
if (prevElement.getParent() instanceof PyTypeParameter typeParam) return typeParam;
if (prevElement instanceof PyReferenceExpression refExpr) return refExpr;
if (prevElement.getParent() instanceof PyReferenceExpression refExpr) return refExpr;
return originalElement;
}
static @NotNull HtmlChunk describeTypeAlias(@NotNull PyTypeAliasStatement typeAliasStatement, @NotNull TypeEvalContext context) {
HtmlBuilder result = new HtmlBuilder();
@@ -220,7 +288,7 @@ public class PythonDocumentationProvider implements DocumentationProvider {
result.append(styledSpan("[", PyHighlighter.PY_BRACKETS));
result.append(StreamEx
.of(typeParameters)
.map(typeParameter -> describeTypeParameter(typeParameter, false, context))
.map(typeParameter -> describeTypeParameter(typeParameter, null, false, context))
.collect(HtmlChunk.toFragment(styledSpan(", ", PyHighlighter.PY_COMMA))));
result.append(styledSpan("]", PyHighlighter.PY_BRACKETS));
}
@@ -306,7 +374,7 @@ public class PythonDocumentationProvider implements DocumentationProvider {
}
else {
paramName = StringUtil.notNullize(paramName, PyNames.UNNAMED_ELEMENT);
// Don't show type for "self" unless it's explicitly annotated
// Don't show a type for "self" unless it's explicitly annotated
showType = !isSelf || (named != null && new PyTypingTypeProvider().getParameterType(named, function, context) != null);
}
@@ -379,7 +447,7 @@ public class PythonDocumentationProvider implements DocumentationProvider {
* Render a type in a human-readable format.
* <p>
* The format is our own compact representation of types, intended to be used in inspection warnings,
* documentation and other user-facing messages. It's not PEP-484 compatible.
* documentation, and other user-facing messages. It's not PEP-484 compatible.
* In particular callables are rendered as {@code (p1: T1, p2: T2, ...) -> R}, not as {@code Callable[[T1, T2, ...], R]}.
* To render a type as a valid expression for a type annotation use {@link #getTypeHint(PyType, TypeEvalContext)}.
*
@@ -462,7 +530,7 @@ public class PythonDocumentationProvider implements DocumentationProvider {
@NotNull TypeEvalContext context,
@NotNull PsiElement anchor,
@NotNull HtmlBuilder body) {
// Variable annotated with "typing.TypeAlias" marker is deliberately treated as having "Any" type
// Variable annotated with 'typing.TypeAlias' marker is deliberately treated as having "Any" type
if (typeOwner instanceof PyTargetExpression && type == null) {
PyAssignmentStatement assignment = as(typeOwner.getParent(), PyAssignmentStatement.class);
if (assignment != null && PyTypingTypeProvider.isExplicitTypeAlias(assignment, context)) {
@@ -0,0 +1,7 @@
class A[<weak_warning descr="Type parameter 'T' is not used">T</weak_warning>]:
def f(self):
pass
class B[S, <weak_warning descr="Type parameter 'T' is not used">T</weak_warning>]:
def f(self, s: S):
pass
@@ -0,0 +1 @@
<html><body><div class="definition"><pre>Type parameter <b>X</b> of <a href="psi_element://#typename#TypeParameter_variance_co.Box">TypeParameter_variance_co.Box</a><br/><span style="color:#000080;font-weight:bold;">out </span><span style="color:#000000;">X</span>, kind: <span style="color:#000000;">TypeVar</span></pre></div></body></html>
@@ -0,0 +1,3 @@
class Box[X]:
def m(self) -> X<the_ref>:
pass
@@ -0,0 +1 @@
<html><body><div class="definition"><pre>Type parameter <b>X</b> of <a href="psi_element://#typename#TypeParameter_variance_contra.Box">TypeParameter_variance_contra.Box</a><br/><span style="color:#000080;font-weight:bold;">in </span><span style="color:#000000;">X</span>, kind: <span style="color:#000000;">TypeVar</span></pre></div></body></html>
@@ -0,0 +1,3 @@
class Box[X]:
def m(self, a: X<the_ref>):
pass
@@ -0,0 +1 @@
<html><body><div class="definition"><pre>Type parameter <b>X</b> of <a href="psi_element://#typename#TypeParameter_variance_inv.Box">TypeParameter_variance_inv.Box</a><br/><span style="color:#000080;font-weight:bold;">invariant </span><span style="color:#000000;">X</span>, kind: <span style="color:#000000;">TypeVar</span></pre></div></body></html>
@@ -0,0 +1,2 @@
class Box[X]:
a: X<the_ref>
@@ -0,0 +1 @@
<html><body><div class="definition"><pre>Type parameter <b>YY</b> of <a href="psi_element://#func#TypeParameter_variance_not_affected_1.m">TypeParameter_variance_not_affected_1.m</a><br/><span style="color:#000080;font-weight:bold;">invariant </span><span style="color:#000000;">YY</span><span style="">: </span><span style="color:#000000;"><span style="color:#000080;"><a href="psi_element://#typename#int">int</a></span></span>, kind: <span style="color:#000000;">TypeVar</span></pre></div></body></html>
@@ -0,0 +1 @@
def m[YY:int](self) -> Y<the_ref>Y:
@@ -0,0 +1 @@
<html><body><div class="definition"><pre>Type parameter <b>X</b> of <a href="psi_element://#typename#TypeParameter_variance_not_affected_2.Box2">TypeParameter_variance_not_affected_2.Box2</a><br/><span style="color:#000080;font-weight:bold;">invariant </span><span style="color:#000000;">X</span>, kind: <span style="color:#000000;">TypeVar</span></pre></div></body></html>
@@ -0,0 +1,6 @@
class Box2[X]:
def m(self) -> X: # inferred as 'out Any'
pass
@classmethod
def f(cls) -> X<the_ref>: # inferred as 'Any'
pass
@@ -0,0 +1 @@
<html><body><div class="definition"><pre>Type parameter <b>X</b> of <a href="psi_element://#typename#TypeParameter_variance_on_its_declaration.Box">TypeParameter_variance_on_its_declaration.Box</a><br/><span style="color:#000080;font-weight:bold;">out </span><span style="color:#000000;">X</span>, kind: <span style="color:#000000;">TypeVar</span></pre></div></body></html>
@@ -0,0 +1,3 @@
class Box[X<the_ref>]:
def m(self) -> X:
pass
@@ -0,0 +1 @@
<html><body><div class="definition"><pre>Type parameter <b>X</b> of <a href="psi_element://#typename#TypeParameter_variance_with_default.A">TypeParameter_variance_with_default.A</a><br/><span style="color:#000080;font-weight:bold;">in </span><span style="color:#000000;">X</span><span style=""> = </span><span style="color:#000000;"><span style="color:#000080;"><a href="psi_element://#typename#int">int</a></span></span>, kind: <span style="color:#000000;">TypeVar</span></pre></div></body></html>
@@ -0,0 +1,3 @@
class A[X = int]:
def f2(self, x: X<the_ref>):
pass
@@ -0,0 +1 @@
<html><body><div class="bottom"><icon src="AllIcons.Nodes.Package"/>&nbsp;<code><a href="psi_element://#module#TypeVar_variance_co">TypeVar_variance_co</a></code></div><div class="definition"><pre><span style="color:#000000;">TT</span><span style="">: </span><span style="color:#000000;"><a href="psi_element://#typename#typing.TypeVar">TypeVar</a></span><span style=""> = </span><span style="">TypeVar(</span><span style="color:#008000;font-weight:bold;">'TT'</span><span style="">,&#32;infer_variance=</span><span style="color:#000080;font-weight:bold;">True</span><span style="">)</span><span style="color:#808080;font-style:italic;"> # inferred as covariant</span></pre></div></body></html>
@@ -0,0 +1,7 @@
from typing import TypeVar, Generic
TT = TypeVar('TT', infer_variance=True)
class C(Generic[TT]):
def f(self) -> T<the_ref>T:
pass
@@ -0,0 +1 @@
<html><body><div class="bottom"><icon src="AllIcons.Nodes.Package"/>&nbsp;<code><a href="psi_element://#module#TypeVar_variance_contra">TypeVar_variance_contra</a></code></div><div class="definition"><pre><span style="color:#000000;">TT</span><span style="">: </span><span style="color:#000000;"><a href="psi_element://#typename#typing.TypeVar">TypeVar</a></span><span style=""> = </span><span style="">TypeVar(</span><span style="color:#008000;font-weight:bold;">'TT'</span><span style="">,&#32;infer_variance=</span><span style="color:#000080;font-weight:bold;">True</span><span style="">)</span><span style="color:#808080;font-style:italic;"> # inferred as contravariant</span></pre></div></body></html>
@@ -0,0 +1,7 @@
from typing import TypeVar, Generic
TT = TypeVar('TT', infer_variance=True)
class C(Generic[TT]):
def f(self, p: T<the_ref>T):
pass
@@ -0,0 +1 @@
<html><body><div class="bottom"><icon src="AllIcons.Nodes.Package"/>&nbsp;<code><a href="psi_element://#module#TypeVar_variance_in_init">TypeVar_variance_in_init</a></code></div><div class="definition"><pre><span style="color:#000000;">T</span><span style="">: </span><span style="color:#000000;"><a href="psi_element://#typename#typing.TypeVar">TypeVar</a></span><span style=""> = </span><span style="">TypeVar(</span><span style="color:#008000;font-weight:bold;">'T'</span><span style="">,&#32;infer_variance=</span><span style="color:#000080;font-weight:bold;">True</span><span style="">)</span><span style="color:#808080;font-style:italic;"> # treated as invariant</span></pre></div></body></html>
@@ -0,0 +1,9 @@
from typing import TypeVar, Generic
T = TypeVar('T', infer_variance=True)
class C(Generic[T]):
def __init__(self, t: T<the_ref>):
pass
def method(self) -> T:
pass
@@ -0,0 +1 @@
<html><body><div class="bottom"><icon src="AllIcons.Nodes.Package"/>&nbsp;<code><a href="psi_element://#module#TypeVar_variance_inv">TypeVar_variance_inv</a></code></div><div class="definition"><pre><span style="color:#000000;">TT</span><span style="">: </span><span style="color:#000000;"><a href="psi_element://#typename#typing.TypeVar">TypeVar</a></span><span style=""> = </span><span style="">TypeVar(</span><span style="color:#008000;font-weight:bold;">'TT'</span><span style="">,&#32;infer_variance=</span><span style="color:#000080;font-weight:bold;">True</span><span style="">)</span><span style="color:#808080;font-style:italic;"> # inferred as invariant</span></pre></div></body></html>
@@ -0,0 +1,7 @@
from typing import TypeVar, Generic
TT = TypeVar('TT', infer_variance=True)
class C(Generic[TT]):
def f(self, p: T<the_ref>T) -> TT:
pass
@@ -0,0 +1 @@
<html><body><div class="bottom"><icon src="AllIcons.Nodes.Package"/>&nbsp;<code><a href="psi_element://#module#TypeVar_variance_not_affected_1">TypeVar_variance_not_affected_1</a></code></div><div class="definition"><pre><span style="color:#000000;">TT</span><span style="">: </span><span style="color:#000000;"><a href="psi_element://#typename#typing.TypeVar">TypeVar</a></span><span style=""> = </span><span style="">TypeVar(</span><span style="color:#008000;font-weight:bold;">'TT'</span><span style="">,&#32;infer_variance=</span><span style="color:#000080;font-weight:bold;">True</span><span style="">)</span><span style="color:#808080;font-style:italic;"> # inferred as invariant</span></pre></div></body></html>
@@ -0,0 +1,9 @@
from typing import TypeVar, Generic
TT = TypeVar('TT', infer_variance=True)
class C(Generic[TT]):
def method(self) -> TT:
pass
def f() -> T<the_ref>T:
@@ -0,0 +1 @@
<html><body><div class="bottom"><icon src="AllIcons.Nodes.Package"/>&nbsp;<code><a href="psi_element://#module#TypeVar_variance_not_affected_2">TypeVar_variance_not_affected_2</a></code></div><div class="definition"><pre><span style="color:#000000;">TT</span><span style="">: </span><span style="color:#000000;"><a href="psi_element://#typename#typing.TypeVar">TypeVar</a></span><span style=""> = </span><span style="">TypeVar(</span><span style="color:#008000;font-weight:bold;">'TT'</span><span style="">,&#32;covariant=</span><span style="color:#000080;font-weight:bold;">True</span><span style="">)</span><span style="color:#808080;font-style:italic;"> # treated as invariant</span></pre></div></body></html>
@@ -0,0 +1,6 @@
from typing import TypeVar, Generic
TT = TypeVar('TT', covariant=True)
class C(Generic[TT]):
def f() -> T<the_ref>T: ...
@@ -843,6 +843,71 @@ public class Py3QuickDocTest extends LightMarkedTestCase {
checkHTMLOnly();
}
// PY-87075
public void testTypeParameter_variance_with_default() {
checkHTMLOnly();
}
// PY-87075
public void testTypeParameter_variance_inv() {
checkHTMLOnly();
}
// PY-87075
public void testTypeParameter_variance_co() {
checkHTMLOnly();
}
// PY-87075
public void testTypeParameter_variance_contra() {
checkHTMLOnly();
}
// PY-87075
public void testTypeParameter_variance_not_affected_1() {
checkHTMLOnly();
}
// PY-87075
public void testTypeParameter_variance_not_affected_2() {
checkHTMLOnly();
}
// PY-87075
public void testTypeParameter_variance_on_its_declaration() {
checkHTMLOnly();
}
// PY-87075
public void testTypeVar_variance_co() {
checkHTMLOnly();
}
// PY-87075
public void testTypeVar_variance_contra() {
checkHTMLOnly();
}
// PY-87075
public void testTypeVar_variance_inv() {
checkHTMLOnly();
}
// PY-87075
public void testTypeVar_variance_in_init() {
checkHTMLOnly();
}
// PY-87075
public void testTypeVar_variance_not_affected_1() {
checkHTMLOnly();
}
// PY-87075
public void testTypeVar_variance_not_affected_2() {
checkHTMLOnly();
}
// PY-64074
public void testTypeKeyword() {
checkHTMLOnly();
@@ -852,7 +917,7 @@ public class Py3QuickDocTest extends LightMarkedTestCase {
public void testTypeAliasStatement() {
checkHTMLOnly();
}
// PY-78119
public void testNeverType() {
checkHTMLOnly();