Skip to content

Commit bb8f7bb

Browse files
committed
Changed PythonMethod; generation of mypy check code
1 parent da5ea15 commit bb8f7bb

File tree

13 files changed

+128
-64
lines changed

13 files changed

+128
-64
lines changed

utbot-python/samples/easy_samples/annotation_tests.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,16 @@
88

99

1010
class A(Generic[XXX]):
11-
self: XXX
11+
self_: XXX
1212

13-
def f(a, b: A[int]):
13+
def f(self, a, b: A[int]):
14+
self.y = b
15+
self_.x = b
1416
pass
1517

18+
def g(self):
19+
self.x = 1
20+
1621

1722
def square(collection: Iterable[int], x: XXX):
1823
result = set()

utbot-python/src/main/kotlin/org/utbot/python/PythonTestCaseGenerator.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ object PythonTestCaseGenerator {
6262

6363
// TODO: consider static and class methods
6464
if (method.containingPythonClassId != null) {
65-
initialArgumentTypes[0] = NormalizedPythonAnnotation(method.containingPythonClassId!!.name)
65+
initialArgumentTypes[0] = NormalizedPythonAnnotation(method.containingPythonClassId.name)
6666
}
6767

6868
logger.debug("Collecting hints about arguments")

utbot-python/src/main/kotlin/org/utbot/python/UTPythonAPI.kt

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,30 @@ import io.github.danielnaczo.python3parser.model.stmts.compoundStmts.functionStm
44
import io.github.danielnaczo.python3parser.model.mods.Module
55
import org.utbot.framework.plugin.api.UtError
66
import org.utbot.framework.plugin.api.UtExecution
7+
import org.utbot.python.code.textToModule
78
import org.utbot.python.framework.api.python.PythonClassId
89
import org.utbot.python.framework.api.python.util.pythonAnyClassId
10+
import org.utbot.python.newtyping.general.FunctionType
911
import org.utbot.python.typing.MypyAnnotations
1012
import org.utbot.python.utils.moduleToString
1113

1214
data class PythonArgument(val name: String, val annotation: String?)
1315

14-
interface PythonMethod {
15-
val name: String
16-
val returnAnnotation: String?
17-
val arguments: List<PythonArgument>
18-
val moduleFilename: String
19-
fun asString(): String
20-
fun ast(): FunctionDef
21-
val containingPythonClassId: PythonClassId?
22-
fun codeLines(): List<String> = moduleToString(Module(listOf(ast().body))).split('\n')
16+
open class PythonMethod(
17+
val name: String,
18+
val returnAnnotation: String?,
19+
val arguments: List<PythonArgument>,
20+
val moduleFilename: String,
21+
val containingPythonClassId: PythonClassId?,
22+
val codeAsString: String
23+
) {
24+
lateinit var type: FunctionType
2325
fun methodSignature(): String = "$name(" + arguments.joinToString(", ") {
2426
"${it.name}: ${it.annotation ?: pythonAnyClassId.name}"
2527
} + ")"
28+
open val oldAst: FunctionDef by lazy {
29+
textToModule(codeAsString).functionDefs.first()
30+
}
2631
}
2732

2833
data class PythonTestSet(

utbot-python/src/main/kotlin/org/utbot/python/code/ArgInfoCollector.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ class ArgInfoCollector(val method: PythonMethod, private val argumentTypes: List
9090
private val visitor = MatchVisitor(paramNames, mutableSetOf(), GeneralStorage())
9191

9292
init {
93-
visitor.visitFunctionDef(method.ast(), collectedValues)
93+
visitor.visitFunctionDef(method.oldAst, collectedValues)
9494
}
9595

9696
fun getConstants(): List<FuzzedConcreteValue> = visitor.constStorage.toList()

utbot-python/src/main/kotlin/org/utbot/python/code/ClassInfoCollector.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ class ClassInfoCollector(pyClass: PythonClass) {
2525
val selfName = getSelfName(method)
2626
if (selfName != null) {
2727
val visitor = Visitor(selfName)
28-
visitor.visitFunctionDef(method.ast(), storage)
28+
visitor.visitFunctionDef(method.oldAst, storage)
2929
}
3030
}
3131
pyClass.topLevelFields.forEach { annAssign ->
@@ -36,7 +36,7 @@ class ClassInfoCollector(pyClass: PythonClass) {
3636
companion object {
3737
fun getSelfName(method: PythonMethod): String? {
3838
val params = method.arguments
39-
if (params.isEmpty() || method.ast().decorators.any {
39+
if (params.isEmpty() || method.oldAst.decorators.any {
4040
listOf(
4141
"staticmethod",
4242
"classmethod"
@@ -46,7 +46,7 @@ class ClassInfoCollector(pyClass: PythonClass) {
4646
}
4747

4848
fun isProperty(method: PythonMethod): Boolean {
49-
return method.ast().decorators.any { it.name.name == "property" }
49+
return method.oldAst.decorators.any { it.name.name == "property" }
5050
}
5151
}
5252

utbot-python/src/main/kotlin/org/utbot/python/code/PythonCodeAPI.kt

Lines changed: 24 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import org.utbot.python.PythonMethod
2727
import org.utbot.python.framework.api.python.NormalizedPythonAnnotation
2828
import org.utbot.python.framework.api.python.PythonClassId
2929
import org.utbot.python.utils.moduleOfType
30+
import org.utbot.python.utils.moduleToString
3031
import java.util.*
3132

3233
private val logger = KotlinLogging.logger {}
@@ -131,42 +132,37 @@ class PythonClass(
131132
}
132133

133134
class PythonMethodBody(
134-
private val ast: FunctionDef,
135-
override val moduleFilename: String = "",
136-
override val containingPythonClassId: PythonClassId? = null
137-
) : PythonMethod {
138-
override val name: String
139-
get() = ast.name.name
135+
ast: FunctionDef,
136+
moduleFilename: String = "",
137+
containingPythonClassId: PythonClassId? = null
138+
) : PythonMethod(
139+
name = ast.name.name,
140+
returnAnnotation = annotationToString(ast.returns),
141+
arguments = getArguments(ast),
142+
moduleFilename,
143+
containingPythonClassId,
144+
codeAsString = moduleToString(Module(listOf(ast.body)))
145+
) {
140146

141-
override val returnAnnotation: String?
142-
get() = annotationToString(ast.returns)
147+
override val oldAst: FunctionDef = ast
143148

144-
// TODO: consider cases of default and keyword arguments
145-
private val getParams: List<Parameter> =
146-
if (ast.parameters.isPresent)
147-
ast.parameters.get().params ?: emptyList()
148-
else
149-
emptyList()
149+
companion object {
150+
fun annotationToString(annotation: Optional<Expression>): String? =
151+
if (annotation.isPresent) astToString(annotation.get()).trim() else null
152+
153+
private fun getParams(ast: FunctionDef): List<Parameter> =
154+
if (ast.parameters.isPresent)
155+
ast.parameters.get().params ?: emptyList()
156+
else
157+
emptyList()
150158

151-
override val arguments: List<PythonArgument>
152-
get() = getParams.map { param ->
159+
fun getArguments(ast: FunctionDef): List<PythonArgument> =
160+
getParams(ast).map { param ->
153161
PythonArgument(
154162
param.parameterName.name,
155163
annotationToString(param.annotation)
156164
)
157165
}
158-
159-
override fun asString(): String {
160-
return astToString(ast)
161-
}
162-
163-
override fun ast(): FunctionDef {
164-
return ast
165-
}
166-
167-
companion object {
168-
fun annotationToString(annotation: Optional<Expression>): String? =
169-
if (annotation.isPresent) astToString(annotation.get()).trim() else null
170166
}
171167
}
172168

utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/PythonCodeGenerator.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -193,13 +193,13 @@ class PythonCodeGenerator(
193193
}
194194

195195
val functionPrefix = "__mypy_check"
196-
val functionName = "def ${functionPrefix}_{method.name}(${parameters.joinToString(", ")}):" // TODO: in future can be "async def"
196+
val functionName = "def ${functionPrefix}_${method.name}(${parameters.joinToString(", ")}):" // TODO: in future can be "async def"
197197

198198
val mypyCheckCode = listOf(
199199
renderer.toString(),
200200
"",
201201
functionName,
202-
) + method.codeLines().map { " $it" }
202+
) + method.codeAsString.split("\n").map { " $it" }
203203
return mypyCheckCode.joinToString("\n")
204204
}
205205
}

utbot-python/src/main/kotlin/org/utbot/python/newtyping/AnnotationFromMypy.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,4 +305,12 @@ class UnknownAnnotationNode: PythonAnnotationNode() {
305305
override fun initializeType(): Type {
306306
return pythonAnyType
307307
}
308+
}
309+
310+
fun main() {
311+
val json = MypyAnnotation::class.java.getResource("/annotation_sample.json")!!.readText()
312+
val storage = readMypyAnnotationStorage(json)
313+
val A = storage.definitions["annotation_tests"]!!["A"]!!.annotation.asUtBotType
314+
val attrs = A.getPythonAttributes().map { it.name }
315+
println(attrs)
308316
}

utbot-python/src/main/kotlin/org/utbot/python/newtyping/PythonType.kt

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,7 @@ fun Type.isPythonObjectType(): Boolean {
3030
}
3131

3232
fun Type.pythonTypeRepresentation(): String {
33-
val description = pythonDescription()
34-
val root = description.name.prefix.joinToString() + "." + description.name.name
35-
val params = pythonAnnotationParameters()
36-
if (params.isEmpty())
37-
return root
38-
return "$root[${params.joinToString { it.pythonTypeRepresentation() }}]"
33+
return pythonDescription().getTypeRepresentation(this)
3934
}
4035

4136
class PythonTypeStorage(
@@ -85,6 +80,13 @@ sealed class PythonTypeDescription(name: Name) : TypeMetaDataWithName(name) {
8580
getNamedMembers(type).find { it.name == name }
8681
open fun createTypeWithNewAnnotationParameters(like: Type, newParams: List<Type>): Type = // overriden for Callable
8782
DefaultSubstitutionProvider.substituteAll(like.getOrigin(), newParams)
83+
open fun getTypeRepresentation(type: Type): String { // overriden for Callable
84+
val root = name.prefix.joinToString() + "." + name.name
85+
val params = getAnnotationParameters(type)
86+
if (params.isEmpty())
87+
return root
88+
return "$root[${params.joinToString { it.pythonTypeRepresentation() }}]"
89+
}
8890
}
8991

9092
sealed class PythonCompositeTypeDescription(
@@ -234,6 +236,14 @@ class PythonCallableTypeDescription(
234236
)
235237
}
236238
}
239+
240+
override fun getTypeRepresentation(type: Type): String {
241+
val functionType = castToCompatibleTypeApi(type)
242+
val root = name.prefix.joinToString(".") + "." + name.name
243+
return "$root[[${
244+
functionType.arguments.joinToString(separator = ".") { it.pythonTypeRepresentation() }
245+
}], ${functionType.returnValue.pythonTypeRepresentation()}]"
246+
}
237247
}
238248

239249
// Special Python annotations

utbot-python/src/main/kotlin/org/utbot/python/newtyping/runmypy/RunMypy.kt

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
package org.utbot.python.newtyping.runmypy
22

3-
import org.utbot.python.newtyping.MypyAnnotationStorage
4-
import org.utbot.python.newtyping.readMypyAnnotationStorage
3+
import org.utbot.python.PythonArgument
4+
import org.utbot.python.PythonMethod
5+
import org.utbot.python.code.PythonCodeGenerator.generateMypyCheckCode
6+
import org.utbot.python.framework.api.python.NormalizedPythonAnnotation
7+
import org.utbot.python.newtyping.*
8+
import org.utbot.python.newtyping.general.FunctionType
9+
import org.utbot.python.utils.Cleaner
510
import org.utbot.python.utils.TemporaryFileManager
611
import org.utbot.python.utils.runCommand
712
import java.io.File
@@ -71,21 +76,49 @@ private fun setConfigFile(): File {
7176
return file
7277
}
7378

79+
fun checkSuggestedSignatureWithDMypy(
80+
method: PythonMethod,
81+
directoriesForSysPath: Set<String>,
82+
moduleToImport: String
83+
): String {
84+
val description = method.type.pythonDescription() as PythonCallableTypeDescription
85+
val annotationMap =
86+
(description.argumentNames zip method.type.arguments.map { it.pythonTypeRepresentation() }).associate {
87+
Pair(it.first, NormalizedPythonAnnotation(it.second))
88+
}
89+
val mypyCode = generateMypyCheckCode(method, annotationMap, directoriesForSysPath, moduleToImport)
90+
return mypyCode
91+
}
92+
7493
fun main() {
7594
TemporaryFileManager.setup()
7695
val configFile = setConfigFile()
77-
println(
78-
readMypyAnnotationStorageAndInitialErrors(
79-
"python3",
80-
"/home/tochilinak/Documents/projects/utbot/UTBotJava/utbot-python/samples/easy_samples/annotation_tests.py",
81-
configFile
82-
)
83-
)
96+
val filePath = "/home/tochilinak/Documents/projects/utbot/UTBotJava/utbot-python/samples/easy_samples/annotation_tests.py"
97+
val (storage, mypyOut) = readMypyAnnotationStorageAndInitialErrors("python3", filePath, configFile)
98+
println(mypyOut)
8499
println(
85100
checkWithDMypy(
86101
"python3",
87-
"/home/tochilinak/Documents/projects/utbot/UTBotJava/utbot-python/samples/easy_samples/annotation_tests.py",
102+
filePath,
88103
configFile
89104
)
90105
)
106+
val type = storage.definitions["annotation_tests"]!!["same_annotations"]!!.annotation.asUtBotType as FunctionType
107+
val pythonMethod = PythonMethod(
108+
"same_annotations",
109+
type.returnValue.pythonTypeRepresentation(),
110+
(type.arguments zip (type.pythonDescription() as PythonCallableTypeDescription).argumentNames).map {
111+
PythonArgument(it.second, it.first.pythonTypeRepresentation())
112+
},
113+
filePath,
114+
null,
115+
"result = set()\n" +
116+
"for elem in collection:\n" +
117+
" result.add(elem ** 2)\n" +
118+
"return result\n"
119+
)
120+
pythonMethod.type = type
121+
println(checkSuggestedSignatureWithDMypy(pythonMethod, emptySet(), "annotation_tests"))
122+
123+
Cleaner.doCleaning()
91124
}

utbot-python/src/main/resources/requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@ astor
33
typeshed-client
44
coverage
55
utbot-executor
6-
utbot-mypy-runner==0.1.4
6+
utbot-mypy-runner==0.1.5

utbot-python/src/test/kotlin/org/utbot/python/newtyping/AnnotationFromMypyKtTest.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,4 +109,11 @@ internal class AnnotationFromMypyKtTest {
109109
(square.pythonDescription() as PythonCallableTypeDescription).argumentNames == listOf("collection", "x")
110110
)
111111
}
112+
113+
@Test
114+
fun testCustomClassAttributes() {
115+
val A = storage.definitions["annotation_tests"]!!["A"]!!.annotation.asUtBotType
116+
val attrs = A.getPythonAttributes().map { it.name }
117+
assertTrue(attrs.containsAll(listOf("y", "x", "self_")))
118+
}
112119
}

utbot-python/src/test/resources/annotation_sample.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)