@@ -13,8 +13,7 @@ import org.utbot.python.newtyping.*
13
13
import org.utbot.python.newtyping.ast.visitor.Visitor
14
14
import org.utbot.python.newtyping.ast.visitor.constants.ConstantCollector
15
15
import 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.*
18
17
import org.utbot.python.newtyping.inference.InferredTypeFeedback
19
18
import org.utbot.python.newtyping.inference.InvalidTypeFeedback
20
19
import org.utbot.python.newtyping.inference.SuccessFeedback
@@ -25,6 +24,7 @@ import org.utbot.python.newtyping.mypy.MypyReportLine
25
24
import org.utbot.python.newtyping.mypy.getErrorNumber
26
25
import org.utbot.python.newtyping.utils.getOffsetLine
27
26
import org.utbot.python.typing.MypyAnnotations
27
+ import org.utbot.python.utils.PriorityCartesianProduct
28
28
import java.io.File
29
29
30
30
private val logger = KotlinLogging .logger {}
@@ -44,16 +44,19 @@ class PythonTestCaseGenerator(
44
44
private val mypyStorage : MypyAnnotationStorage ,
45
45
private val mypyReportLine : List <MypyReportLine >,
46
46
private val mypyConfigFile : File ,
47
- ){
47
+ ) {
48
48
49
49
private val storageForMypyMessages: MutableList <MypyAnnotations .MypyReportLine > = mutableListOf ()
50
50
51
51
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 ) {
54
55
mypyStorage.definitions[curModule]!! [method.name]!! .getUtBotDefinition()!!
55
56
} 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 {
57
60
it.meta.name == method.name
58
61
}
59
62
} as ? PythonFunctionDefinition ? : error(" Selected method is not a function definition" )
@@ -64,7 +67,7 @@ class PythonTestCaseGenerator(
64
67
return PythonMethod (
65
68
name = method.name,
66
69
moduleFilename = method.moduleFilename,
67
- containingPythonClassId = method.containingPythonClassId ,
70
+ containingPythonClass = containingClass ,
68
71
codeAsString = funcDef.body.source,
69
72
definition = functionDef,
70
73
ast = funcDef.body
@@ -84,13 +87,64 @@ class PythonTestCaseGenerator(
84
87
} ? : emptyMap()
85
88
86
89
val namesStorage = GlobalNamesStorage (mypyStorage)
87
- val hintCollector = HintCollector (method.definition, typeStorage, mypyExpressionTypes , namesStorage, curModule)
90
+ val hintCollector = HintCollector (method.definition, typeStorage, mypyExpressionTypes, namesStorage, curModule)
88
91
val constantCollector = ConstantCollector (typeStorage)
89
92
val visitor = Visitor (listOf (hintCollector, constantCollector))
90
93
visitor.visit(method.ast)
91
94
return Pair (hintCollector, constantCollector)
92
95
}
93
96
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
+
94
148
fun generate (methodDescription : PythonMethodHeader ): PythonTestSet {
95
149
storageForMypyMessages.clear()
96
150
@@ -108,65 +162,69 @@ class PythonTestCaseGenerator(
108
162
var missingLines: Set <Int >? = null
109
163
val coveredLines = mutableSetOf<Int >()
110
164
var generated = 0
111
- val typeInferenceCancellation = { isCancelled() || System .currentTimeMillis() >= until || missingLines?.size == 0 }
165
+ val typeInferenceCancellation =
166
+ { isCancelled() || System .currentTimeMillis() >= until || missingLines?.size == 0 }
112
167
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
+ }
158
219
}
159
- is TypeErrorFeedback -> {
160
- feedback = InvalidTypeFeedback
220
+ val coveredAfter = coveredLines.size
221
+ if (coveredAfter == coveredBefore) {
222
+ coverageLimit - = 1
161
223
}
224
+ coveredBefore = coveredAfter
162
225
}
163
- val coveredAfter = coveredLines.size
164
- if (coveredAfter == coveredBefore) {
165
- coverageLimit - = 1
166
- }
167
- coveredBefore = coveredAfter
226
+ feedback
168
227
}
169
- feedback
170
228
}
171
229
172
230
@@ -231,13 +289,13 @@ class PythonTestCaseGenerator(
231
289
mypyConfigFile
232
290
)
233
291
234
- runBlocking breaking@ {
292
+ runBlocking breaking@{
235
293
if (isCancelled()) {
236
294
return @breaking
237
295
}
238
296
239
297
val existsAnnotation = method.definition.type
240
- if (existsAnnotation.arguments.all {it.pythonTypeName() != " typing.Any" }) {
298
+ if (existsAnnotation.arguments.all { it.pythonTypeName() != " typing.Any" }) {
241
299
annotationHandler(existsAnnotation)
242
300
}
243
301
0 commit comments