@@ -13,8 +13,7 @@ import org.utbot.python.newtyping.*
1313import org.utbot.python.newtyping.ast.visitor.Visitor
1414import org.utbot.python.newtyping.ast.visitor.constants.ConstantCollector
1515import org.utbot.python.newtyping.ast.visitor.hints.HintCollector
16- import org.utbot.python.newtyping.general.FunctionType
17- import org.utbot.python.newtyping.general.Type
16+ import org.utbot.python.newtyping.general.*
1817import org.utbot.python.newtyping.inference.InferredTypeFeedback
1918import org.utbot.python.newtyping.inference.InvalidTypeFeedback
2019import org.utbot.python.newtyping.inference.SuccessFeedback
@@ -25,6 +24,7 @@ import org.utbot.python.newtyping.mypy.MypyReportLine
2524import org.utbot.python.newtyping.mypy.getErrorNumber
2625import org.utbot.python.newtyping.utils.getOffsetLine
2726import org.utbot.python.typing.MypyAnnotations
27+ import org.utbot.python.utils.PriorityCartesianProduct
2828import java.io.File
2929
3030private val logger = KotlinLogging .logger {}
@@ -44,16 +44,19 @@ class PythonTestCaseGenerator(
4444 private val mypyStorage : MypyAnnotationStorage ,
4545 private val mypyReportLine : List <MypyReportLine >,
4646 private val mypyConfigFile : File ,
47- ){
47+ ) {
4848
4949 private val storageForMypyMessages: MutableList <MypyAnnotations .MypyReportLine > = mutableListOf ()
5050
5151 private fun findMethodByDescription (mypyStorage : MypyAnnotationStorage , method : PythonMethodHeader ): PythonMethod {
52- val containingClass = method.containingPythonClassId
53- val functionDef = if (containingClass == null ) {
52+ var containingClass: CompositeType ? = null
53+ val containingClassName = method.containingPythonClassId?.simpleName
54+ val functionDef = if (containingClassName == null ) {
5455 mypyStorage.definitions[curModule]!! [method.name]!! .getUtBotDefinition()!!
5556 } else {
56- mypyStorage.definitions[curModule]!! [containingClass.simpleName]!! .type.asUtBotType.getPythonAttributes().first {
57+ containingClass =
58+ mypyStorage.definitions[curModule]!! [containingClassName]!! .getUtBotType() as CompositeType
59+ mypyStorage.definitions[curModule]!! [containingClassName]!! .type.asUtBotType.getPythonAttributes().first {
5760 it.meta.name == method.name
5861 }
5962 } as ? PythonFunctionDefinition ? : error(" Selected method is not a function definition" )
@@ -64,7 +67,7 @@ class PythonTestCaseGenerator(
6467 return PythonMethod (
6568 name = method.name,
6669 moduleFilename = method.moduleFilename,
67- containingPythonClassId = method.containingPythonClassId ,
70+ containingPythonClass = containingClass ,
6871 codeAsString = funcDef.body.source,
6972 definition = functionDef,
7073 ast = funcDef.body
@@ -84,13 +87,64 @@ class PythonTestCaseGenerator(
8487 } ? : emptyMap()
8588
8689 val namesStorage = GlobalNamesStorage (mypyStorage)
87- val hintCollector = HintCollector (method.definition, typeStorage, mypyExpressionTypes , namesStorage, curModule)
90+ val hintCollector = HintCollector (method.definition, typeStorage, mypyExpressionTypes, namesStorage, curModule)
8891 val constantCollector = ConstantCollector (typeStorage)
8992 val visitor = Visitor (listOf (hintCollector, constantCollector))
9093 visitor.visit(method.ast)
9194 return Pair (hintCollector, constantCollector)
9295 }
9396
97+ private fun getCandidates (param : TypeParameter , typeStorage : PythonTypeStorage ): List <Type > {
98+ val meta = param.pythonDescription() as PythonTypeVarDescription
99+ return when (meta.parameterKind) {
100+ PythonTypeVarDescription .ParameterKind .WithConcreteValues -> {
101+ param.constraints.map { it.boundary }
102+ }
103+ PythonTypeVarDescription .ParameterKind .WithUpperBound -> {
104+ typeStorage.simpleTypes.filter {
105+ if (it.hasBoundedParameters())
106+ return @filter false
107+ val bound = param.constraints.first().boundary
108+ PythonSubtypeChecker .checkIfRightIsSubtypeOfLeft(bound, it, typeStorage)
109+ }
110+ }
111+ }
112+ }
113+
114+ private val maxSubstitutions = 10
115+
116+ private fun generateTypesAfterSubstitution (type : Type , typeStorage : PythonTypeStorage ): List <Type > {
117+ val params = type.getBoundedParameters()
118+ return PriorityCartesianProduct (params.map { getCandidates(it, typeStorage) }).getSequence().map { subst ->
119+ DefaultSubstitutionProvider .substitute(type, (params zip subst).associate { it })
120+ }.take(maxSubstitutions).toList()
121+ }
122+
123+ private fun substituteTypeParameters (method : PythonMethod , typeStorage : PythonTypeStorage ): List <PythonMethod > {
124+ val newClasses =
125+ if (method.containingPythonClass != null ) {
126+ generateTypesAfterSubstitution(method.containingPythonClass, typeStorage)
127+ } else {
128+ listOf (null )
129+ }
130+ return newClasses.flatMap { newClass ->
131+ val funcType = newClass?.getPythonAttributeByName(typeStorage, method.name)?.type as ? FunctionType
132+ ? : method.definition.type
133+ val newFuncTypes = generateTypesAfterSubstitution(funcType, typeStorage)
134+ newFuncTypes.map { newFuncType ->
135+ val def = PythonFunctionDefinition (method.definition.meta, newFuncType as FunctionType )
136+ PythonMethod (
137+ method.name,
138+ method.moduleFilename,
139+ newClass as ? CompositeType ,
140+ method.codeAsString,
141+ def,
142+ method.ast
143+ )
144+ }
145+ }.take(maxSubstitutions)
146+ }
147+
94148 fun generate (methodDescription : PythonMethodHeader ): PythonTestSet {
95149 storageForMypyMessages.clear()
96150
@@ -108,65 +162,69 @@ class PythonTestCaseGenerator(
108162 var missingLines: Set <Int >? = null
109163 val coveredLines = mutableSetOf<Int >()
110164 var generated = 0
111- val typeInferenceCancellation = { isCancelled() || System .currentTimeMillis() >= until || missingLines?.size == 0 }
165+ val typeInferenceCancellation =
166+ { isCancelled() || System .currentTimeMillis() >= until || missingLines?.size == 0 }
112167
113- inferAnnotations(
114- method,
115- mypyStorage,
116- typeStorage,
117- hintCollector,
118- mypyReportLine,
119- mypyConfigFile,
120- typeInferenceCancellation
121- ) { functionType ->
122- val args = (functionType as FunctionType ).arguments
123-
124- logger.info { " Inferred annotations: ${ args.joinToString { it.pythonTypeRepresentation() } } " }
125-
126- val engine = PythonEngine (
127- method,
128- directoriesForSysPath,
129- curModule,
130- pythonPath,
131- constants,
132- timeoutForRun,
133- coveredLines,
134- PythonTypeStorage .get(mypyStorage)
135- )
136-
137- var coverageLimit = COVERAGE_LIMIT
138- var coveredBefore = coveredLines.size
139-
140- var feedback: InferredTypeFeedback = SuccessFeedback
141-
142- val fuzzerCancellation = { typeInferenceCancellation() || coverageLimit == 0 } // || feedback is InvalidTypeFeedback }
143-
144- engine.fuzzing(args, fuzzerCancellation, until).collect {
145- generated + = 1
146- when (it) {
147- is ValidExecution -> {
148- executions + = it.utFuzzedExecution
149- missingLines = updateCoverage(it.utFuzzedExecution, coveredLines, missingLines)
150- feedback = SuccessFeedback
151- }
152- is InvalidExecution -> {
153- errors + = it.utError
154- feedback = SuccessFeedback
155- }
156- is ArgumentsTypeErrorFeedback -> {
157- feedback = InvalidTypeFeedback
168+ substituteTypeParameters(method, typeStorage).forEach { newMethod ->
169+ inferAnnotations(
170+ newMethod,
171+ mypyStorage,
172+ typeStorage,
173+ hintCollector,
174+ mypyReportLine,
175+ mypyConfigFile,
176+ typeInferenceCancellation
177+ ) { functionType ->
178+ val args = (functionType as FunctionType ).arguments
179+
180+ logger.info { " Inferred annotations: ${args.joinToString { it.pythonTypeRepresentation() }} " }
181+
182+ val engine = PythonEngine (
183+ newMethod,
184+ directoriesForSysPath,
185+ curModule,
186+ pythonPath,
187+ constants,
188+ timeoutForRun,
189+ coveredLines,
190+ PythonTypeStorage .get(mypyStorage)
191+ )
192+
193+ var coverageLimit = COVERAGE_LIMIT
194+ var coveredBefore = coveredLines.size
195+
196+ var feedback: InferredTypeFeedback = SuccessFeedback
197+
198+ val fuzzerCancellation =
199+ { typeInferenceCancellation() || coverageLimit == 0 } // || feedback is InvalidTypeFeedback }
200+
201+ engine.fuzzing(args, fuzzerCancellation, until).collect {
202+ generated + = 1
203+ when (it) {
204+ is ValidExecution -> {
205+ executions + = it.utFuzzedExecution
206+ missingLines = updateCoverage(it.utFuzzedExecution, coveredLines, missingLines)
207+ feedback = SuccessFeedback
208+ }
209+ is InvalidExecution -> {
210+ errors + = it.utError
211+ feedback = SuccessFeedback
212+ }
213+ is ArgumentsTypeErrorFeedback -> {
214+ feedback = InvalidTypeFeedback
215+ }
216+ is TypeErrorFeedback -> {
217+ feedback = InvalidTypeFeedback
218+ }
158219 }
159- is TypeErrorFeedback -> {
160- feedback = InvalidTypeFeedback
220+ val coveredAfter = coveredLines.size
221+ if (coveredAfter == coveredBefore) {
222+ coverageLimit - = 1
161223 }
224+ coveredBefore = coveredAfter
162225 }
163- val coveredAfter = coveredLines.size
164- if (coveredAfter == coveredBefore) {
165- coverageLimit - = 1
166- }
167- coveredBefore = coveredAfter
226+ feedback
168227 }
169- feedback
170228 }
171229
172230
@@ -231,13 +289,13 @@ class PythonTestCaseGenerator(
231289 mypyConfigFile
232290 )
233291
234- runBlocking breaking@ {
292+ runBlocking breaking@{
235293 if (isCancelled()) {
236294 return @breaking
237295 }
238296
239297 val existsAnnotation = method.definition.type
240- if (existsAnnotation.arguments.all {it.pythonTypeName() != " typing.Any" }) {
298+ if (existsAnnotation.arguments.all { it.pythonTypeName() != " typing.Any" }) {
241299 annotationHandler(existsAnnotation)
242300 }
243301
0 commit comments