Skip to content

Commit 228072f

Browse files
Added NumPy support for utbot-python (#2742)
1 parent 2b37f07 commit 228072f

File tree

30 files changed

+595
-43
lines changed

30 files changed

+595
-43
lines changed

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

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ class TestWriter {
99

1010
fun generateTestCode(): String {
1111
val (importLines, code) = testCode.fold(mutableListOf<String>() to StringBuilder()) { acc, s ->
12-
val lines = s.split(System.lineSeparator())
12+
// val lines = s.split(System.lineSeparator())
13+
val lines = s.split("(\\r\\n|\\r|\\n)".toRegex())
1314
val firstClassIndex = lines.indexOfFirst { it.startsWith("class") }
1415
lines.take(firstClassIndex).forEach { line -> if (line !in acc.first) acc.first.add(line) }
1516
lines.drop(firstClassIndex).forEach { line -> acc.second.append(line + System.lineSeparator()) }

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "utbot-executor"
3-
version = "1.9.19"
3+
version = "1.10.0"
44
description = ""
55
authors = ["Vyacheslav Tamarin <[email protected]>"]
66
readme = "README.md"

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

+1
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ def serialize_objects_dump(objs: List[Any], clear_visited: bool = False) -> Tupl
5050
serializer.write_object_to_memory(obj)
5151
for obj in objs
5252
]
53+
5354
return ids, serializer.memory, serialize_memory_dump(serializer.memory)
5455

5556

utbot-python-executor/src/main/python/utbot_executor/utbot_executor/deep_serialization/json_converter.py

+56-2
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,14 @@
1111
ListMemoryObject,
1212
DictMemoryObject,
1313
ReduceMemoryObject,
14-
MemoryDump, IteratorMemoryObject,
14+
MemoryDump, IteratorMemoryObject, NdarrayMemoryObject,
1515
)
1616
from utbot_executor.deep_serialization.utils import PythonId, TypeInfo
17+
try:
18+
import numpy as np
19+
except ImportError:
20+
import sys
21+
print("numpy is not installed", file=sys.stderr)
1722

1823

1924
class MemoryObjectEncoder(json.JSONEncoder):
@@ -27,6 +32,10 @@ def default(self, o):
2732
}
2833
if isinstance(o, ReprMemoryObject):
2934
base_json["value"] = o.value
35+
elif isinstance(o, NdarrayMemoryObject):
36+
base_json["items"] = o.items
37+
base_json["comparable"] = True
38+
base_json["dimensions"] = o.dimensions
3039
elif isinstance(o, (ListMemoryObject, DictMemoryObject)):
3140
base_json["items"] = o.items
3241
elif isinstance(o, IteratorMemoryObject):
@@ -53,6 +62,10 @@ def default(self, o):
5362
"kind": o.kind,
5463
"module": o.module,
5564
}
65+
if isinstance(o, np.ndarray):
66+
raise NotImplementedError("np.ndarray is not supported")
67+
if isinstance(o, np.bool_):
68+
return bool(o)
5669
return json.JSONEncoder.default(self, o)
5770

5871

