diff --git a/utbot-python/samples/easy_samples/field.py b/utbot-python/samples/easy_samples/field.py new file mode 100644 index 0000000000..55a4676041 --- /dev/null +++ b/utbot-python/samples/easy_samples/field.py @@ -0,0 +1,10 @@ +class NoTestsProblem: + def __init__(self): + self.board = [] + + def set_position(self, row, col, symbol): + self.board[row][col] = symbol + return symbol + + def start(self): + self.set_position(1, 2, "O") diff --git a/utbot-python/samples/easy_samples/general.py b/utbot-python/samples/easy_samples/general.py index 9076c442ed..a1d628f690 100644 --- a/utbot-python/samples/easy_samples/general.py +++ b/utbot-python/samples/easy_samples/general.py @@ -2,7 +2,7 @@ import heapq import typing from socket import socket -from typing import List, Dict, Set, Optional +from typing import List, Dict, Set, Optional, AbstractSet from dataclasses import dataclass import logging import datetime @@ -94,7 +94,7 @@ def empty(): return 1 -def id_(x): +def id_(x: AbstractSet[int]): return x diff --git a/utbot-python/src/main/kotlin/org/utbot/python/PythonEngine.kt b/utbot-python/src/main/kotlin/org/utbot/python/PythonEngine.kt index 840aa5aa05..aecc2be86a 100644 --- a/utbot-python/src/main/kotlin/org/utbot/python/PythonEngine.kt +++ b/utbot-python/src/main/kotlin/org/utbot/python/PythonEngine.kt @@ -11,6 +11,7 @@ import org.utbot.fuzzing.utils.Trie import org.utbot.python.evaluation.* import org.utbot.python.evaluation.serialiation.MemoryDump import org.utbot.python.evaluation.serialiation.toPythonTree +import org.utbot.python.framework.api.python.PythonTree import org.utbot.python.framework.api.python.PythonTreeModel import org.utbot.python.framework.api.python.PythonTreeWrapper import org.utbot.python.fuzzing.* @@ -259,6 +260,12 @@ class PythonEngine( return@PythonFuzzing PythonFeedback(control = Control.STOP) } + if (arguments.any { PythonTree.containsFakeNode(it.tree) }) { + logger.debug("FakeNode in Python model") + emit(InvalidExecution(UtError("Bad input object", Throwable()))) + return@PythonFuzzing PythonFeedback(control = Control.CONTINUE) + } + val pair = Pair(description, arguments.map { PythonTreeWrapper(it.tree) }) val mem = cache.get(pair) if (mem != null) { @@ -278,7 +285,7 @@ class PythonEngine( return@PythonFuzzing result.fuzzingPlatformFeedback }.fuzz(pmd) } catch (_: Exception) { // e.g. NoSeedValueException - logger.info { "Cannot fuzz values for types: $parameters" } + logger.info { "Cannot fuzz values for types: ${parameters.map { it.pythonTypeRepresentation() }}" } } } manager.disconnect() diff --git a/utbot-python/src/main/kotlin/org/utbot/python/PythonTestCaseGenerator.kt b/utbot-python/src/main/kotlin/org/utbot/python/PythonTestCaseGenerator.kt index 73c07d00ee..44872ef545 100644 --- a/utbot-python/src/main/kotlin/org/utbot/python/PythonTestCaseGenerator.kt +++ b/utbot-python/src/main/kotlin/org/utbot/python/PythonTestCaseGenerator.kt @@ -29,6 +29,7 @@ import org.utbot.python.utils.TimeoutMode import java.io.File private val logger = KotlinLogging.logger {} +private const val RANDOM_TYPE_FREQUENCY = 6 class PythonTestCaseGenerator( private val withMinimization: Boolean = true, @@ -117,14 +118,21 @@ class PythonTestCaseGenerator( }.take(maxSubstitutions) } - fun generate(method: PythonMethod, until: Long): PythonTestSet { - storageForMypyMessages.clear() + private fun methodHandler( + method: PythonMethod, + typeStorage: PythonTypeStorage, + coveredLines: MutableSet, + errors: MutableList, + executions: MutableList, + initMissingLines: Set?, + until: Long, + additionalVars: String = "" + ): Set? { // returns missing lines val limitManager = TestGenerationLimitManager( ExecutionWithTimeoutMode, until, ) - - val typeStorage = PythonTypeStorage.get(mypyStorage) + var missingLines = initMissingLines val (hintCollector, constantCollector) = constructCollectors(mypyStorage, typeStorage, method) val constants = constantCollector.result.map { (type, value) -> @@ -132,13 +140,6 @@ class PythonTestCaseGenerator( PythonFuzzedConcreteValue(type, value) } - val executions = mutableListOf() - val errors = mutableListOf() - var missingLines: Set? = null - val coveredLines = mutableSetOf() - var generated = 0 - - logger.info("Start test generation for ${method.name}") substituteTypeParameters(method, typeStorage).forEach { newMethod -> inferAnnotations( newMethod, @@ -148,6 +149,7 @@ class PythonTestCaseGenerator( mypyReportLine, mypyConfigFile, limitManager, + additionalVars ) { functionType -> val args = (functionType as FunctionType).arguments @@ -168,7 +170,6 @@ class PythonTestCaseGenerator( val fuzzerCancellation = { isCancelled() || limitManager.isCancelled() } engine.fuzzing(args, fuzzerCancellation, until).collect { - generated += 1 when (it) { is ValidExecution -> { executions += it.utFuzzedExecution @@ -196,6 +197,42 @@ class PythonTestCaseGenerator( feedback } } + return missingLines + } + + fun generate(method: PythonMethod, until: Long): PythonTestSet { + storageForMypyMessages.clear() + + val typeStorage = PythonTypeStorage.get(mypyStorage) + + val executions = mutableListOf() + val errors = mutableListOf() + val coveredLines = mutableSetOf() + + logger.info("Start test generation for ${method.name}") + val meta = method.definition.type.pythonDescription() as PythonCallableTypeDescription + val argKinds = meta.argumentKinds + if (argKinds.any { it != PythonCallableTypeDescription.ArgKind.ARG_POS }) { + val now = System.currentTimeMillis() + val firstUntil = (until - now) / 2 + now + val originalDef = method.definition + val shortType = meta.removeNonPositionalArgs(originalDef.type) + val shortMeta = PythonFuncItemDescription( + originalDef.meta.name, + originalDef.meta.args.take(shortType.arguments.size) + ) + val additionalVars = originalDef.meta.args + .drop(shortType.arguments.size) + .joinToString(separator="\n", prefix="\n") { arg -> + "${arg.name}: ${pythonAnyType.pythonTypeRepresentation()}" // TODO: better types + } + method.definition = PythonFunctionDefinition(shortMeta, shortType) + val missingLines = methodHandler(method, typeStorage, coveredLines, errors, executions, null, firstUntil, additionalVars) + method.definition = originalDef + methodHandler(method, typeStorage, coveredLines, errors, executions, missingLines, until) + } else { + methodHandler(method, typeStorage, coveredLines, errors, executions, null, until) + } logger.info("Collect all test executions for ${method.name}") val (successfulExecutions, failedExecutions) = executions.partition { it.result is UtExecutionSuccess } @@ -234,6 +271,7 @@ class PythonTestCaseGenerator( report: List, mypyConfigFile: File, limitManager: TestGenerationLimitManager, + additionalVars: String, annotationHandler: suspend (Type) -> InferredTypeFeedback, ) { val namesInModule = mypyStorage.names @@ -257,7 +295,9 @@ class PythonTestCaseGenerator( getOffsetLine(sourceFileContent, method.ast.beginOffset), getOffsetLine(sourceFileContent, method.ast.endOffset) ), - mypyConfigFile + mypyConfigFile, + additionalVars, + randomTypeFrequency = RANDOM_TYPE_FREQUENCY ) runBlocking breaking@{ diff --git a/utbot-python/src/main/kotlin/org/utbot/python/code/CodeGen.kt b/utbot-python/src/main/kotlin/org/utbot/python/code/CodeGen.kt index 8ec18a2997..cd2450c602 100644 --- a/utbot-python/src/main/kotlin/org/utbot/python/code/CodeGen.kt +++ b/utbot-python/src/main/kotlin/org/utbot/python/code/CodeGen.kt @@ -45,7 +45,8 @@ object PythonCodeGenerator { methodAnnotations: Map, directoriesForSysPath: Set, moduleToImport: String, - namesInModule: Collection + namesInModule: Collection, + additionalVars: String ): String { val context = UtContext(this::class.java.classLoader) withUtContext(context) { @@ -60,7 +61,8 @@ object PythonCodeGenerator { methodAnnotations, directoriesForSysPath, moduleToImport, - namesInModule + namesInModule, + additionalVars ) } } diff --git a/utbot-python/src/main/kotlin/org/utbot/python/framework/api/python/PythonTree.kt b/utbot-python/src/main/kotlin/org/utbot/python/framework/api/python/PythonTree.kt index e07015daab..245d8c2c32 100644 --- a/utbot-python/src/main/kotlin/org/utbot/python/framework/api/python/PythonTree.kt +++ b/utbot-python/src/main/kotlin/org/utbot/python/framework/api/python/PythonTree.kt @@ -26,6 +26,19 @@ object PythonTree { return tree.children.any { isRecursiveObjectDFS(it, visited) } } + fun containsFakeNode(tree: PythonTreeNode): Boolean { + return containsFakeNodeDFS(tree, mutableSetOf()) + } + + private fun containsFakeNodeDFS(tree: PythonTreeNode, visited: MutableSet): Boolean { + if (visited.contains(tree)) + return false + if (tree is FakeNode) + return true + visited.add(tree) + return tree.children.any { containsFakeNodeDFS(it, visited) } + } + open class PythonTreeNode( val id: Long, val type: PythonClassId, @@ -69,6 +82,8 @@ object PythonTree { 1 + children.fold(0) { acc, child -> acc + child.diversity() } } + object FakeNode: PythonTreeNode(0L, PythonClassId("")) + class PrimitiveNode( id: Long, type: PythonClassId, diff --git a/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/PythonCodeGenerator.kt b/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/PythonCodeGenerator.kt index 8bb24450a9..e991aaaf0b 100644 --- a/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/PythonCodeGenerator.kt +++ b/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/PythonCodeGenerator.kt @@ -211,7 +211,8 @@ class PythonCodeGenerator( methodAnnotations: Map, directoriesForSysPath: Set, moduleToImport: String, - namesInModule: Collection + namesInModule: Collection, + additionalVars: String ): String { val cgRendererContext = CgRendererContext.fromCgContext(context) val printer = CgPrinterImpl() @@ -243,6 +244,8 @@ class PythonCodeGenerator( val mypyCheckCode = listOf( renderer.toString(), "", + additionalVars, + "", functionName, ) + method.codeAsString.split("\n").map { " $it" } return mypyCheckCode.joinToString("\n") diff --git a/utbot-python/src/main/kotlin/org/utbot/python/fuzzing/PythonApi.kt b/utbot-python/src/main/kotlin/org/utbot/python/fuzzing/PythonApi.kt index 9d75a2f453..d87c40a3da 100644 --- a/utbot-python/src/main/kotlin/org/utbot/python/fuzzing/PythonApi.kt +++ b/utbot-python/src/main/kotlin/org/utbot/python/fuzzing/PythonApi.kt @@ -16,11 +16,10 @@ import org.utbot.fuzzing.utils.Trie import org.utbot.python.framework.api.python.PythonTree import org.utbot.python.fuzzing.provider.* import org.utbot.python.fuzzing.provider.utils.isAny -import org.utbot.python.newtyping.PythonProtocolDescription -import org.utbot.python.newtyping.PythonSubtypeChecker -import org.utbot.python.newtyping.PythonTypeStorage +import org.utbot.python.fuzzing.provider.utils.isConcreteType +import org.utbot.python.newtyping.* +import org.utbot.python.newtyping.general.DefaultSubstitutionProvider import org.utbot.python.newtyping.general.Type -import org.utbot.python.newtyping.pythonTypeRepresentation private val logger = KotlinLogging.logger {} @@ -59,7 +58,7 @@ class PythonFuzzedValue( val summary: String? = null, ) -fun pythonDefaultValueProviders() = listOf( +fun pythonDefaultValueProviders(typeStorage: PythonTypeStorage) = listOf( NoneValueProvider, BoolValueProvider, IntValueProvider, @@ -76,7 +75,8 @@ fun pythonDefaultValueProviders() = listOf( BytearrayValueProvider, ReduceValueProvider, ConstantValueProvider, - TypeAliasValueProvider + TypeAliasValueProvider, + SubtypeValueProvider(typeStorage) ) class PythonFuzzing( @@ -84,28 +84,13 @@ class PythonFuzzing( val execute: suspend (description: PythonMethodDescription, values: List) -> PythonFeedback, ) : Fuzzing { - private fun generateDefault(description: PythonMethodDescription, type: Type): Sequence> { - var providers = emptyList>().asSequence() - pythonDefaultValueProviders().asSequence().forEach { provider -> + private fun generateDefault(description: PythonMethodDescription, type: Type)= sequence> { + pythonDefaultValueProviders(pythonTypeStorage).asSequence().forEach { provider -> if (provider.accept(type)) { logger.debug { "Provider ${provider.javaClass.simpleName} accepts type ${type.pythonTypeRepresentation()}" } - providers += provider.generate(description, type) + yieldAll(provider.generate(description, type)) } } - return providers - } - - private fun generateSubtype(description: PythonMethodDescription, type: Type): Sequence> { - var providers = emptyList>().asSequence() - if (type.meta is PythonProtocolDescription) { - val subtypes = pythonTypeStorage.allTypes.filter { - PythonSubtypeChecker.checkIfRightIsSubtypeOfLeft(type, it, pythonTypeStorage) - } - subtypes.forEach { - providers += generateDefault(description, it) - } - } - return providers } override fun generate(description: PythonMethodDescription, type: Type): Sequence> { @@ -115,7 +100,6 @@ class PythonFuzzing( logger.debug("Any does not have provider") } else { providers += generateDefault(description, type) - providers += generateSubtype(description, type) } return providers diff --git a/utbot-python/src/main/kotlin/org/utbot/python/fuzzing/provider/ReduceValueProvider.kt b/utbot-python/src/main/kotlin/org/utbot/python/fuzzing/provider/ReduceValueProvider.kt index cb5ef0c9c4..9aec8b9599 100644 --- a/utbot-python/src/main/kotlin/org/utbot/python/fuzzing/provider/ReduceValueProvider.kt +++ b/utbot-python/src/main/kotlin/org/utbot/python/fuzzing/provider/ReduceValueProvider.kt @@ -8,6 +8,7 @@ import org.utbot.python.framework.api.python.PythonTree import org.utbot.python.framework.api.python.util.toPythonRepr import org.utbot.python.fuzzing.PythonFuzzedValue import org.utbot.python.fuzzing.PythonMethodDescription +import org.utbot.python.fuzzing.provider.utils.isConcreteType import org.utbot.python.newtyping.* import org.utbot.python.newtyping.general.FunctionType import org.utbot.python.newtyping.general.Type @@ -30,7 +31,7 @@ object ReduceValueProvider : ValueProvider { + override fun accept(type: Type): Boolean { + return type.meta is PythonProtocolDescription || + ((type.meta as? PythonConcreteCompositeTypeDescription)?.isAbstract == true) + } + + private val concreteTypes = typeStorage.allTypes.filter { + isConcreteType(it) && it.pythonDescription().name.name.first() != '_' // Don't substitute private classes + }.map { + DefaultSubstitutionProvider.substituteAll(it, it.parameters.map { pythonAnyType }) + } + + override fun generate(description: PythonMethodDescription, type: Type) = sequence { + val subtypes = concreteTypes.filter { checkIfRightIsSubtypeOfLeft(type, it, typeStorage) } + subtypes.forEach { subtype -> + yield( + Seed.Recursive( + construct = Routine.Create(listOf(subtype)) { v -> v.first() }, + empty = Routine.Empty { PythonFuzzedValue(PythonTree.FakeNode) } + )) + } + } +} \ No newline at end of file diff --git a/utbot-python/src/main/kotlin/org/utbot/python/fuzzing/provider/TypeAliasValueProvider.kt b/utbot-python/src/main/kotlin/org/utbot/python/fuzzing/provider/TypeAliasValueProvider.kt index 53f906ed34..e32cbcfd94 100644 --- a/utbot-python/src/main/kotlin/org/utbot/python/fuzzing/provider/TypeAliasValueProvider.kt +++ b/utbot-python/src/main/kotlin/org/utbot/python/fuzzing/provider/TypeAliasValueProvider.kt @@ -21,12 +21,7 @@ object TypeAliasValueProvider : ValueProvider v.first() }, - empty = Routine.Empty { - PythonFuzzedValue( - PythonTree.fromObject(), - "%var% = ${type.pythonTypeRepresentation()}" - ) - } + empty = Routine.Empty { PythonFuzzedValue(PythonTree.FakeNode) } ) ) } diff --git a/utbot-python/src/main/kotlin/org/utbot/python/fuzzing/provider/UnionValueProvider.kt b/utbot-python/src/main/kotlin/org/utbot/python/fuzzing/provider/UnionValueProvider.kt index d376900519..4cac17f912 100644 --- a/utbot-python/src/main/kotlin/org/utbot/python/fuzzing/provider/UnionValueProvider.kt +++ b/utbot-python/src/main/kotlin/org/utbot/python/fuzzing/provider/UnionValueProvider.kt @@ -21,10 +21,7 @@ object UnionValueProvider : ValueProvider yield(Seed.Recursive( construct = Routine.Create(listOf(unionParam)) { v -> v.first() }, - empty = Routine.Empty { PythonFuzzedValue( - PythonTree.fromObject(), - "%var% = ${unionParam.meta} from ${type.pythonTypeRepresentation()}", - )} + empty = Routine.Empty { PythonFuzzedValue(PythonTree.FakeNode) } )) } } diff --git a/utbot-python/src/main/kotlin/org/utbot/python/fuzzing/provider/utils/ProviderUtils.kt b/utbot-python/src/main/kotlin/org/utbot/python/fuzzing/provider/utils/ProviderUtils.kt index f797cb4b48..80513fed77 100644 --- a/utbot-python/src/main/kotlin/org/utbot/python/fuzzing/provider/utils/ProviderUtils.kt +++ b/utbot-python/src/main/kotlin/org/utbot/python/fuzzing/provider/utils/ProviderUtils.kt @@ -5,6 +5,7 @@ import org.utbot.python.framework.api.python.PythonTree import org.utbot.python.fuzzing.PythonFuzzedValue import org.utbot.python.fuzzing.PythonMethodDescription import org.utbot.python.newtyping.PythonAnyTypeDescription +import org.utbot.python.newtyping.PythonConcreteCompositeTypeDescription import org.utbot.python.newtyping.PythonSubtypeChecker import org.utbot.python.newtyping.general.Type @@ -20,4 +21,8 @@ fun getSuitableConstantsFromCode(description: PythonMethodDescription, type: Typ Seed.Simple(PythonFuzzedValue(it)) } } +} + +fun isConcreteType(type: Type): Boolean { + return (type.meta as? PythonConcreteCompositeTypeDescription)?.isAbstract == false } \ No newline at end of file diff --git a/utbot-python/src/main/kotlin/org/utbot/python/newtyping/PythonType.kt b/utbot-python/src/main/kotlin/org/utbot/python/newtyping/PythonType.kt index 939e4d321e..a5ea8a0eb9 100644 --- a/utbot-python/src/main/kotlin/org/utbot/python/newtyping/PythonType.kt +++ b/utbot-python/src/main/kotlin/org/utbot/python/newtyping/PythonType.kt @@ -200,6 +200,26 @@ class PythonCallableTypeDescription( functionType.arguments.joinToString(separator = ", ") { it.pythonTypeRepresentation() } }], ${functionType.returnValue.pythonTypeRepresentation()}]" } + + fun removeNonPositionalArgs(type: Type): FunctionType { + val functionType = castToCompatibleTypeApi(type) + val argsCount = argumentKinds.count { it == ArgKind.ARG_POS } + return createPythonCallableType( + functionType.parameters.size, + argumentKinds.take(argsCount), + argumentNames.take(argsCount) + ) { self -> + val substitution = (functionType.parameters zip self.parameters).associate { + Pair(it.first as TypeParameter, it.second) + } + FunctionTypeCreator.InitializationData( + functionType.arguments.take(argsCount).map { + DefaultSubstitutionProvider.substitute(it, substitution) + }, + DefaultSubstitutionProvider.substitute(functionType.returnValue, substitution) + ) + } + } } // Special Python annotations diff --git a/utbot-python/src/main/kotlin/org/utbot/python/newtyping/ast/test.kt b/utbot-python/src/main/kotlin/org/utbot/python/newtyping/ast/test.kt index 93b15589c4..e9bd46d2ca 100644 --- a/utbot-python/src/main/kotlin/org/utbot/python/newtyping/ast/test.kt +++ b/utbot-python/src/main/kotlin/org/utbot/python/newtyping/ast/test.kt @@ -8,7 +8,7 @@ fun main() { val content = """ class A: @decorator - def func(x): + def func(x, y = 1, *args, **kwargs): return 1 """.trimIndent() diff --git a/utbot-python/src/main/kotlin/org/utbot/python/newtyping/inference/TypeInferenceProcessor.kt b/utbot-python/src/main/kotlin/org/utbot/python/newtyping/inference/TypeInferenceProcessor.kt index ef7d2f6f2d..854fe97226 100644 --- a/utbot-python/src/main/kotlin/org/utbot/python/newtyping/inference/TypeInferenceProcessor.kt +++ b/utbot-python/src/main/kotlin/org/utbot/python/newtyping/inference/TypeInferenceProcessor.kt @@ -101,7 +101,8 @@ class TypeInferenceProcessor( getOffsetLine(sourceFileContent, pythonMethod.ast.beginOffset), getOffsetLine(sourceFileContent, pythonMethod.ast.endOffset) ), - configFile + configFile, + "" ) startingTypeInferenceAction() diff --git a/utbot-python/src/main/kotlin/org/utbot/python/newtyping/inference/baseline/BaselineAlgorithm.kt b/utbot-python/src/main/kotlin/org/utbot/python/newtyping/inference/baseline/BaselineAlgorithm.kt index 48aa0cae02..b4a5118c94 100644 --- a/utbot-python/src/main/kotlin/org/utbot/python/newtyping/inference/baseline/BaselineAlgorithm.kt +++ b/utbot-python/src/main/kotlin/org/utbot/python/newtyping/inference/baseline/BaselineAlgorithm.kt @@ -31,7 +31,9 @@ class BaselineAlgorithm( private val moduleToImport: String, private val namesInModule: Collection, private val initialErrorNumber: Int, - private val configFile: File + private val configFile: File, + private val additionalVars: String, + private val randomTypeFrequency: Int = 0 ) : TypeInferenceAlgorithm() { private val random = Random(0) @@ -46,6 +48,9 @@ class BaselineAlgorithm( val fileForMypyRuns = TemporaryFileManager.assignTemporaryFile(tag = "mypy.py") var iterationCounter = 0 + val simpleTypes = simplestTypes(storage) + val mixtureType = createPythonUnionType(simpleTypes) + run breaking@ { while (states.isNotEmpty()) { if (isCancelled()) @@ -53,6 +58,16 @@ class BaselineAlgorithm( logger.debug("State number: ${states.size}") iterationCounter++ + if (randomTypeFrequency > 0 && iterationCounter % randomTypeFrequency == 0) { + val weights = states.map { 1.0 / (it.anyNodes.size * it.anyNodes.size + 1) } + val state = weightedRandom(states, weights, random) + val newState = expandState(state, storage, state.anyNodes.map { mixtureType }) + if (newState != null) { + logger.info("Random type: ${newState.signature.pythonTypeRepresentation()}") + annotationHandler(newState.signature) + } + } + val state = chooseState(states) val newState = expandState(state, storage) if (newState != null) { @@ -93,7 +108,8 @@ class BaselineAlgorithm( fileForMypyRuns, pythonPath, configFile, - initialErrorNumber + initialErrorNumber, + additionalVars ) } diff --git a/utbot-python/src/main/kotlin/org/utbot/python/newtyping/inference/baseline/StateExpansion.kt b/utbot-python/src/main/kotlin/org/utbot/python/newtyping/inference/baseline/StateExpansion.kt index 7be94fbf3e..e343d94185 100644 --- a/utbot-python/src/main/kotlin/org/utbot/python/newtyping/inference/baseline/StateExpansion.kt +++ b/utbot-python/src/main/kotlin/org/utbot/python/newtyping/inference/baseline/StateExpansion.kt @@ -12,6 +12,12 @@ fun expandState(state: BaselineAlgorithmState, typeStorage: PythonTypeStorage): if (state.anyNodes.isEmpty()) return null val types = state.candidateGraph.getNext() ?: return null + return expandState(state, typeStorage, types) +} + +fun expandState(state: BaselineAlgorithmState, typeStorage: PythonTypeStorage, types: List): BaselineAlgorithmState? { + if (types.isEmpty()) + return null val substitution = (state.anyNodes zip types).associate { it } return expandNodes(state, substitution, state.generalRating, typeStorage) } diff --git a/utbot-python/src/main/kotlin/org/utbot/python/newtyping/inference/baseline/TypeCandidateGeneration.kt b/utbot-python/src/main/kotlin/org/utbot/python/newtyping/inference/baseline/TypeCandidateGeneration.kt index fb7aa6af6d..e6f41a26fa 100644 --- a/utbot-python/src/main/kotlin/org/utbot/python/newtyping/inference/baseline/TypeCandidateGeneration.kt +++ b/utbot-python/src/main/kotlin/org/utbot/python/newtyping/inference/baseline/TypeCandidateGeneration.kt @@ -158,6 +158,16 @@ fun createTypeRating( return TypeRating(scores) } +fun simplestTypes(storage: PythonTypeStorage): List { + val int = storage.pythonInt + val listOfAny = DefaultSubstitutionProvider.substituteAll(storage.pythonList, listOf(pythonAnyType)) + val str = storage.pythonStr + val bool = storage.pythonBool + val float = storage.pythonFloat + val dictOfAny = DefaultSubstitutionProvider.substituteAll(storage.pythonDict, listOf(pythonAnyType, pythonAnyType)) + return listOf(int, listOfAny, str, bool, float, dictOfAny) +} + fun createGeneralTypeRating(hintCollectorResult: HintCollectorResult, storage: PythonTypeStorage): List { val allLowerBounds: MutableList = mutableListOf() val allUpperBounds: MutableList = mutableListOf() @@ -170,13 +180,7 @@ fun createGeneralTypeRating(hintCollectorResult: HintCollectorResult, storage: P !typesAreEqual(it, pythonAnyType) }) } - val int = storage.pythonInt - val listOfAny = DefaultSubstitutionProvider.substituteAll(storage.pythonList, listOf(pythonAnyType)) - val str = storage.pythonStr - val bool = storage.pythonBool - val float = storage.pythonFloat - val dictOfAny = DefaultSubstitutionProvider.substituteAll(storage.pythonDict, listOf(pythonAnyType, pythonAnyType)) - val prefix = listOf(int, listOfAny, str, bool, float, dictOfAny) + val prefix = simplestTypes(storage) val rating = createTypeRating( storage.simpleTypes.filter { !prefix.any { type -> typesAreEqual(type.getOrigin(), it) } }, allLowerBounds, diff --git a/utbot-python/src/main/kotlin/org/utbot/python/newtyping/mypy/RunMypy.kt b/utbot-python/src/main/kotlin/org/utbot/python/newtyping/mypy/RunMypy.kt index 56a1af6c9b..b707d81638 100644 --- a/utbot-python/src/main/kotlin/org/utbot/python/newtyping/mypy/RunMypy.kt +++ b/utbot-python/src/main/kotlin/org/utbot/python/newtyping/mypy/RunMypy.kt @@ -83,9 +83,11 @@ fun setConfigFile(directoriesForSysPath: Set): File { show_absolute_path = True cache_fine_grained = True check_untyped_defs = True - strict_optional = False disable_error_code = assignment,union-attr implicit_optional = True + strict_optional = False + allow_redefinition = True + local_partial_types = True """.trimIndent() TemporaryFileManager.writeToAssignedFile(file, configContent) return file @@ -99,13 +101,14 @@ fun checkSuggestedSignatureWithDMypy( fileForMypyCode: File, pythonPath: String, configFile: File, - initialErrorNumber: Int + initialErrorNumber: Int, + additionalVars: String ): Boolean { val annotationMap = (method.definition.meta.args.map { it.name } zip method.definition.type.arguments).associate { Pair(it.first, it.second) } - val mypyCode = generateMypyCheckCode(method, annotationMap, directoriesForSysPath, moduleToImport, namesInModule) + val mypyCode = generateMypyCheckCode(method, annotationMap, directoriesForSysPath, moduleToImport, namesInModule, additionalVars) // logger.debug(mypyCode) TemporaryFileManager.writeToAssignedFile(fileForMypyCode, mypyCode) val mypyOutput = checkWithDMypy(pythonPath, fileForMypyCode.canonicalPath, configFile) diff --git a/utbot-python/src/test/kotlin/org/utbot/python/newtyping/PythonSubtypeCheckerTest.kt b/utbot-python/src/test/kotlin/org/utbot/python/newtyping/PythonSubtypeCheckerTest.kt index 2a538b519f..58f874fa4a 100644 --- a/utbot-python/src/test/kotlin/org/utbot/python/newtyping/PythonSubtypeCheckerTest.kt +++ b/utbot-python/src/test/kotlin/org/utbot/python/newtyping/PythonSubtypeCheckerTest.kt @@ -116,4 +116,16 @@ internal class PythonSubtypeCheckerTest { assertTrue(checkIfRightIsSubtypeOfLeft(tupleOfAny, tupleOfIntAndFloat, pythonTypeStorage)) assertFalse(checkIfRightIsSubtypeOfLeft(tupleOfInt, tupleOfIntAndFloat, pythonTypeStorage)) } + + @Test + fun testAbstractSet() { + val abstractSet = storage.definitions["typing"]!!["AbstractSet"]!!.getUtBotType() + assertTrue((abstractSet.pythonDescription() as PythonConcreteCompositeTypeDescription).isAbstract) + val set = storage.definitions["builtins"]!!["set"]!!.getUtBotType() + + val abstractSetOfAny = DefaultSubstitutionProvider.substituteByIndex(abstractSet, 0, pythonAnyType) + val setOfAny = DefaultSubstitutionProvider.substituteByIndex(set, 0, pythonAnyType) + + assertTrue(checkIfRightIsSubtypeOfLeft(abstractSetOfAny, setOfAny, pythonTypeStorage)) + } } \ No newline at end of file