Skip to content

Commit b51d1b8

Browse files
committed
Supported inner classes
1 parent b446449 commit b51d1b8

File tree

12 files changed

+62
-39
lines changed

12 files changed

+62
-39
lines changed

utbot-cli-python/src/main/kotlin/org/utbot/cli/language/python/sbft/SbftGenerateTestsCommand.kt

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import org.utbot.python.code.PythonCode
2626
import org.utbot.python.coverage.CoverageOutputFormat
2727
import org.utbot.python.coverage.PythonCoverageMode
2828
import org.utbot.python.framework.api.python.PythonClassId
29+
import org.utbot.python.framework.api.python.pythonBuiltinsModuleName
2930
import org.utbot.python.framework.codegen.model.Pytest
3031
import org.utbot.python.framework.codegen.model.PythonImport
3132
import org.utbot.python.newtyping.ast.parseClassDefinition
@@ -136,15 +137,20 @@ class SbftGenerateTestsCommand : CliktCommand(
136137
.mapNotNull { parseFunctionDefinition(it) }
137138
.map { PythonMethodHeader(it.name.toString(), absPathToSourceFile, null) }
138139
val methods = topLevelClasses
139-
.mapNotNull { parseClassDefinition(it) }
140-
.map { cls ->
141-
PythonCode.getClassMethods(cls.body)
142-
.mapNotNull { parseFunctionDefinition(it) }
143-
.map { function ->
144-
val parsedClassName = PythonClassId(cls.name.toString())
145-
PythonMethodHeader(function.name.toString(), absPathToSourceFile, parsedClassName)
146-
}
140+
.mapNotNull { cls ->
141+
val parsedClass = parseClassDefinition(cls) ?: return@mapNotNull null
142+
val innerClasses = PythonCode.getInnerClasses(cls)
143+
(listOf(parsedClass to null) + innerClasses.mapNotNull { innerClass -> parseClassDefinition(innerClass)?.let { it to parsedClass } }).map { (cls, parent) ->
144+
PythonCode.getClassMethods(cls.body)
145+
.mapNotNull { parseFunctionDefinition(it) }
146+
.map { function ->
147+
val clsName = (parent?.let { "${it.name}." } ?: "") + cls.name.toString()
148+
val parsedClassName = PythonClassId(pythonBuiltinsModuleName, clsName)
149+
PythonMethodHeader(function.name.toString(), absPathToSourceFile, parsedClassName)
150+
}
151+
}
147152
}
153+
.flatten()
148154
return methods + listOf(functions)
149155
}
150156

utbot-python-executor/src/main/python/utbot_executor/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "utbot-executor"
3-
version = "1.9.6"
3+
version = "1.9.7"
44
description = ""
55
authors = ["Vyacheslav Tamarin <vyacheslav.tamarin@yandex.ru>"]
66
readme = "README.md"

utbot-python-executor/src/main/python/utbot_executor/utbot_executor/parser.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import dataclasses
22
import enum
33
import json
4-
from typing import Dict, List, Union, Tuple, Literal
4+
from typing import Dict, List, Union
55

66

77
class MemoryMode(enum.StrEnum):
@@ -24,7 +24,7 @@ class ExecutionRequest:
2424

2525
def get_class_name(self) -> str | None:
2626
if "." in self.function_name:
27-
return self.function_name.split(".")[0]
27+
return self.function_name.rsplit(".", 1)[0]
2828
return None
2929

3030

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.9.6
1+
1.9.7

utbot-python-types/src/main/python/utbot_mypy_runner/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "utbot_mypy_runner"
3-
version = "0.2.15"
3+
version = "0.2.16"
44
description = ""
55
authors = ["Ekaterina Tochilina <katerina_t_n@mail.ru>"]
66
readme = "README.md"

utbot-python-types/src/main/python/utbot_mypy_runner/utbot_mypy_runner/extract_annotations.py

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
11
import json
2-
import typing as tp
3-
from collections import defaultdict
4-
52
import mypy.nodes
63
import mypy.types
7-
8-
import utbot_mypy_runner.mypy_main as mypy_main
4+
import typing as tp
95
import utbot_mypy_runner.expression_traverser as expression_traverser
6+
import utbot_mypy_runner.mypy_main as mypy_main
107
import utbot_mypy_runner.names
11-
from utbot_mypy_runner.utils import get_borders
8+
from collections import defaultdict
129
from utbot_mypy_runner.nodes import *
10+
from utbot_mypy_runner.utils import get_borders
1311

1412

1513
class ExpressionType:
@@ -87,14 +85,14 @@ def get_result_from_mypy_build(build_result: mypy_main.build.BuildResult, source
8785
only_types = mypy_file.path not in source_paths
8886

8987
try:
90-
definition = get_definition_from_symbol_node(symbol_table_node, Meta(module), only_types)
88+
definitions = get_definition_from_symbol_node(symbol_table_node, Meta(module), name, only_types)
9189
except NoTypeVarBindingException:
9290
# Bad definition, like this one:
9391
# https://github.com/sqlalchemy/sqlalchemy/blob/rel_2_0_20/lib/sqlalchemy/orm/mapped_collection.py#L521
94-
definition = None
92+
definitions = {}
9593

96-
if definition is not None:
97-
annotation_dict[module][name] = definition
94+
for def_name, definition in definitions.items():
95+
annotation_dict[module][def_name] = definition
9896

9997
def processor(line, col, end_line, end_col, type_, meta):
10098
expression_types[module_for_types].append(

utbot-python-types/src/main/python/utbot_mypy_runner/utbot_mypy_runner/nodes.py

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
1-
import typing as tp
2-
from collections import defaultdict
31
import copy
4-
import sys
5-
62
import mypy.nodes
73
import mypy.types
8-
4+
import sys
5+
import typing as tp
6+
from collections import defaultdict
97

108
added_keys: tp.List[str] = []
119
annotation_node_dict: tp.Dict[str, "AnnotationNode"] = {}
@@ -563,10 +561,24 @@ def get_definition_from_node(node: mypy.nodes.Node, meta: Meta, only_types: bool
563561
def get_definition_from_symbol_node(
564562
table_node: mypy.nodes.SymbolTableNode,
565563
meta: Meta,
564+
base_name: str,
566565
only_types: bool = False
567-
) -> tp.Optional[Definition]:
566+
) -> tp.Dict[str, Definition]:
568567
if table_node.node is None or not (table_node.node.fullname.startswith(meta.module_name)) \
569568
or not isinstance(table_node.node, mypy.nodes.Node): # this check is only for mypy
570-
return None
571-
572-
return get_definition_from_node(table_node.node, meta, only_types)
569+
return {}
570+
571+
definitions = {}
572+
base_def = get_definition_from_node(table_node.node, meta, only_types)
573+
if base_def is not None:
574+
definitions[base_name] = base_def
575+
576+
try:
577+
defn = table_node.node.defn
578+
if isinstance(defn, mypy.nodes.ClassDef):
579+
for inner_class in filter(lambda x: isinstance(x, mypy.nodes.ClassDef), table_node.node.defn.defs.body):
580+
definitions[f"{base_name}.{inner_class.name}"] = get_definition_from_node(inner_class.info, meta, only_types)
581+
except AttributeError:
582+
pass
583+
584+
return definitions
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.2.15
1+
0.2.16

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ abstract class PythonTestGenerationProcessor {
171171
abstract fun processCoverageInfo(testSets: List<PythonTestSet>)
172172

173173
private fun getContainingClassName(testSets: List<PythonTestSet>): String {
174-
val containingClasses = testSets.map { it.method.containingPythonClass?.pythonName() ?: "TopLevelFunctions" }
174+
val containingClasses = testSets.map { it.method.containingPythonClass?.pythonName()?.replace(".", "") ?: "TopLevelFunctions" }
175175
return containingClasses.toSet().first()
176176
}
177177

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

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ package org.utbot.python.code
22

33
import org.parsers.python.ast.Block
44
import org.parsers.python.ast.ClassDefinition
5-
import org.parsers.python.ast.Module
65
import org.parsers.python.ast.FunctionDefinition
6+
import org.parsers.python.ast.Module
77
import org.utbot.python.PythonMethodHeader
88
import org.utbot.python.newtyping.ast.ParsedFunctionDefinition
99
import org.utbot.python.newtyping.ast.parseClassDefinition
@@ -19,8 +19,12 @@ object PythonCode {
1919
return parsedFile.children().filterIsInstance<ClassDefinition>()
2020
}
2121

22-
fun getClassMethods(class_: Block): List<FunctionDefinition> {
23-
return class_.children().filterIsInstance<FunctionDefinition>()
22+
fun getInnerClasses(classDef: ClassDefinition): List<ClassDefinition> {
23+
return classDef.children().filterIsInstance<Block>().flatMap {it.children() }.filterIsInstance<ClassDefinition>()
24+
}
25+
26+
fun getClassMethods(classBlock: Block): List<FunctionDefinition> {
27+
return classBlock.children().filterIsInstance<FunctionDefinition>()
2428
}
2529

2630
fun findFunctionDefinition(parsedFile: Module, method: PythonMethodHeader): ParsedFunctionDefinition {
@@ -32,6 +36,7 @@ object PythonCode {
3236
} ?: throw Exception("Couldn't find top-level function ${method.name}")
3337
} else {
3438
getTopLevelClasses(parsedFile)
39+
.flatMap { listOf(it) + getInnerClasses(it) }
3540
.mapNotNull { parseClassDefinition(it) }
3641
.flatMap { getClassMethods(it.body) }
3742
.mapNotNull { parseFunctionDefinition(it) }

0 commit comments

Comments
 (0)