@@ -75,6 +88,17 @@ def as_reduce_object(dct: Dict) -> Union[MemoryObject, Dict]:
7588
)
7689
obj.comparable = dct["comparable"]
7790
return obj
91+
92+
if dct["strategy"] == "ndarray":
93+
obj = NdarrayMemoryObject.__new__(NdarrayMemoryObject)
94+
obj.items = dct["items"]
95+
obj.typeinfo = TypeInfo(
96+
kind=dct["typeinfo"]["kind"], module=dct["typeinfo"]["module"]
97+
)
98+
obj.comparable = dct["comparable"]
99+
obj.dimensions = dct["dimensions"]
100+
return obj
101+
78102
if dct["strategy"] == "dict":
79103
obj = DictMemoryObject.__new__(DictMemoryObject)
80104
obj.items = dct["items"]
@@ -138,6 +162,11 @@ def reload_id(self) -> MemoryDump:
138162
new_memory_object.items = [
139163
self.dump_id_to_real_id[id_] for id_ in new_memory_object.items
140164
]
165+
elif isinstance(new_memory_object, NdarrayMemoryObject):
166+
new_memory_object.items = [
167+
self.dump_id_to_real_id[id_] for id_ in new_memory_object.items
168+
]
169+
new_memory_object.dimensions = obj.dimensions
141170
elif isinstance(new_memory_object, IteratorMemoryObject):
142171
new_memory_object.items = [
143172
self.dump_id_to_real_id[id_] for id_ in new_memory_object.items
@@ -198,6 +227,27 @@ def load_object(self, python_id: PythonId) -> object:
198227

199228
for item in dump_object.items:
200229
real_object.append(self.load_object(item))
230+
231+
elif isinstance(dump_object, NdarrayMemoryObject):
232+
real_object = []
233+
234+
id_ = PythonId(str(id(real_object)))
235+
self.dump_id_to_real_id[python_id] = id_
236+
self.memory[id_] = real_object
237+
238+
temp_list = []
239+
for item in dump_object.items:
240+
temp_list.append(self.load_object(item))
241+
242+
dt = np.dtype(type(temp_list[0]) if len(temp_list) > 0 else np.int64)
243+
temp_list = np.array(temp_list, dtype=dt)
244+
245+
real_object = np.ndarray(
246+
shape=dump_object.dimensions,
247+
dtype=dt,
248+
buffer=temp_list
249+
)
250+
201251
elif isinstance(dump_object, DictMemoryObject):
202252
real_object = {}
203253

@@ -250,7 +300,7 @@ def load_object(self, python_id: PythonId) -> object:
250300
for key, dictitem in dictitems.items():
251301
real_object[key] = dictitem
252302
else:
253-
raise TypeError(f"Invalid type {dump_object}")
303+
raise TypeError(f"Invalid type {dump_object}, type: {type(dump_object)}")
254304

255305
id_ = PythonId(str(id(real_object)))
256306
self.dump_id_to_real_id[python_id] = id_
@@ -279,6 +329,10 @@ def main():
279329
"builtins.tuple",
280330
"builtins.bytes",
281331
"builtins.type",
332+
"numpy.ndarray"
282333
]
283334
)
284335
print(loader.load_object(PythonId("140239390887040")))
336+
337+
if __name__ == '__main__':
338+
main()

utbot-python-executor/src/main/python/utbot_executor/utbot_executor/deep_serialization/memory_objects.py

+52-11
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@
88
import typing
99
from itertools import zip_longest
1010
from typing import Any, Callable, Dict, List, Optional, Set, Type, Iterable
11+
try:
12+
import numpy as np
13+
except ImportError:
14+
import sys
15+
print("numpy is not installed", file=sys.stderr)
1116

1217
from utbot_executor.deep_serialization.config import PICKLE_PROTO
1318
from utbot_executor.deep_serialization.iterator_wrapper import IteratorWrapper
@@ -41,7 +46,7 @@ def __init__(self, obj: object) -> None:
4146
self.id_ = PythonId(str(id(self.obj)))
4247

4348
def _initialize(
44-
self, deserialized_obj: object = None, comparable: bool = True
49+
self, deserialized_obj: object = None, comparable: bool = True
4550
) -> None:
4651
self.deserialized_obj = deserialized_obj
4752
self.comparable = comparable
@@ -111,14 +116,42 @@ def initialize(self) -> None:
111116
elif self.typeinfo.fullname == "builtins.set":
112117
deserialized_obj = set(deserialized_obj)
113118

119+
114120
comparable = all(serializer.get_by_id(elem).comparable for elem in self.items)
115121

116122
super()._initialize(deserialized_obj, comparable)
117123

