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/src/com/jetbrains/python/psi/types/PyType.flex b/python/src/com/jetbrains/python/psi/types/PyType.flex index d6fadb088bef..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/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)) { diff --git a/python/src/com/jetbrains/python/psi/types/PyTypeLexer.java b/python/src/com/jetbrains/python/psi/types/PyTypeLexer.java index fb3a402870e3..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 6/24/13 5: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 6/24/13 5: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 { @@ -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\24\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..88d45ad11106 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 @@ -200,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/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 diff --git a/python/testSrc/com/jetbrains/python/PyTypeParserTest.java b/python/testSrc/com/jetbrains/python/PyTypeParserTest.java index aacd1cb7f1fc..178482300029 100644 --- a/python/testSrc/com/jetbrains/python/PyTypeParserTest.java +++ b/python/testSrc/com/jetbrains/python/PyTypeParserTest.java @@ -184,4 +184,47 @@ 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()); + } + + 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"); + } } 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; } }