From 2a302e12883bb14ce1d05d588ce8ad75b7c05f9b Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Wed, 17 Jul 2013 18:47:54 +0400 Subject: [PATCH 1/5] Fixed duplicate requirements if several requires args are specified (PY-10297) --- .../com/jetbrains/python/packaging/PyRequirement.java | 4 ++-- .../DuplicateInstallAndTests/setup.py | 10 ++++++++++ .../DuplicateInstallAndTests/test1.py | 2 ++ 3 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 python/testData/inspections/PyPackageRequirementsInspection/DuplicateInstallAndTests/setup.py create mode 100644 python/testData/inspections/PyPackageRequirementsInspection/DuplicateInstallAndTests/test1.py diff --git a/python/openapi/src/com/jetbrains/python/packaging/PyRequirement.java b/python/openapi/src/com/jetbrains/python/packaging/PyRequirement.java index 607af5df5783..c65ddbb7a6ac 100644 --- a/python/openapi/src/com/jetbrains/python/packaging/PyRequirement.java +++ b/python/openapi/src/com/jetbrains/python/packaging/PyRequirement.java @@ -288,7 +288,7 @@ public class PyRequirement { @NotNull private static List parse(@NotNull String s, @Nullable VirtualFile anchor, @NotNull Set visited) { - final List result = new ArrayList(); + final Set result = new LinkedHashSet(); for (String line : StringUtil.splitByLines(s)) { final String trimmed = line.trim(); if (!trimmed.isEmpty()) { @@ -301,7 +301,7 @@ public class PyRequirement { } } } - return result; + return new ArrayList(result); } @NotNull diff --git a/python/testData/inspections/PyPackageRequirementsInspection/DuplicateInstallAndTests/setup.py b/python/testData/inspections/PyPackageRequirementsInspection/DuplicateInstallAndTests/setup.py new file mode 100644 index 000000000000..ea69e78d0f6a --- /dev/null +++ b/python/testData/inspections/PyPackageRequirementsInspection/DuplicateInstallAndTests/setup.py @@ -0,0 +1,10 @@ +from distutils.core import setup + +requires = [ + 'mynose' +] + +setup(name='foo', + version=0.1, + install_requires=requires, + tests_require=requires) diff --git a/python/testData/inspections/PyPackageRequirementsInspection/DuplicateInstallAndTests/test1.py b/python/testData/inspections/PyPackageRequirementsInspection/DuplicateInstallAndTests/test1.py new file mode 100644 index 000000000000..854c6b1d6127 --- /dev/null +++ b/python/testData/inspections/PyPackageRequirementsInspection/DuplicateInstallAndTests/test1.py @@ -0,0 +1,2 @@ +print("Hello, World!") + \ No newline at end of file From 77a4936251c4912d74bbb5eae50411e7842aaf50 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Wed, 17 Jul 2013 19:31:53 +0400 Subject: [PATCH 2/5] Don't try to unify types of *args and **kwargs Specifying types for *args and **kwargs is not supported yet, so we shouldn't use them in type unification. --- python/src/com/jetbrains/python/psi/types/PyTypeChecker.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/python/src/com/jetbrains/python/psi/types/PyTypeChecker.java b/python/src/com/jetbrains/python/psi/types/PyTypeChecker.java index fa9565d5d894..6cc3e53ebf0e 100644 --- a/python/src/com/jetbrains/python/psi/types/PyTypeChecker.java +++ b/python/src/com/jetbrains/python/psi/types/PyTypeChecker.java @@ -253,6 +253,9 @@ public class PyTypeChecker { final Map substitutions = collectCallGenerics(function, receiver, context); for (Map.Entry entry : arguments.entrySet()) { final PyNamedParameter p = entry.getValue(); + if (p.isPositionalContainer() || p.isKeywordContainer()) { + continue; + } final PyType argType = context.getType(entry.getKey()); final PyType paramType = context.getType(p); if (!match(paramType, argType, context, substitutions)) { From d0f5d9a9f865c6dfb45a339dfb8591f668101d9f Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Fri, 19 Jul 2013 14:35:36 +0400 Subject: [PATCH 3/5] Support for square bracket syntax for parameterized types --- .../jetbrains/python/psi/types/PyType.flex | 2 +- .../python/psi/types/PyTypeLexer.java | 12 ++--- .../python/psi/types/PyTypeParser.java | 50 ++++++++++++++----- .../jetbrains/python/PyTypeParserTest.java | 31 ++++++++++++ 4 files changed, 76 insertions(+), 19 deletions(-) diff --git a/python/src/com/jetbrains/python/psi/types/PyType.flex b/python/src/com/jetbrains/python/psi/types/PyType.flex index d6fadb088bef..a0c74e16bff7 100644 --- a/python/src/com/jetbrains/python/psi/types/PyType.flex +++ b/python/src/com/jetbrains/python/psi/types/PyType.flex @@ -26,6 +26,6 @@ import static com.jetbrains.python.psi.types.PyTypeTokenTypes.*; [\r\n]+ { return NL; } [\ \t] { return SPACE; } (":py"?":class:`"[~!]?)|("`")|([A-Z]"{")|("}") { return MARKUP; } -("or")|("of")|("from")|("to")|[,\(\)\.] { return OP; } +("or")|("of")|("from")|("to")|[,\(\)\.\[\]] { return OP; } [T-Z] { return PARAMETER; } [A-Za-z_][A-Za-z_0-9]* { return IDENTIFIER; } diff --git a/python/src/com/jetbrains/python/psi/types/PyTypeLexer.java b/python/src/com/jetbrains/python/psi/types/PyTypeLexer.java index fb3a402870e3..3b3241b92e36 100644 --- a/python/src/com/jetbrains/python/psi/types/PyTypeLexer.java +++ b/python/src/com/jetbrains/python/psi/types/PyTypeLexer.java @@ -1,4 +1,4 @@ -/* The following code was generated by JFlex 1.4.3 on 6/24/13 5:23 PM */ +/* The following code was generated by JFlex 1.4.3 on 7/18/13 7:23 PM */ package com.jetbrains.python.psi.types; @@ -11,7 +11,7 @@ import static com.jetbrains.python.psi.types.PyTypeTokenTypes.*; /** * This class is a scanner generated by * JFlex 1.4.3 - * on 6/24/13 5:23 PM from the specification file + * on 7/18/13 7:23 PM from the specification file * /home/user/src/idea/tools/lexer/../../python/src/com/jetbrains/python/psi/types/PyType.flex */ public class PyTypeLexer implements FlexLexer { @@ -37,9 +37,9 @@ public class PyTypeLexer implements FlexLexer { private static final String ZZ_CMAP_PACKED = "\11\0\1\2\1\1\2\0\1\1\22\0\1\2\1\13\6\0\2\24"+ "\2\0\1\24\1\0\1\24\1\0\12\27\1\3\6\0\23\14\7\25"+ - "\4\0\1\26\1\12\1\10\1\26\1\6\2\26\1\21\5\26\1\7"+ - "\1\22\1\26\1\17\1\4\1\26\1\20\1\11\1\23\4\26\1\5"+ - "\1\26\1\15\1\0\1\16\1\13\uff81\0"; + "\1\24\1\0\1\24\1\0\1\26\1\12\1\10\1\26\1\6\2\26"+ + "\1\21\5\26\1\7\1\22\1\26\1\17\1\4\1\26\1\20\1\11"+ + "\1\23\4\26\1\5\1\26\1\15\1\0\1\16\1\13\uff81\0"; /** * Translates characters to character classes @@ -254,7 +254,7 @@ public class PyTypeLexer implements FlexLexer { char [] map = new char[0x10000]; int i = 0; /* index in packed string */ int j = 0; /* index in unpacked array */ - while (i < 92) { + while (i < 98) { int count = packed.charAt(i++); char value = packed.charAt(i++); do map[j++] = value; while (--count > 0); diff --git a/python/src/com/jetbrains/python/psi/types/PyTypeParser.java b/python/src/com/jetbrains/python/psi/types/PyTypeParser.java index c9a109e9c994..0967a86a644f 100644 --- a/python/src/com/jetbrains/python/psi/types/PyTypeParser.java +++ b/python/src/com/jetbrains/python/psi/types/PyTypeParser.java @@ -161,24 +161,50 @@ public class PyTypeParser { .named("simple-expr"); final FunctionalParser paramExpr = - simpleExpr.thenSkip(op("of")).then(simpleExpr) - .map(new Function, ParseResult>() { + simpleExpr.thenSkip(op("[")).then(typeExpr).then(many(op(",").skipThen(typeExpr))).thenSkip(op("]")) + .map(new Function, List>, ParseResult>() { @Override - public ParseResult fun(Pair value) { - final ParseResult firstResult = value.getFirst(); - final ParseResult secondResult = value.getSecond(); - final ParseResult result = firstResult.merge(secondResult); - final PyType firstType = firstResult.getType(); - final PyType secondType = secondResult.getType(); - if (firstType != null) { - if (firstType instanceof PyClassType && secondType != null) { - return result.withType(new PyCollectionTypeImpl(((PyClassType)firstType).getPyClass(), false, secondType)); + public ParseResult fun(Pair, List> value) { + final Pair firstPair = value.getFirst(); + final ParseResult first = firstPair.getFirst(); + final ParseResult second = firstPair.getSecond(); + final List third = value.getSecond(); + final PyType firstType = first.getType(); + if (firstType instanceof PyClassType) { + final List tupleTypes = new ArrayList(); + tupleTypes.add(second.getType()); + ParseResult result = first; + result = result.merge(second); + for (ParseResult r : third) { + tupleTypes.add(r.getType()); + result = result.merge(r); } - return result.withType(firstType); + final PyType elementType = third.isEmpty() ? second.getType() : + PyTupleType.create(anchor, tupleTypes.toArray(new PyType[tupleTypes.size()])); + final PyCollectionTypeImpl type = new PyCollectionTypeImpl(((PyClassType)firstType).getPyClass(), false, elementType); + return result.withType(type); } return EMPTY_RESULT; } }) + .or(simpleExpr.thenSkip(op("of")).then(simpleExpr) + .map(new Function, ParseResult>() { + @Override + public ParseResult fun(Pair value) { + final ParseResult firstResult = value.getFirst(); + final ParseResult secondResult = value.getSecond(); + final ParseResult result = firstResult.merge(secondResult); + final PyType firstType = firstResult.getType(); + final PyType secondType = secondResult.getType(); + if (firstType != null) { + if (firstType instanceof PyClassType && secondType != null) { + return result.withType(new PyCollectionTypeImpl(((PyClassType)firstType).getPyClass(), false, secondType)); + } + return result.withType(firstType); + } + return EMPTY_RESULT; + } + })) .or(simpleExpr.thenSkip(op("from")).then(simpleExpr).thenSkip(op("to")).then(simpleExpr) .map(new Function, ParseResult>, ParseResult>() { @Override diff --git a/python/testSrc/com/jetbrains/python/PyTypeParserTest.java b/python/testSrc/com/jetbrains/python/PyTypeParserTest.java index aacd1cb7f1fc..ed9dffea4ae0 100644 --- a/python/testSrc/com/jetbrains/python/PyTypeParserTest.java +++ b/python/testSrc/com/jetbrains/python/PyTypeParserTest.java @@ -184,4 +184,35 @@ public class PyTypeParserTest extends PyTestCase { final PyType bound = genericType.getBound(); assertInstanceOf(bound, PyUnionType.class); } + + public void testBracketSingleParam() { + myFixture.configureByFile("typeParser/typeParser.py"); + final PyType type = PyTypeParser.getTypeByName(myFixture.getFile(), "list[int]"); + assertInstanceOf(type, PyCollectionType.class); + final PyCollectionType collectionType = (PyCollectionType)type; + assertNotNull(collectionType); + assertEquals("list", collectionType.getName()); + final PyType elementType = collectionType.getElementType(TypeEvalContext.codeInsightFallback()); + assertNotNull(elementType); + assertEquals("int", elementType.getName()); + } + + public void testBracketMultipleParams() { + myFixture.configureByFile("typeParser/typeParser.py"); + final PyType type = PyTypeParser.getTypeByName(myFixture.getFile(), "dict[str, int]"); + assertInstanceOf(type, PyCollectionType.class); + final PyCollectionType collectionType = (PyCollectionType)type; + assertNotNull(collectionType); + assertEquals("dict", collectionType.getName()); + final PyType elementType = collectionType.getElementType(TypeEvalContext.codeInsightFallback()); + assertNotNull(elementType); + assertInstanceOf(elementType, PyTupleType.class); + final PyTupleType tupleType = (PyTupleType)elementType; + final PyType first = tupleType.getElementType(0); + assertNotNull(first); + assertEquals("str", first.getName()); + final PyType second = tupleType.getElementType(1); + assertNotNull(second); + assertEquals("int", second.getName()); + } } From 495a932d4d0ffcfebcd6c153370ece5abbb61894 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Fri, 19 Jul 2013 18:03:33 +0400 Subject: [PATCH 4/5] Rebuild stubs on creation of mock Python SDKs in unit tests --- python/testSrc/com/jetbrains/python/PythonMockSdk.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/python/testSrc/com/jetbrains/python/PythonMockSdk.java b/python/testSrc/com/jetbrains/python/PythonMockSdk.java index a1e1809d2c4a..4b7f5a57e466 100644 --- a/python/testSrc/com/jetbrains/python/PythonMockSdk.java +++ b/python/testSrc/com/jetbrains/python/PythonMockSdk.java @@ -7,6 +7,8 @@ import com.intellij.openapi.projectRoots.SdkType; import com.intellij.openapi.projectRoots.impl.ProjectJdkImpl; import com.intellij.openapi.roots.OrderRootType; import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.psi.stubs.StubUpdatingIndex; +import com.intellij.util.indexing.FileBasedIndex; import com.jetbrains.python.codeInsight.userSkeletons.PyUserSkeletonsUtil; import com.jetbrains.python.sdk.PythonSdkType; import org.jetbrains.annotations.NonNls; @@ -59,8 +61,9 @@ public class PythonMockSdk { String mock_stubs_path = mock_path + PythonSdkType.SKELETON_DIR_NAME; sdkModificator.addRoot(LocalFileSystem.getInstance().refreshAndFindFileByPath(mock_stubs_path), PythonSdkType.BUILTIN_ROOT_TYPE); - //PythonSdkType.setupSdkPaths(sdkModificator, null); sdkModificator.commitChanges(); + FileBasedIndex.getInstance().requestRebuild(StubUpdatingIndex.INDEX_ID); + return sdk; } } From dbaa838fd748906d90dcff745139e1b68bf42ba3 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Fri, 19 Jul 2013 18:04:45 +0400 Subject: [PATCH 5/5] Support for pipe operator syntax for union types --- .../src/com/jetbrains/python/psi/types/PyType.flex | 2 +- .../com/jetbrains/python/psi/types/PyTypeLexer.java | 6 +++--- .../com/jetbrains/python/psi/types/PyTypeParser.java | 2 +- .../com/jetbrains/python/PyTypeParserTest.java | 12 ++++++++++++ 4 files changed, 17 insertions(+), 5 deletions(-) diff --git a/python/src/com/jetbrains/python/psi/types/PyType.flex b/python/src/com/jetbrains/python/psi/types/PyType.flex index a0c74e16bff7..d340b102e809 100644 --- a/python/src/com/jetbrains/python/psi/types/PyType.flex +++ b/python/src/com/jetbrains/python/psi/types/PyType.flex @@ -26,6 +26,6 @@ import static com.jetbrains.python.psi.types.PyTypeTokenTypes.*; [\r\n]+ { return NL; } [\ \t] { return SPACE; } (":py"?":class:`"[~!]?)|("`")|([A-Z]"{")|("}") { return MARKUP; } -("or")|("of")|("from")|("to")|[,\(\)\.\[\]] { return OP; } +("or")|("of")|("from")|("to")|[,\(\)\.\[\]|] { return OP; } [T-Z] { return PARAMETER; } [A-Za-z_][A-Za-z_0-9]* { return IDENTIFIER; } diff --git a/python/src/com/jetbrains/python/psi/types/PyTypeLexer.java b/python/src/com/jetbrains/python/psi/types/PyTypeLexer.java index 3b3241b92e36..9d786f5373d1 100644 --- a/python/src/com/jetbrains/python/psi/types/PyTypeLexer.java +++ b/python/src/com/jetbrains/python/psi/types/PyTypeLexer.java @@ -1,4 +1,4 @@ -/* The following code was generated by JFlex 1.4.3 on 7/18/13 7:23 PM */ +/* The following code was generated by JFlex 1.4.3 on 7/19/13 2:41 PM */ package com.jetbrains.python.psi.types; @@ -11,7 +11,7 @@ import static com.jetbrains.python.psi.types.PyTypeTokenTypes.*; /** * This class is a scanner generated by * JFlex 1.4.3 - * on 7/18/13 7:23 PM from the specification file + * on 7/19/13 2:41 PM from the specification file * /home/user/src/idea/tools/lexer/../../python/src/com/jetbrains/python/psi/types/PyType.flex */ public class PyTypeLexer implements FlexLexer { @@ -39,7 +39,7 @@ public class PyTypeLexer implements FlexLexer { "\2\0\1\24\1\0\1\24\1\0\12\27\1\3\6\0\23\14\7\25"+ "\1\24\1\0\1\24\1\0\1\26\1\12\1\10\1\26\1\6\2\26"+ "\1\21\5\26\1\7\1\22\1\26\1\17\1\4\1\26\1\20\1\11"+ - "\1\23\4\26\1\5\1\26\1\15\1\0\1\16\1\13\uff81\0"; + "\1\23\4\26\1\5\1\26\1\15\1\24\1\16\1\13\uff81\0"; /** * Translates characters to character classes diff --git a/python/src/com/jetbrains/python/psi/types/PyTypeParser.java b/python/src/com/jetbrains/python/psi/types/PyTypeParser.java index 0967a86a644f..88d45ad11106 100644 --- a/python/src/com/jetbrains/python/psi/types/PyTypeParser.java +++ b/python/src/com/jetbrains/python/psi/types/PyTypeParser.java @@ -226,7 +226,7 @@ public class PyTypeParser { .named("param-expr"); final FunctionalParser unionExpr = - paramExpr.then(many(op("or").skipThen(paramExpr))) + paramExpr.then(many(op("or").or(op("|")).skipThen(paramExpr))) .map(new Function>, ParseResult>() { @Override public ParseResult fun(Pair> value) { diff --git a/python/testSrc/com/jetbrains/python/PyTypeParserTest.java b/python/testSrc/com/jetbrains/python/PyTypeParserTest.java index ed9dffea4ae0..178482300029 100644 --- a/python/testSrc/com/jetbrains/python/PyTypeParserTest.java +++ b/python/testSrc/com/jetbrains/python/PyTypeParserTest.java @@ -215,4 +215,16 @@ public class PyTypeParserTest extends PyTestCase { assertNotNull(second); assertEquals("int", second.getName()); } + + public void testUnionOrOperator() { + myFixture.configureByFile("typeParser/typeParser.py"); + final PyUnionType type = (PyUnionType)PyTypeParser.getTypeByName(myFixture.getFile(), "MyObject | str | unicode"); + assertNotNull(type); + final Collection members = type.getMembers(); + assertEquals(3, members.size()); + final List list = new ArrayList(members); + assertClassType(list.get(0), "MyObject"); + assertClassType(list.get(1), "str"); + assertClassType(list.get(2), "unicode"); + } }