124+
class NdarrayMemoryObject(MemoryObject):
125+
strategy: str = "ndarray"
126+
items: List[PythonId] = []
127+
dimensions: List[int] = []
128+
129+
def __init__(self, ndarray_object: object) -> None:
130+
self.items: List[PythonId] = []
131+
super().__init__(ndarray_object)
132+
133+
def initialize(self) -> None:
134+
serializer = PythonSerializer()
135+
self.deserialized_obj = [] # for recursive collections
136+
self.comparable = False # for recursive collections
137+
138+
temp_object = self.obj.copy().flatten()
139+
140+
self.dimensions = self.obj.shape
141+
if temp_object.shape != (0, ):
142+
for elem in temp_object:
143+
elem_id = serializer.write_object_to_memory(elem)
144+
self.items.append(elem_id)
145+
self.deserialized_obj.append(serializer[elem_id])
146+
147+
deserialized_obj = self.deserialized_obj
148+
comparable = all(serializer.get_by_id(elem).comparable for elem in self.items) if self.deserialized_obj != [] else True
149+
super()._initialize(deserialized_obj, comparable)
150+
118151
def __repr__(self) -> str:
119152
if hasattr(self, "obj"):
120153
return str(self.obj)
121-
return f"{self.typeinfo.kind}{self.items}"
154+
return f"{self.typeinfo.kind}{self.items}{self.dimensions}"
122155

123156

124157
class DictMemoryObject(MemoryObject):
@@ -264,10 +297,10 @@ def constructor_builder(self) -> typing.Tuple[typing.Any, typing.Callable]:
264297

265298
is_reconstructor = constructor_kind.qualname == "copyreg._reconstructor"
266299
is_reduce_user_type = (
267-
len(self.reduce_value[1]) == 3
268-
and isinstance(self.reduce_value[1][0], type(self.obj))
269-
and self.reduce_value[1][1] is object
270-
and self.reduce_value[1][2] is None
300+
len(self.reduce_value[1]) == 3
301+
and isinstance(self.reduce_value[1][0], type(self.obj))
302+
and self.reduce_value[1][1] is object
303+
and self.reduce_value[1][2] is None
271304
)
272305
is_reduce_ex_user_type = len(self.reduce_value[1]) == 1 and isinstance(
273306
self.reduce_value[1][0], type(self.obj)
@@ -294,8 +327,8 @@ def constructor_builder(self) -> typing.Tuple[typing.Any, typing.Callable]:
294327
len(inspect.signature(init_method).parameters),
295328
)
296329
if (
297-
not init_from_object
298-
and len(inspect.signature(init_method).parameters) == 1
330+
not init_from_object
331+
and len(inspect.signature(init_method).parameters) == 1
299332
) or init_from_object:
300333
logging.debug("init with one argument! %s", init_method)
301334
constructor_arguments = []
@@ -317,9 +350,9 @@ def constructor_builder(self) -> typing.Tuple[typing.Any, typing.Callable]:
317350
if is_reconstructor and is_user_type:
318351
constructor_arguments = self.reduce_value[1]
319352
if (
320-
len(constructor_arguments) == 3
321-
and constructor_arguments[-1] is None
322-
and constructor_arguments[-2] == object
353+
len(constructor_arguments) == 3
354+
and constructor_arguments[-1] is None
355+
and constructor_arguments[-2] == object
323356
):
324357
del constructor_arguments[1:]
325358
callable_constructor = object.__new__
@@ -392,6 +425,12 @@ def get_serializer(obj: object) -> Optional[Type[MemoryObject]]:
392425
return ListMemoryObject
393426
return None
394427

428+
class NdarrayMemoryObjectProvider(MemoryObjectProvider):
429+
@staticmethod
430+
def get_serializer(obj: object) -> Optional[Type[MemoryObject]]:
431+
if type(obj) == np.ndarray:
432+
return NdarrayMemoryObject
433+
return None
395434

396435
class DictMemoryObjectProvider(MemoryObjectProvider):
397436
@staticmethod
@@ -425,6 +464,7 @@ def get_serializer(obj: object) -> Optional[Type[MemoryObject]]:
425464
return None
426465

427466

467+
428468
class ReprMemoryObjectProvider(MemoryObjectProvider):
429469
@staticmethod
430470
def get_serializer(obj: object) -> Optional[Type[MemoryObject]]:
@@ -450,6 +490,7 @@ class PythonSerializer:
450490
visited: Set[PythonId] = set()
451491

