Parse references to assigned 'typing' expressions inside docstrings (PY-16303)

This commit is contained in:
Andrey Vlasovskikh
2015-10-21 17:34:12 +03:00
parent 80595d0e39
commit a1d2464aa0
4 changed files with 79 additions and 21 deletions
@@ -236,6 +236,11 @@ public class PyTypingTypeProvider extends PyTypeProviderBase {
@Nullable
private static PyType getType(@NotNull PyExpression expression, @NotNull TypeEvalContext context) {
final PsiElement resolved = tryResolving(expression, context);
return getTypeForResolvedElement(resolved, context);
}
@Nullable
private static PyType getTypeForResolvedElement(@NotNull PsiElement resolved, @NotNull TypeEvalContext context) {
final PyType unionType = getUnionType(resolved, context);
if (unionType != null) {
return unionType;
@@ -294,6 +299,13 @@ public class PyTypingTypeProvider extends PyTypeProviderBase {
return null;
}
@Nullable
public static PyType getTypeFromTargetExpression(@NotNull PyTargetExpression expression, @NotNull TypeEvalContext context) {
// XXX: Requires switching from stub to AST
final PyExpression assignedValue = expression.findAssignedValue();
return assignedValue != null ? getTypeForResolvedElement(assignedValue, context) : null;
}
@Nullable
private static Ref<PyType> getClassType(@NotNull PsiElement element, @NotNull TypeEvalContext context) {
if (element instanceof PyTypedElement) {
@@ -441,31 +441,34 @@ public class PyTypeParser {
PsiElement resolved = file.getElementNamed(firstText);
if (resolved != null) {
// Local or imported name
if (resolved instanceof PyTypedElement) {
if (resolved instanceof PyTargetExpression) {
type = PyTypingTypeProvider.getTypeFromTargetExpression((PyTargetExpression)resolved, context);
}
if (type == null && resolved instanceof PyTypedElement) {
type = context.getType((PyTypedElement)resolved);
if (type != null) {
tokens.remove(0);
if (!allowResolveToType(type)) {
return null;
}
if (type instanceof PyClassLikeType) {
type = ((PyClassLikeType)type).toInstance();
}
types.put(firstRange, type);
fullRanges.put(type, firstRange);
for (PyFromImportStatement fromImportStatement : file.getFromImports()) {
for (PyImportElement importElement : fromImportStatement.getImportElements()) {
if (firstText.equals(importElement.getVisibleName())) {
imports.put(type, importElement);
}
}
}
for (PyImportElement importElement : file.getImportTargets()) {
}
if (type != null) {
tokens.remove(0);
if (!allowResolveToType(type)) {
return null;
}
if (type instanceof PyClassLikeType) {
type = ((PyClassLikeType)type).toInstance();
}
types.put(firstRange, type);
fullRanges.put(type, firstRange);
for (PyFromImportStatement fromImportStatement : file.getFromImports()) {
for (PyImportElement importElement : fromImportStatement.getImportElements()) {
if (firstText.equals(importElement.getVisibleName())) {
imports.put(type, importElement);
}
}
}
for (PyImportElement importElement : file.getImportTargets()) {
if (firstText.equals(importElement.getVisibleName())) {
imports.put(type, importElement);
}
}
}
}
else {
@@ -497,7 +500,8 @@ public class PyTypeParser {
}
private static boolean allowResolveToType(@NotNull PyType type) {
return type instanceof PyClassLikeType || type instanceof PyModuleType || type instanceof PyImportedModuleType;
return type instanceof PyClassLikeType || type instanceof PyModuleType || type instanceof PyImportedModuleType ||
type instanceof PyGenericType;
}
@Nullable
+1 -1
View File
@@ -1,4 +1,4 @@
def ones(shape, dtype=None, order='C')
Inferred type: (shape:&nbsp;Union[<a href="psi_element://#typename#int">int</a>,&nbsp;Iterable[<a href="psi_element://#typename#int">int</a>]],&nbsp;dtype:&nbsp;Optional[<a href="psi_element://#typename#object">object</a>],&nbsp;order:&nbsp;Optional[<a href="psi_element://#typename#str">str</a>])&nbsp;-&gt;&nbsp;ndarray<br>
Inferred type: (shape:&nbsp;Union[<a href="psi_element://#typename#int">int</a>,&nbsp;Iterable[<a href="psi_element://#typename#int">int</a>]],&nbsp;dtype:&nbsp;Optional[<a href="psi_element://#typename#object">object</a>],&nbsp;order:&nbsp;Optional[<a href="psi_element://#typename#str">str</a>])&nbsp;-&gt;&nbsp;<a href="psi_element://#typename#ndarray">ndarray</a><br>
**Test docstring**
Return a new array of given shape and type, filled with ones.
@@ -377,6 +377,48 @@ public class PyTypingTest extends PyTestCase {
" pass\n");
}
// PY-16303
public void testAssignedTypeInDocstring() {
doTest("list[int]",
"from typing import List\n" +
"\n" +
"IntList = List[int]\n" +
"\n" +
"def foo(expr):\n" +
" '''\n" +
" :type expr: IntList\n" +
" '''\n" +
" pass\n");
}
// PY-16303
public void testParameterAssignedTypeInDocstring() {
doTest("Union[int, list[int]]",
"from typing import List, Union\n" +
"\n" +
"IntList = List[int]\n" +
"\n" +
"def foo(expr):\n" +
" '''\n" +
" :type expr: Union[int, IntList]\n" +
" '''\n" +
" pass\n");
}
// PY-16303
public void testTypeVarInDocstring() {
doTest("TypeVar('TV')",
"from typing import TypeVar\n" +
"\n" +
"TV = TypeVar('TV')\n" +
"\n" +
"def foo(expr):\n" +
" '''\n" +
" :type expr: TV\n" +
" '''\n" +
" pass\n");
}
private void doTestNoInjectedText(@NotNull String text) {
myFixture.configureByText(PythonFileType.INSTANCE, text);
final InjectedLanguageManager languageManager = InjectedLanguageManager.getInstance(myFixture.getProject());