IDEA-CR-68177 PY-36610 Parameter info doesn't work in Python Console

GitOrigin-RevId: f60d65180234eca86488aba5911b033333e04642
This commit is contained in:
Elizaveta Shashkova
2020-12-09 10:39:37 +00:00
committed by intellij-monorepo-bot
parent d0b64e38bc
commit 31f33fd175
7 changed files with 57 additions and 10 deletions
@@ -46,7 +46,7 @@ def get_description(obj):
if isinstance(obj, type) or type(obj).__name__ == 'classobj':
fob = getattr(obj, '__init__', lambda: None)
if not isinstance(fob, (types.FunctionType, types.MethodType)):
if not callable(fob):
fob = obj
elif is_bound_method(ob_call):
fob = ob_call
@@ -54,9 +54,8 @@ def get_description(obj):
fob = obj
argspec = ""
fn_name = None
fn_class = None
if isinstance(fob, (types.FunctionType, types.MethodType)):
if callable(fob):
spec_info = inspect.getfullargspec(fob) if IS_PY3K else inspect.getargspec(fob)
argspec = inspect.formatargspec(*spec_info)
fn_name = getattr(fob, '__name__', None)
@@ -5,6 +5,7 @@ import com.intellij.codeInsight.completion.CompletionUtilCoreImpl;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.Ref;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiReference;
import com.intellij.psi.ResolveResult;
import com.intellij.psi.util.PsiTreeUtil;
@@ -12,6 +13,7 @@ import com.intellij.util.ObjectUtils;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.MultiMap;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.PythonRuntimeService;
import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.impl.references.PyReferenceImpl;
@@ -104,8 +106,7 @@ public final class PyCallExpressionHelper {
* please obtain its result via {@link TypeEvalContext#getType} with {@code call.getCallee()} as an argument.
*/
static @Nullable PyType getCalleeType(@NotNull PyCallExpression call,
@NotNull PyResolveContext resolveContext,
@SuppressWarnings("unused") @NotNull TypeEvalContext.Key key) {
@NotNull PyResolveContext resolveContext) {
final List<PyType> callableTypes = new ArrayList<>();
final TypeEvalContext context = resolveContext.getTypeEvalContext();
@@ -189,7 +190,10 @@ public final class PyCallExpressionHelper {
return PyUtil.getParameterizedCachedValue(
call,
resolveContext,
it -> ContainerUtil.concat(getExplicitResolveResults(call, it), getImplicitResolveResults(call, it))
it -> ContainerUtil.concat(
getExplicitResolveResults(call, it),
getImplicitResolveResults(call, it),
getRemoteResolveResults(call, it))
);
}
@@ -277,6 +281,16 @@ public final class PyCallExpressionHelper {
return Collections.emptyList();
}
@NotNull
private static List<@NotNull PyCallableType> getRemoteResolveResults(@NotNull PyCallExpression call,
@NotNull PyResolveContext resolveContext) {
if (!resolveContext.allowRemote()) return Collections.emptyList();
PsiFile file = call.getContainingFile();
if (file == null || !PythonRuntimeService.getInstance().isInPydevConsole(file)) return Collections.emptyList();
PyType calleeType = getCalleeType(call, resolveContext);
return PyTypeUtil.toStream(calleeType).select(PyCallableType.class).toList();
}
@NotNull
private static List<@NotNull PyCallableType> selectCallableTypes(@NotNull List<PsiElement> resolveResults,
@NotNull TypeEvalContext context) {
@@ -249,7 +249,7 @@ public class PyReferenceExpressionImpl extends PyElementImpl implements PyRefere
private PyType getCallableType(@NotNull TypeEvalContext context, @NotNull TypeEvalContext.Key key) {
PyCallExpression callExpression = PyCallExpressionNavigator.getPyCallExpressionByCallee(this);
if (callExpression != null) {
return getCalleeType(callExpression, PyResolveContext.defaultContext().withTypeEvalContext(context), key);
return getCalleeType(callExpression, PyResolveContext.defaultContext().withTypeEvalContext(context));
}
return null;
}
@@ -404,7 +404,7 @@ public abstract class PydevConsoleCommunication extends AbstractConsoleCommunica
// add temporary value to avoid repeated requests for the same expression
myPrevNameToDescription = Pair.create(text, "");
}
if (ApplicationManager.getApplication().isDispatchThread()) {
if (ApplicationManager.getApplication().isDispatchThread() && !ApplicationManager.getApplication().isUnitTestMode()) {
throw new PyDebuggerException("Documentation in Python Console shouldn't be called from Dispatch Thread!");
}
@@ -18,6 +18,7 @@ import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiFile;
import com.intellij.util.ui.UIUtil;
import com.intellij.xdebugger.frame.XValueChildrenList;
import com.jetbrains.env.PyExecutionFixtureTestTask;
@@ -438,6 +439,10 @@ public class PyConsoleTask extends PyExecutionFixtureTestTask {
});
}
public PsiFile getConsoleFile() {
return myConsoleView.getFile();
}
@NotNull
@Override
public Set<String> getTags() {
@@ -3,6 +3,7 @@ package com.jetbrains.env.debug;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.xdebugger.impl.ui.tree.nodes.XDebuggerTreeNode;
import com.jetbrains.env.PyEnvTestCase;
import com.jetbrains.python.console.pydev.PydevCompletionVariant;
@@ -20,6 +21,7 @@ import java.util.function.Predicate;
import static com.intellij.testFramework.UsefulTestCase.assertContainsElements;
import static com.jetbrains.env.debug.PyBaseDebuggerTask.findCompletionVariantByName;
import static com.jetbrains.env.debug.PyBaseDebuggerTask.findDebugValueByName;
import static com.jetbrains.python.PyParameterInfoTest.checkParameters;
import static org.junit.Assert.*;
public class PythonConsoleTest extends PyEnvTestCase {
@@ -284,4 +286,20 @@ public class PythonConsoleTest extends PyEnvTestCase {
}
});
}
@Test
public void testParameterInfo() {
runPythonTest(new PyConsoleTask("/debug") {
@Override
public void testing() throws Exception {
exec("from os import getenv");
exec("print(\"Hi\")");
waitForOutput("Hi");
addTextToEditor("getenv()");
ApplicationManager.getApplication().invokeAndWait(() -> {
checkParameters(7, getConsoleFile(), "key, default=None", new String[]{"key, "});
});
}
});
}
}
@@ -1150,16 +1150,22 @@ public class PyParameterInfoTest extends LightMarkedTestCase {
);
}
@NotNull
private Collector feignCtrlP(int offset) {
return feignCtrlP(offset, myFixture.getFile());
}
/**
* Imitates pressing of Ctrl+P; fails if results are not as expected.
*
* @param offset offset of 'cursor' where Ctrl+P is pressed.
* @return a {@link Collector} with collected hint info.
*/
@NotNull
private Collector feignCtrlP(int offset) {
private static Collector feignCtrlP(int offset, @NotNull PsiFile file) {
final PyParameterInfoHandler handler = new PyParameterInfoHandler();
final Collector collector = new Collector(myFixture.getFile(), offset);
final Collector collector = new Collector(file, offset);
collector.setParameterOwner(handler.findElementForParameterInfo(collector));
if (collector.getParameterOwner() != null) {
@@ -1174,6 +1180,11 @@ public class PyParameterInfoTest extends LightMarkedTestCase {
return collector;
}
public static void checkParameters(int offset, @NotNull PsiFile file, @NotNull String text, String @NotNull [] highlighted) {
Collector collector = feignCtrlP(offset, file);
collector.check(text, highlighted);
}
/**
* Imitates the normal UI contexts to the extent we use it. Collects highlighting.
*/