452492
providers: List[MemoryObjectProvider] = [
493+
NdarrayMemoryObjectProvider,
453494
ListMemoryObjectProvider,
454495
DictMemoryObjectProvider,
455496
IteratorMemoryObjectProvider,

utbot-python-executor/src/main/python/utbot_executor/utbot_executor/deep_serialization/utils.py

+13-5
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ def get_constructor_info(constructor: object, obj: object) -> TypeInfo:
9595

9696

9797
def has_reduce(py_object: object) -> bool:
98+
if get_kind(py_object).module == "numpy":
99+
return False
98100
reduce = getattr(py_object, "__reduce__", None)
99101
if reduce is None:
100102
return False
@@ -161,6 +163,10 @@ def check_eval(py_object: object) -> bool:
161163
except Exception:
162164
return False
163165

166+
try:
167+
import numpy as np
168+
except ImportError:
169+
pass
164170

165171
def has_repr(py_object: object) -> bool:
166172
reprable_types = [
@@ -171,11 +177,13 @@ def has_repr(py_object: object) -> bool:
171177
bytes,
172178
bytearray,
173179
str,
174-
# tuple,
175-
# list,
176-
# dict,
177-
# set,
178-
# frozenset,
180+
np.int64,
181+
np.int32,
182+
np.int16,
183+
np.int8,
184+
np.float32,
185+
np.float16,
186+
np.float64,
179187
type,
180188
]
181189
if type(py_object) in reprable_types:

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

+16-12
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444

4545

4646
def _update_states(
47-
init_memory_dump: MemoryDump, before_memory_dump: MemoryDump
47+
init_memory_dump: MemoryDump, before_memory_dump: MemoryDump
4848
) -> MemoryDump:
4949
for id_, obj in before_memory_dump.objects.items():
5050
if id_ in init_memory_dump.objects:
@@ -87,8 +87,9 @@ def add_imports(imports: Iterable[str]):
8787
globals()[submodule_name] = importlib.import_module(
8888
submodule_name
8989
)
90-
except ModuleNotFoundError:
90+
except ModuleNotFoundError as e:
9191
logging.warning("Import submodule %s failed", submodule_name)
92+
raise e
9293
logging.debug("Submodule #%d: OK", i)
9394

9495
def run_function(self, request: ExecutionRequest) -> ExecutionResponse:
@@ -115,6 +116,9 @@ def run_reduce_function(self, request: ExecutionRequest) -> ExecutionResponse:
115116
self.add_imports(request.imports)
116117
loader.add_syspaths(request.syspaths)
117118
loader.add_imports(request.imports)
119+
except ModuleNotFoundError as _:
120+
logging.debug("Error \n%s", traceback.format_exc())
121+
return ExecutionFailResponse("fail", traceback.format_exc())
118122
except Exception as _:
119123
logging.debug("Error \n%s", traceback.format_exc())
120124
return ExecutionFailResponse("fail", traceback.format_exc())
@@ -246,9 +250,9 @@ def run_pickle_function(self, request: ExecutionRequest) -> ExecutionResponse:
246250

247251

248252
def _serialize_state(
249-
args: List[Any],
250-
kwargs: Dict[str, Any],
251-
result: Any = None,
253+
args: List[Any],
254+
kwargs: Dict[str, Any],
255+
result: Any = None,
252256
) -> Tuple[List[PythonId], Dict[str, PythonId], PythonId, MemoryDump, str]:
253257
"""Serialize objects from args, kwargs and result.
254258
@@ -267,13 +271,13 @@ def _serialize_state(
267271

268272

269273
def _run_calculate_function_value(
270-
function: types.FunctionType,
271-
args: List[Any],
272-
kwargs: Dict[str, Any],
273-
fullpath: str,
274-
state_init: str,
275-
tracer: UtTracer,
276-
state_assertions: bool,
274+
function: types.FunctionType,
275+
args: List[Any],
276+
kwargs: Dict[str, Any],
277+
fullpath: str,
278+
state_init: str,
279+
tracer: UtTracer,
280+
state_assertions: bool,
277281
) -> ExecutionResponse:
278282
"""Calculate function evaluation result.
279283

0 commit comments

Comments
 (0)