PY-75831 tests for split cache(resolve/type) into library and user part

(cherry picked from commit ca5f003693960b25d78bda050b55fe39900be5a7)

IJ-MR-169158

GitOrigin-RevId: 759cb38f76124b5b0709757e8f73ddffbf4a8248
This commit is contained in:
Mikhail Golubev
2025-07-28 08:06:50 +00:00
committed by intellij-monorepo-bot
parent 31991b70bd
commit fd4360b032
13 changed files with 102 additions and 7 deletions
-1
View File
@@ -23,7 +23,6 @@ jvm_library(
"//python/python-ast:ast",
"//python/python-syntax-core:syntax-core",
"@lib//:kotlin-stdlib",
"//platform/platform-api:ide",
],
exports = [
"//python/python-parser:parser",
@@ -20,6 +20,5 @@
<orderEntry type="module" module-name="intellij.python.syntax.core" />
<orderEntry type="library" name="kotlin-stdlib" level="project" />
<orderEntry type="module" module-name="intellij.platform.backend" scope="RUNTIME" />
<orderEntry type="module" module-name="intellij.platform.ide" />
</component>
</module>
@@ -99,7 +99,7 @@ public sealed class TypeEvalContext {
* the analyzed code was called or may be called. Since this is basically guesswork, the results should be used only for code completion.
*/
public static @NotNull TypeEvalContext codeCompletion(final @NotNull Project project, final @Nullable PsiFile origin) {
if (Registry.is("python.use.library.leve.type.eval.context")) {
if (Registry.is("python.use.separated.libraries.type.cache")) {
return new LibraryLongLiveTypeEvalContext(true, true, true, origin);
}
return getContextFromCache(project, new TypeEvalContext(true, true, true, origin));
@@ -113,7 +113,7 @@ public sealed class TypeEvalContext {
* For code completion see {@link TypeEvalContext#codeCompletion(Project, PsiFile)}.
*/
public static TypeEvalContext userInitiated(final @NotNull Project project, final @Nullable PsiFile origin) {
if (Registry.is("python.use.library.leve.type.eval.context")) {
if (Registry.is("python.use.separated.libraries.type.cache")) {
return new LibraryLongLiveTypeEvalContext(true, true, false, origin);
}
return getContextFromCache(project, new TypeEvalContext(true, true, false, origin));
-1
View File
@@ -49,7 +49,6 @@ jvm_library(
"//python/python-parser:parser",
"//python/python-syntax-core:syntax-core",
"//python/impl.helperLocator:community-helpersLocator",
"//platform/platform-api:ide",
],
exports = ["//python/python-syntax-core:syntax-core"],
runtime_deps = [":psi-impl_resources"]
@@ -45,6 +45,5 @@
<orderEntry type="module" module-name="intellij.python.parser" />
<orderEntry type="module" module-name="intellij.python.syntax.core" exported="" />
<orderEntry type="module" module-name="intellij.python.community.helpersLocator" />
<orderEntry type="module" module-name="intellij.platform.ide" />
</component>
</module>
@@ -507,7 +507,7 @@
description="When enabled, activates LiteralString inference for Python string literals" />
<registryKey key="python.optimized.type.eval.context" defaultValue="true"
description="It optimises cache usage for storing calculated types."/>
<registryKey key="python.use.library.leve.type.eval.context" defaultValue="true"
<registryKey key="python.use.separated.libraries.type.cache" defaultValue="true"
description="It enables the use of a library-level cache for PSI elements from packages."/>
<registryKey key="python.statement.lists.incremental.reparse" defaultValue="false"
description="Enables incremental reparse for statement lists"/>
@@ -0,0 +1,11 @@
from lib import not_annotated_func_returning_str
x = not_annotated_func_returning_str()
def expects_int(p: int) -> None:
pass
expects_int(x) # should have no error, because we infer Any for not_annotated_func_returning_str()
@@ -0,0 +1,2 @@
def not_annotated_func_returning_str():
return "foo"
@@ -0,0 +1,3 @@
from mylib2 import func
attr = func()
@@ -0,0 +1,2 @@
def func():
pass
@@ -0,0 +1,3 @@
import mylib1
expr = mylib1.attr
@@ -0,0 +1,2 @@
def func() -> int:
...
@@ -0,0 +1,76 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.jetbrains.python;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.vfs.StandardFileSystems;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import com.jetbrains.python.documentation.PythonDocumentationProvider;
import com.jetbrains.python.fixtures.PyTestCase;
import com.jetbrains.python.inspections.PyTypeCheckerInspection;
import com.jetbrains.python.psi.PyFunction;
import com.jetbrains.python.psi.PyReferenceExpression;
import com.jetbrains.python.psi.PyTargetExpression;
import com.jetbrains.python.psi.PyUtil;
import com.jetbrains.python.psi.types.TypeEvalContext;
import org.jetbrains.annotations.NotNull;
public final class PyTypeCachingTest extends PyTestCase {
public void testInspectionPassAfterUserInitiatedAction() {
myFixture.copyDirectoryToProject("NonAnnotatedDefinitionInAnotherProjectFile", "");
myFixture.configureFromTempProjectFile("a.py");
// Here can be any action using TypeEvalContext with myAllowStubToAST==true
PyTargetExpression argument = myFixture.findElementByText("x", PyTargetExpression.class);
String argumentQuickDoc = new PythonDocumentationProvider().generateDoc(argument, argument);
assertTrue(argumentQuickDoc.contains("<a href=\"psi_element://#typename#str\">str</a>"));
myFixture.enableInspections(PyTypeCheckerInspection.class);
myFixture.checkHighlighting();
}
public void testUserInitiatedActionAfterInspectionPass() {
myFixture.copyDirectoryToProject("NonAnnotatedDefinitionInAnotherProjectFile", "");
myFixture.configureFromTempProjectFile("a.py");
myFixture.enableInspections(PyTypeCheckerInspection.class);
myFixture.checkHighlighting();
PyTargetExpression argument = myFixture.findElementByText("x", PyTargetExpression.class);
String argumentQuickDoc = new PythonDocumentationProvider().generateDoc(argument, argument);
assertTrue(argumentQuickDoc.contains("<a href=\"psi_element://#typename#str\">str</a>"));
}
public void testProjectPyiStubChangesLibraryType() {
final String libRootPath = getTestDataPath() + "/" + getTestName(false) + "/lib";
final VirtualFile libRoot = StandardFileSystems.local().findFileByPath(libRootPath);
runWithAdditionalClassEntryInSdkRoots(libRoot, () -> {
myFixture.copyDirectoryToProject(getTestName(false) + "/user", "");
myFixture.configureFromTempProjectFile("main.py");
assertType("int",
myFixture.findElementByText("expr", PyTargetExpression.class),
TypeEvalContext.userInitiated(myFixture.getProject(), myFixture.getFile()));
// Change the return type annotation in the project .pyi stub
PsiFile userPyiStub = myFixture.configureFromTempProjectFile("mylib2.pyi");
PyReferenceExpression returnTypeHint = myFixture.findElementByText("int", PyReferenceExpression.class);
WriteCommandAction.runWriteCommandAction(myFixture.getProject(), () -> {
PyUtil.updateDocumentUnblockedAndCommitted(userPyiStub, document -> {
document.replaceString(returnTypeHint.getTextOffset(), returnTypeHint.getTextRange().getEndOffset(), "str");
});
});
PyFunction func = myFixture.findElementByText("func", PyFunction.class);
assertType("() -> str", func, TypeEvalContext.userInitiated(myFixture.getProject(), myFixture.getFile()));
myFixture.configureFromTempProjectFile("main.py");
assertType("str",
myFixture.findElementByText("expr", PyTargetExpression.class),
TypeEvalContext.userInitiated(myFixture.getProject(), myFixture.getFile()));
});
}
@Override
public @NotNull String getTestDataPath() {
return super.getTestDataPath() + "/caching/";
}
}