onec-interactive-runtime-core 0.1.19__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- onec_interactive_runtime_core-0.1.19.dist-info/METADATA +12 -0
- onec_interactive_runtime_core-0.1.19.dist-info/RECORD +87 -0
- onec_interactive_runtime_core-0.1.19.dist-info/WHEEL +4 -0
- onec_interactive_runtime_core-0.1.19.dist-info/licenses/COPYRIGHT +9 -0
- onec_interactive_runtime_core-0.1.19.dist-info/licenses/LICENSE +232 -0
- onec_interactive_runtime_core-0.1.19.dist-info/licenses/THIRD_PARTY_NOTICES.txt +32 -0
- onec_runtime/AGENTS.md +7 -0
- onec_runtime/__init__.py +3 -0
- onec_runtime/artifacts.py +59 -0
- onec_runtime/bootstrap.py +310 -0
- onec_runtime/breakpoint_workspace.py +249 -0
- onec_runtime/bsl/__init__.py +139 -0
- onec_runtime/bsl/diagnostics.py +1046 -0
- onec_runtime/bsl/full_ast_worker_projection.py +522 -0
- onec_runtime/bsl/generated_semantic_parser.py +6322 -0
- onec_runtime/bsl/lexer.py +164 -0
- onec_runtime/bsl/module_catalog.py +420 -0
- onec_runtime/bsl/module_delta.py +1079 -0
- onec_runtime/bsl/module_universe.py +1877 -0
- onec_runtime/bsl/notebook_cells.py +232 -0
- onec_runtime/bsl/notebook_method_globals.py +74 -0
- onec_runtime/bsl/notebook_methods.py +175 -0
- onec_runtime/bsl/parser_artifact_identity.py +119 -0
- onec_runtime/bsl/parser_target.py +388 -0
- onec_runtime/bsl/preprocessor.py +16 -0
- onec_runtime/bsl/semantic_lowering.py +1539 -0
- onec_runtime/bsl/source_maps.py +2411 -0
- onec_runtime/bsl/worker_dependency_resolver.py +289 -0
- onec_runtime/bsl/worker_preprocessor.py +247 -0
- onec_runtime/bsl/worker_projection_model.py +137 -0
- onec_runtime/bsl/worker_reload_source_map.py +1697 -0
- onec_runtime/capture.py +113 -0
- onec_runtime/capture_source.py +453 -0
- onec_runtime/compact_table.py +376 -0
- onec_runtime/compact_table_backend.py +500 -0
- onec_runtime/config.py +198 -0
- onec_runtime/configurator_agent.py +520 -0
- onec_runtime/controller_worker.py +400 -0
- onec_runtime/credentials.py +18 -0
- onec_runtime/epf_container.py +196 -0
- onec_runtime/errors.py +205 -0
- onec_runtime/experiment.py +130 -0
- onec_runtime/extension_bundle.py +944 -0
- onec_runtime/extension_lifecycle.py +402 -0
- onec_runtime/extension_state.py +222 -0
- onec_runtime/fault_injection.py +30 -0
- onec_runtime/kernel.py +179 -0
- onec_runtime/lease.py +123 -0
- onec_runtime/observation.py +287 -0
- onec_runtime/performance_profile.py +245 -0
- onec_runtime/privacy.py +263 -0
- onec_runtime/processes.py +265 -0
- onec_runtime/prototype_runtime.py +2644 -0
- onec_runtime/rdbg/__init__.py +1 -0
- onec_runtime/rdbg/models.py +130 -0
- onec_runtime/rdbg/reconnect.py +59 -0
- onec_runtime/rdbg/session.py +913 -0
- onec_runtime/rdbg/transport.py +138 -0
- onec_runtime/rdbg/xml_codec.py +980 -0
- onec_runtime/recovery.py +106 -0
- onec_runtime/recovery_journal.py +79 -0
- onec_runtime/resources/extension/OnecInteractiveRuntime.cfe +0 -0
- onec_runtime/resources/extension/extension-manifest.json +108 -0
- onec_runtime/runtime_api.py +5272 -0
- onec_runtime/runtime_contracts.py +196 -0
- onec_runtime/server_worker.py +2034 -0
- onec_runtime/session.py +2123 -0
- onec_runtime/startup_diagnostics.py +45 -0
- onec_runtime/stop_routing.py +65 -0
- onec_runtime/supervised_processes.py +232 -0
- onec_runtime/supervised_runtime.py +512 -0
- onec_runtime/supervisor.py +363 -0
- onec_runtime/supervisor_model.py +148 -0
- onec_runtime/supervisor_protocol.py +77 -0
- onec_runtime/table_materialization.py +471 -0
- onec_runtime/table_transfer_backend.py +203 -0
- onec_runtime/table_value.py +311 -0
- onec_runtime/toolchain.py +730 -0
- onec_runtime/value_materialization.py +432 -0
- onec_runtime/value_transfer_backend.py +199 -0
- onec_runtime/worker_breakpoints.py +1431 -0
- onec_runtime/worker_epf.py +313 -0
- onec_runtime/worker_epf_template.py +53 -0
- onec_runtime/worker_stage_protocol.py +516 -0
- onec_runtime/worker_universe.py +3772 -0
- onec_runtime_build/__init__.py +1 -0
- onec_runtime_build/parser_target_development.py +116 -0
|
@@ -0,0 +1,980 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import base64
|
|
4
|
+
from collections.abc import Iterable
|
|
5
|
+
from uuid import UUID
|
|
6
|
+
from xml.etree import ElementTree
|
|
7
|
+
from xml.sax.saxutils import escape
|
|
8
|
+
|
|
9
|
+
from onec_runtime.errors import ProtocolError
|
|
10
|
+
from onec_runtime.rdbg.models import (
|
|
11
|
+
CollectionCell,
|
|
12
|
+
CollectionRow,
|
|
13
|
+
DebugTarget,
|
|
14
|
+
EvaluationResult,
|
|
15
|
+
FrameVariable,
|
|
16
|
+
LocalVariablesResult,
|
|
17
|
+
ModuleLocation,
|
|
18
|
+
ModifyResult,
|
|
19
|
+
StackFrame,
|
|
20
|
+
StopEvent,
|
|
21
|
+
TargetId,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
RDBG_NS = "http://v8.1c.ru/8.3/debugger/debugRDBGRequestResponse"
|
|
26
|
+
BASE_NS = "http://v8.1c.ru/8.3/debugger/debugBaseData"
|
|
27
|
+
BP_NS = "http://v8.1c.ru/8.3/debugger/debugBreakpoints"
|
|
28
|
+
CALC_NS = "http://v8.1c.ru/8.3/debugger/debugCalculations"
|
|
29
|
+
AUTO_ATTACH_NS = "http://v8.1c.ru/8.3/debugger/debugAutoAttach"
|
|
30
|
+
XSI_NS = "http://www.w3.org/2001/XMLSchema-instance"
|
|
31
|
+
|
|
32
|
+
ElementTree.register_namespace("rdbg", RDBG_NS)
|
|
33
|
+
ElementTree.register_namespace("bd", BASE_NS)
|
|
34
|
+
ElementTree.register_namespace("bp", BP_NS)
|
|
35
|
+
ElementTree.register_namespace("calc", CALC_NS)
|
|
36
|
+
ElementTree.register_namespace("aa", AUTO_ATTACH_NS)
|
|
37
|
+
ElementTree.register_namespace("xsi", XSI_NS)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _tag(namespace: str, name: str) -> str:
|
|
41
|
+
return f"{{{namespace}}}{name}"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _local_name(element: ElementTree.Element) -> str:
|
|
45
|
+
return element.tag.rsplit("}", 1)[-1]
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _children(element: ElementTree.Element, name: str) -> list[ElementTree.Element]:
|
|
49
|
+
return [child for child in element if _local_name(child) == name]
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _descendants(element: ElementTree.Element, name: str) -> list[ElementTree.Element]:
|
|
53
|
+
return [child for child in element.iter() if _local_name(child) == name]
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _required_text(element: ElementTree.Element, name: str) -> str:
|
|
57
|
+
matches = _descendants(element, name)
|
|
58
|
+
if not matches or matches[0].text is None or not matches[0].text.strip():
|
|
59
|
+
raise ProtocolError(f"RDBG XML is missing required {name}")
|
|
60
|
+
return matches[0].text.strip()
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _optional_text(element: ElementTree.Element, name: str) -> str | None:
|
|
64
|
+
matches = _descendants(element, name)
|
|
65
|
+
if not matches or matches[0].text is None:
|
|
66
|
+
return None
|
|
67
|
+
value = matches[0].text.strip()
|
|
68
|
+
return value or None
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _target_id(element: ElementTree.Element) -> TargetId:
|
|
72
|
+
seance_id = _optional_text(element, "seanceId")
|
|
73
|
+
seance_no = _optional_text(element, "seanceNo")
|
|
74
|
+
infobase_instance_id = _optional_text(element, "infoBaseInstanceID")
|
|
75
|
+
return TargetId(
|
|
76
|
+
UUID(_required_text(element, "id")),
|
|
77
|
+
_required_text(element, "infoBaseAlias"),
|
|
78
|
+
UUID(seance_id) if seance_id is not None else None,
|
|
79
|
+
int(seance_no) if seance_no is not None else None,
|
|
80
|
+
UUID(infobase_instance_id) if infobase_instance_id is not None else None,
|
|
81
|
+
_optional_text(element, "configVersion") or "",
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _optional_bool(element: ElementTree.Element, name: str) -> bool | None:
|
|
86
|
+
matches = _descendants(element, name)
|
|
87
|
+
if not matches or matches[0].text is None:
|
|
88
|
+
return None
|
|
89
|
+
value = matches[0].text.strip().casefold()
|
|
90
|
+
if value == "true":
|
|
91
|
+
return True
|
|
92
|
+
if value == "false":
|
|
93
|
+
return False
|
|
94
|
+
raise ProtocolError(f"RDBG XML has invalid {name}: {matches[0].text!r}")
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _parse(payload: bytes) -> ElementTree.Element:
|
|
98
|
+
try:
|
|
99
|
+
return ElementTree.fromstring(payload)
|
|
100
|
+
except ElementTree.ParseError as error:
|
|
101
|
+
raise ProtocolError(f"Malformed RDBG XML: {error}") from error
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def parse_ping_document(payload: bytes) -> ElementTree.Element:
|
|
105
|
+
"""Parse a ping response once for the specialized event extractors below."""
|
|
106
|
+
return _parse(payload)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def validate_command_acknowledgement(payload: bytes, *, command: str) -> None:
|
|
110
|
+
"""Accept only the two success forms emitted by RDBG command endpoints."""
|
|
111
|
+
if not payload.strip():
|
|
112
|
+
return
|
|
113
|
+
try:
|
|
114
|
+
root = _parse(payload)
|
|
115
|
+
except ProtocolError as error:
|
|
116
|
+
raise ProtocolError(f"Invalid {command} acknowledgement") from error
|
|
117
|
+
results = _children(root, "result")
|
|
118
|
+
if (
|
|
119
|
+
_local_name(root) != "response"
|
|
120
|
+
or len(results) != 1
|
|
121
|
+
or (results[0].text or "").strip().casefold() != "success"
|
|
122
|
+
or len(results[0]) != 0
|
|
123
|
+
):
|
|
124
|
+
raise ProtocolError(f"Invalid {command} acknowledgement")
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def _serialize(root: ElementTree.Element) -> bytes:
|
|
128
|
+
return ElementTree.tostring(root, encoding="utf-8", xml_declaration=True)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def _request(alias: str, ui_id: UUID) -> ElementTree.Element:
|
|
132
|
+
root = ElementTree.Element(_tag(RDBG_NS, "request"))
|
|
133
|
+
ElementTree.SubElement(root, _tag(RDBG_NS, "infoBaseAlias")).text = alias
|
|
134
|
+
ElementTree.SubElement(root, _tag(RDBG_NS, "idOfDebuggerUI")).text = str(ui_id)
|
|
135
|
+
return root
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def _target_id_element(parent: ElementTree.Element, name: str, target: TargetId) -> None:
|
|
139
|
+
target_element = ElementTree.SubElement(
|
|
140
|
+
parent,
|
|
141
|
+
_tag(RDBG_NS, name),
|
|
142
|
+
{_tag(XSI_NS, "type"): "bd:DebugTargetIdLight"},
|
|
143
|
+
)
|
|
144
|
+
ElementTree.SubElement(target_element, _tag(BASE_NS, "id")).text = str(target.id)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def build_attach_request(alias: str, ui_id: UUID) -> bytes:
|
|
148
|
+
root = _request(alias, ui_id)
|
|
149
|
+
options = ElementTree.SubElement(root, _tag(RDBG_NS, "options"))
|
|
150
|
+
ElementTree.SubElement(options, _tag(RDBG_NS, "foregroundAbility")).text = "true"
|
|
151
|
+
return _serialize(root)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def build_detach_request(alias: str, ui_id: UUID) -> bytes:
|
|
155
|
+
return _serialize(_request(alias, ui_id))
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def build_terminate_request(
|
|
159
|
+
alias: str, ui_id: UUID, targets: tuple[TargetId, ...], states_payload: bytes,
|
|
160
|
+
*, target_type: str = "Server",
|
|
161
|
+
) -> bytes:
|
|
162
|
+
"""Terminate requires full native identities, unlike attach/step Light IDs."""
|
|
163
|
+
if target_type not in {"Server", "ManagedClient"}:
|
|
164
|
+
raise ValueError("Unsupported termination target type")
|
|
165
|
+
document = _parse(states_payload)
|
|
166
|
+
root = _request(alias, ui_id)
|
|
167
|
+
for target in targets:
|
|
168
|
+
matches = [
|
|
169
|
+
node for node in _descendants(document, "targetID")
|
|
170
|
+
if _target_id(node) == target
|
|
171
|
+
and _required_text(node, "targetType") == target_type
|
|
172
|
+
]
|
|
173
|
+
if len(matches) != 1:
|
|
174
|
+
raise ProtocolError(f"{target_type} termination requires one exact target identity")
|
|
175
|
+
identity = ElementTree.fromstring(ElementTree.tostring(matches[0]))
|
|
176
|
+
identity.tag = _tag(RDBG_NS, "targetID")
|
|
177
|
+
root.append(identity)
|
|
178
|
+
if not targets:
|
|
179
|
+
raise ValueError(f"{target_type} termination requires at least one target")
|
|
180
|
+
return _serialize(root)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def build_init_settings_request(
|
|
184
|
+
alias: str, ui_id: UUID, *, break_on_next: bool = False
|
|
185
|
+
) -> bytes:
|
|
186
|
+
root = _request(alias, ui_id)
|
|
187
|
+
data = ElementTree.SubElement(root, _tag(RDBG_NS, "data"))
|
|
188
|
+
ElementTree.SubElement(data, _tag(RDBG_NS, "breakOnNextLine")).text = str(
|
|
189
|
+
break_on_next
|
|
190
|
+
).lower()
|
|
191
|
+
return _serialize(root)
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def build_auto_attach_request(
|
|
195
|
+
alias: str,
|
|
196
|
+
ui_id: UUID,
|
|
197
|
+
*,
|
|
198
|
+
target_types: Iterable[str] = ("ServerEmulation", "ManagedClient"),
|
|
199
|
+
) -> bytes:
|
|
200
|
+
root = _request(alias, ui_id)
|
|
201
|
+
settings = ElementTree.SubElement(
|
|
202
|
+
root,
|
|
203
|
+
_tag(RDBG_NS, "autoAttachSettings"),
|
|
204
|
+
{_tag(XSI_NS, "type"): "aa:DebugAutoAttachSettings"},
|
|
205
|
+
)
|
|
206
|
+
for target_type in target_types:
|
|
207
|
+
ElementTree.SubElement(
|
|
208
|
+
settings, _tag(AUTO_ATTACH_NS, "targetType")
|
|
209
|
+
).text = target_type
|
|
210
|
+
ElementTree.SubElement(settings, _tag(AUTO_ATTACH_NS, "areaName")).text = ""
|
|
211
|
+
return _serialize(root)
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def build_get_targets_request(alias: str, ui_id: UUID) -> bytes:
|
|
215
|
+
return _serialize(_request(alias, ui_id))
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def build_clear_break_request(alias: str, ui_id: UUID) -> bytes:
|
|
219
|
+
return _serialize(_request(alias, ui_id))
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def build_attach_target_request(
|
|
223
|
+
alias: str, ui_id: UUID, target: TargetId, *, attach: bool
|
|
224
|
+
) -> bytes:
|
|
225
|
+
root = _request(alias, ui_id)
|
|
226
|
+
ElementTree.SubElement(root, _tag(RDBG_NS, "attach")).text = str(attach).lower()
|
|
227
|
+
_target_id_element(root, "id", target)
|
|
228
|
+
return _serialize(root)
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def build_call_stack_request(alias: str, ui_id: UUID, target: TargetId) -> bytes:
|
|
232
|
+
root = _request(alias, ui_id)
|
|
233
|
+
outer = ElementTree.SubElement(root, _tag(RDBG_NS, "id"))
|
|
234
|
+
ElementTree.SubElement(outer, _tag(BASE_NS, "id")).text = str(target.id)
|
|
235
|
+
return _serialize(root)
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
def build_breakpoint_request(
|
|
239
|
+
alias: str, ui_id: UUID, location: ModuleLocation
|
|
240
|
+
) -> bytes:
|
|
241
|
+
return build_breakpoints_request(alias, ui_id, (location,))
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def build_breakpoints_request(
|
|
245
|
+
alias: str,
|
|
246
|
+
ui_id: UUID,
|
|
247
|
+
locations: Iterable[ModuleLocation],
|
|
248
|
+
) -> bytes:
|
|
249
|
+
location_items = tuple(locations)
|
|
250
|
+
if not location_items:
|
|
251
|
+
raise ValueError("At least one breakpoint location is required")
|
|
252
|
+
fields: Iterable[tuple[str, str]] = (
|
|
253
|
+
("isActive", "true"),
|
|
254
|
+
("breakOnCondition", "false"),
|
|
255
|
+
("breakOnParentMethod", "false"),
|
|
256
|
+
("breakOnHitCount", "false"),
|
|
257
|
+
("hitCountVariant", "0"),
|
|
258
|
+
("hitCount", "1"),
|
|
259
|
+
("showOutputMessage", "false"),
|
|
260
|
+
("putStackTrace", "false"),
|
|
261
|
+
("putHitCount", "false"),
|
|
262
|
+
("continueExecution", "false"),
|
|
263
|
+
("temp", "false"),
|
|
264
|
+
("user", "true"),
|
|
265
|
+
)
|
|
266
|
+
common_fields = "".join(
|
|
267
|
+
f"<{name}>{escape(value)}</{name}>" for name, value in fields
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
def module_key(location: ModuleLocation) -> tuple[object, ...]:
|
|
271
|
+
return (
|
|
272
|
+
location.module_type,
|
|
273
|
+
location.url,
|
|
274
|
+
location.extension_name,
|
|
275
|
+
location.object_id,
|
|
276
|
+
location.property_id,
|
|
277
|
+
location.ext_id,
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
grouped: dict[tuple[object, ...], list[ModuleLocation]] = {}
|
|
281
|
+
for location in location_items:
|
|
282
|
+
grouped.setdefault(module_key(location), []).append(location)
|
|
283
|
+
|
|
284
|
+
def module_breakpoint(module_locations: list[ModuleLocation]) -> str:
|
|
285
|
+
location = module_locations[0]
|
|
286
|
+
bp_items = "".join(
|
|
287
|
+
f"<bpInfo><line>{item.line}</line>{common_fields}</bpInfo>"
|
|
288
|
+
for item in module_locations
|
|
289
|
+
)
|
|
290
|
+
return (
|
|
291
|
+
f'<moduleBPInfo xmlns="{BP_NS}"><id>'
|
|
292
|
+
f'<type xmlns="{BASE_NS}">{escape(location.module_type)}</type>'
|
|
293
|
+
f'<URL xmlns="{BASE_NS}">{escape(location.url)}</URL>'
|
|
294
|
+
f'<extensionName xmlns="{BASE_NS}">{escape(location.extension_name)}</extensionName>'
|
|
295
|
+
f'<objectID xmlns="{BASE_NS}">{location.object_id}</objectID>'
|
|
296
|
+
f'<propertyID xmlns="{BASE_NS}">{location.property_id}</propertyID>'
|
|
297
|
+
f'<extId xmlns="{BASE_NS}">{location.ext_id}</extId>'
|
|
298
|
+
f"</id>{bp_items}</moduleBPInfo>"
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
breakpoint_items = "".join(
|
|
302
|
+
module_breakpoint(module_locations)
|
|
303
|
+
for module_locations in grouped.values()
|
|
304
|
+
)
|
|
305
|
+
xml = (
|
|
306
|
+
'<?xml version="1.0" encoding="utf-8"?>'
|
|
307
|
+
"<request>"
|
|
308
|
+
f'<infoBaseAlias xmlns="{RDBG_NS}">{escape(alias)}</infoBaseAlias>'
|
|
309
|
+
f'<idOfDebuggerUI xmlns="{RDBG_NS}">{ui_id}</idOfDebuggerUI>'
|
|
310
|
+
f'<bpWorkspace xmlns="{RDBG_NS}">'
|
|
311
|
+
f"{breakpoint_items}"
|
|
312
|
+
"</bpWorkspace></request>"
|
|
313
|
+
)
|
|
314
|
+
return xml.encode("utf-8")
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
def _calculation_storage(
|
|
318
|
+
parent: ElementTree.Element,
|
|
319
|
+
element_name: str,
|
|
320
|
+
expression: str,
|
|
321
|
+
result_id: UUID,
|
|
322
|
+
*,
|
|
323
|
+
max_text_size: int = 307_200,
|
|
324
|
+
interface: str = "context",
|
|
325
|
+
start_index: int | None = None,
|
|
326
|
+
page_size: int | None = None,
|
|
327
|
+
stack_level: int = 0,
|
|
328
|
+
) -> None:
|
|
329
|
+
storage = ElementTree.SubElement(
|
|
330
|
+
parent,
|
|
331
|
+
_tag(RDBG_NS, element_name),
|
|
332
|
+
{_tag(XSI_NS, "type"): "calc:CalculationSourceDataStorage"},
|
|
333
|
+
)
|
|
334
|
+
ElementTree.SubElement(storage, _tag(CALC_NS, "stackLevel")).text = str(
|
|
335
|
+
stack_level
|
|
336
|
+
)
|
|
337
|
+
source = ElementTree.SubElement(storage, _tag(CALC_NS, "srcCalcInfo"))
|
|
338
|
+
ElementTree.SubElement(source, _tag(CALC_NS, "expressionResultID")).text = str(result_id)
|
|
339
|
+
item = ElementTree.SubElement(source, _tag(CALC_NS, "calcItem"))
|
|
340
|
+
ElementTree.SubElement(item, _tag(CALC_NS, "itemType")).text = "expression"
|
|
341
|
+
ElementTree.SubElement(item, _tag(CALC_NS, "expression")).text = expression
|
|
342
|
+
ElementTree.SubElement(item, _tag(CALC_NS, "property")).text = ""
|
|
343
|
+
ElementTree.SubElement(source, _tag(CALC_NS, "interfaces")).text = interface
|
|
344
|
+
if start_index is not None:
|
|
345
|
+
ElementTree.SubElement(source, _tag(CALC_NS, "startIndex")).text = str(
|
|
346
|
+
start_index
|
|
347
|
+
)
|
|
348
|
+
if page_size is not None:
|
|
349
|
+
ElementTree.SubElement(source, _tag(CALC_NS, "pageSize")).text = str(page_size)
|
|
350
|
+
options = ElementTree.SubElement(storage, _tag(CALC_NS, "presOptions"))
|
|
351
|
+
ElementTree.SubElement(options, _tag(CALC_NS, "maxTextSize")).text = str(
|
|
352
|
+
max_text_size
|
|
353
|
+
)
|
|
354
|
+
ElementTree.SubElement(options, _tag(CALC_NS, "stopOnFirstEOL")).text = "false"
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
def build_eval_request(
|
|
358
|
+
alias: str,
|
|
359
|
+
ui_id: UUID,
|
|
360
|
+
target: TargetId,
|
|
361
|
+
expression: str,
|
|
362
|
+
result_id: UUID,
|
|
363
|
+
*,
|
|
364
|
+
max_text_size: int = 307_200,
|
|
365
|
+
stack_level: int = 0,
|
|
366
|
+
) -> bytes:
|
|
367
|
+
if max_text_size <= 0:
|
|
368
|
+
raise ValueError("max_text_size must be positive")
|
|
369
|
+
if stack_level < 0:
|
|
370
|
+
raise ValueError("stack_level must be non-negative")
|
|
371
|
+
root = _request(alias, ui_id)
|
|
372
|
+
# This is a server-side batching delay, not the HTTP timeout. Large values
|
|
373
|
+
# add directly to every variable read even when calculation is immediate.
|
|
374
|
+
ElementTree.SubElement(root, _tag(RDBG_NS, "calcWaitingTime")).text = "25"
|
|
375
|
+
_target_id_element(root, "targetID", target)
|
|
376
|
+
_calculation_storage(
|
|
377
|
+
root,
|
|
378
|
+
"expr",
|
|
379
|
+
expression,
|
|
380
|
+
result_id,
|
|
381
|
+
max_text_size=max_text_size,
|
|
382
|
+
stack_level=stack_level,
|
|
383
|
+
)
|
|
384
|
+
return _serialize(root)
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
def build_collection_eval_request(
|
|
388
|
+
alias: str,
|
|
389
|
+
ui_id: UUID,
|
|
390
|
+
target: TargetId,
|
|
391
|
+
expression: str,
|
|
392
|
+
result_id: UUID,
|
|
393
|
+
*,
|
|
394
|
+
start_index: int,
|
|
395
|
+
page_size: int = 2400,
|
|
396
|
+
max_text_size: int = 4096,
|
|
397
|
+
stack_level: int = 0,
|
|
398
|
+
) -> bytes:
|
|
399
|
+
if start_index < 0:
|
|
400
|
+
raise ValueError("start_index must be non-negative")
|
|
401
|
+
if page_size <= 0:
|
|
402
|
+
raise ValueError("page_size must be positive")
|
|
403
|
+
if max_text_size <= 0:
|
|
404
|
+
raise ValueError("max_text_size must be positive")
|
|
405
|
+
if stack_level < 0:
|
|
406
|
+
raise ValueError("stack_level must be non-negative")
|
|
407
|
+
root = _request(alias, ui_id)
|
|
408
|
+
ElementTree.SubElement(root, _tag(RDBG_NS, "calcWaitingTime")).text = "25"
|
|
409
|
+
_target_id_element(root, "targetID", target)
|
|
410
|
+
_calculation_storage(
|
|
411
|
+
root,
|
|
412
|
+
"expr",
|
|
413
|
+
expression,
|
|
414
|
+
result_id,
|
|
415
|
+
max_text_size=max_text_size,
|
|
416
|
+
interface="collection",
|
|
417
|
+
start_index=start_index,
|
|
418
|
+
page_size=page_size,
|
|
419
|
+
stack_level=stack_level,
|
|
420
|
+
)
|
|
421
|
+
return _serialize(root)
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
def build_local_variables_request(
|
|
425
|
+
alias: str,
|
|
426
|
+
ui_id: UUID,
|
|
427
|
+
target: TargetId,
|
|
428
|
+
stack_level: int,
|
|
429
|
+
result_id: UUID,
|
|
430
|
+
*,
|
|
431
|
+
max_text_size: int = 307_200,
|
|
432
|
+
) -> bytes:
|
|
433
|
+
if type(max_text_size) is not int or max_text_size <= 0:
|
|
434
|
+
raise ValueError("max_text_size must be positive")
|
|
435
|
+
root = _request(alias, ui_id)
|
|
436
|
+
ElementTree.SubElement(root, _tag(RDBG_NS, "calcWaitingTime")).text = "25"
|
|
437
|
+
_target_id_element(root, "targetID", target)
|
|
438
|
+
storage = ElementTree.SubElement(
|
|
439
|
+
root,
|
|
440
|
+
_tag(RDBG_NS, "expr"),
|
|
441
|
+
{_tag(XSI_NS, "type"): "calc:CalculationSourceDataStorage"},
|
|
442
|
+
)
|
|
443
|
+
ElementTree.SubElement(storage, _tag(CALC_NS, "stackLevel")).text = str(
|
|
444
|
+
stack_level
|
|
445
|
+
)
|
|
446
|
+
source = ElementTree.SubElement(storage, _tag(CALC_NS, "srcCalcInfo"))
|
|
447
|
+
ElementTree.SubElement(source, _tag(CALC_NS, "expressionResultID")).text = str(
|
|
448
|
+
result_id
|
|
449
|
+
)
|
|
450
|
+
ElementTree.SubElement(source, _tag(CALC_NS, "interfaces")).text = "context"
|
|
451
|
+
options = ElementTree.SubElement(storage, _tag(CALC_NS, "presOptions"))
|
|
452
|
+
ElementTree.SubElement(options, _tag(CALC_NS, "maxTextSize")).text = str(max_text_size)
|
|
453
|
+
ElementTree.SubElement(options, _tag(CALC_NS, "stopOnFirstEOL")).text = "false"
|
|
454
|
+
return _serialize(root)
|
|
455
|
+
|
|
456
|
+
|
|
457
|
+
def build_modify_request(
|
|
458
|
+
alias: str,
|
|
459
|
+
ui_id: UUID,
|
|
460
|
+
target: TargetId,
|
|
461
|
+
variable: str,
|
|
462
|
+
value_expression: str,
|
|
463
|
+
result_id: UUID,
|
|
464
|
+
) -> bytes:
|
|
465
|
+
root = _request(alias, ui_id)
|
|
466
|
+
_target_id_element(root, "targetID", target)
|
|
467
|
+
_calculation_storage(root, "modifyDataPath", variable, result_id)
|
|
468
|
+
value = ElementTree.SubElement(
|
|
469
|
+
root,
|
|
470
|
+
_tag(RDBG_NS, "newValueInfo"),
|
|
471
|
+
{_tag(XSI_NS, "type"): "calc:NewValueInfo"},
|
|
472
|
+
)
|
|
473
|
+
ElementTree.SubElement(value, _tag(CALC_NS, "variant")).text = "expr"
|
|
474
|
+
ElementTree.SubElement(value, _tag(CALC_NS, "valueExpression")).text = value_expression
|
|
475
|
+
ElementTree.SubElement(root, _tag(RDBG_NS, "timeout")).text = "60000"
|
|
476
|
+
return _serialize(root)
|
|
477
|
+
|
|
478
|
+
|
|
479
|
+
def build_step_request(alias: str, ui_id: UUID, target: TargetId) -> bytes:
|
|
480
|
+
root = _request(alias, ui_id)
|
|
481
|
+
_target_id_element(root, "targetID", target)
|
|
482
|
+
ElementTree.SubElement(root, _tag(RDBG_NS, "action")).text = "Continue"
|
|
483
|
+
ElementTree.SubElement(root, _tag(RDBG_NS, "simple")).text = "false"
|
|
484
|
+
return _serialize(root)
|
|
485
|
+
|
|
486
|
+
|
|
487
|
+
def _module_location(frame: ElementTree.Element) -> ModuleLocation:
|
|
488
|
+
module_matches = _descendants(frame, "moduleID")
|
|
489
|
+
if not module_matches:
|
|
490
|
+
raise ProtocolError("RDBG XML is missing required moduleID")
|
|
491
|
+
module = module_matches[0]
|
|
492
|
+
try:
|
|
493
|
+
type_nodes = _descendants(module, "type")
|
|
494
|
+
module_type = (
|
|
495
|
+
(type_nodes[0].text or "").strip() if type_nodes else "ConfigModule"
|
|
496
|
+
)
|
|
497
|
+
if not module_type:
|
|
498
|
+
module_type = "ConfigModule"
|
|
499
|
+
url_nodes = _descendants(module, "URL")
|
|
500
|
+
extension_nodes = _descendants(module, "extensionName")
|
|
501
|
+
ext_id_nodes = _descendants(module, "extId")
|
|
502
|
+
return ModuleLocation(
|
|
503
|
+
module_type=module_type,
|
|
504
|
+
url=(url_nodes[0].text or "").strip() if url_nodes else "",
|
|
505
|
+
object_id=UUID(_required_text(module, "objectID")),
|
|
506
|
+
property_id=UUID(_required_text(module, "propertyID")),
|
|
507
|
+
line=int(_required_text(frame, "lineNo")),
|
|
508
|
+
extension_name=(extension_nodes[0].text or "").strip()
|
|
509
|
+
if extension_nodes
|
|
510
|
+
else "",
|
|
511
|
+
ext_id=int((ext_id_nodes[0].text or "0").strip())
|
|
512
|
+
if ext_id_nodes
|
|
513
|
+
else 0,
|
|
514
|
+
)
|
|
515
|
+
except ValueError as error:
|
|
516
|
+
raise ProtocolError(f"Invalid module location: {error}") from error
|
|
517
|
+
|
|
518
|
+
|
|
519
|
+
def parse_targets(payload: bytes) -> list[DebugTarget]:
|
|
520
|
+
root = _parse(payload)
|
|
521
|
+
targets: list[DebugTarget] = []
|
|
522
|
+
for item in _descendants(root, "item"):
|
|
523
|
+
target_nodes = _children(item, "targetID")
|
|
524
|
+
if not target_nodes:
|
|
525
|
+
raise ProtocolError("RDBG XML is missing required targetID")
|
|
526
|
+
target_node = target_nodes[0]
|
|
527
|
+
try:
|
|
528
|
+
target_id = _target_id(target_node)
|
|
529
|
+
state_number_text = _required_text(item, "stateNum")
|
|
530
|
+
targets.append(
|
|
531
|
+
DebugTarget(
|
|
532
|
+
target_id=target_id,
|
|
533
|
+
target_type=_required_text(target_node, "targetType"),
|
|
534
|
+
state=_required_text(item, "state"),
|
|
535
|
+
state_number=int(state_number_text),
|
|
536
|
+
)
|
|
537
|
+
)
|
|
538
|
+
except ValueError as error:
|
|
539
|
+
raise ProtocolError(f"Invalid target state: {error}") from error
|
|
540
|
+
return targets
|
|
541
|
+
|
|
542
|
+
|
|
543
|
+
def parse_call_stack(payload: bytes, target: TargetId) -> list[StackFrame]:
|
|
544
|
+
root = _parse(payload)
|
|
545
|
+
frames: list[StackFrame] = []
|
|
546
|
+
protocol_frames = _descendants(root, "callStack")
|
|
547
|
+
for level, frame in enumerate(reversed(protocol_frames)):
|
|
548
|
+
if not (
|
|
549
|
+
_descendants(frame, "objectID")
|
|
550
|
+
and _descendants(frame, "propertyID")
|
|
551
|
+
):
|
|
552
|
+
continue
|
|
553
|
+
frames.append(StackFrame(target, level, _module_location(frame)))
|
|
554
|
+
return frames
|
|
555
|
+
|
|
556
|
+
|
|
557
|
+
def parse_ping_events(payload: bytes) -> list[StopEvent]:
|
|
558
|
+
return parse_ping_events_from_document(parse_ping_document(payload))
|
|
559
|
+
|
|
560
|
+
|
|
561
|
+
def parse_ping_events_from_document(
|
|
562
|
+
root: ElementTree.Element,
|
|
563
|
+
) -> list[StopEvent]:
|
|
564
|
+
events: list[StopEvent] = []
|
|
565
|
+
for item in _children(root, "result"):
|
|
566
|
+
commands = _children(item, "cmdID")
|
|
567
|
+
if not commands or not commands[0].text:
|
|
568
|
+
continue
|
|
569
|
+
command = commands[0].text.strip()
|
|
570
|
+
if command != "callStackFormed":
|
|
571
|
+
continue
|
|
572
|
+
target_nodes = _children(item, "targetID")
|
|
573
|
+
stack_nodes = _children(item, "callStack")
|
|
574
|
+
if not target_nodes or not stack_nodes:
|
|
575
|
+
raise ProtocolError("Stop event is missing targetID or callStack")
|
|
576
|
+
target_node = target_nodes[0]
|
|
577
|
+
target = _target_id(target_node)
|
|
578
|
+
runtime_errors = _descendants(item, "exceptionStr")
|
|
579
|
+
runtime_error = (
|
|
580
|
+
_decode_presentation(runtime_errors[0].text or "")
|
|
581
|
+
if runtime_errors
|
|
582
|
+
else ""
|
|
583
|
+
)
|
|
584
|
+
addressable_frames: list[StackFrame] = []
|
|
585
|
+
for level, frame in enumerate(reversed(stack_nodes)):
|
|
586
|
+
if not (
|
|
587
|
+
_descendants(frame, "objectID")
|
|
588
|
+
and _descendants(frame, "propertyID")
|
|
589
|
+
):
|
|
590
|
+
continue
|
|
591
|
+
addressable_frames.append(StackFrame(target, level, _module_location(frame)))
|
|
592
|
+
if not addressable_frames:
|
|
593
|
+
raise ProtocolError("Stop event has no addressable stack frame")
|
|
594
|
+
stack = tuple(frame.location for frame in addressable_frames)
|
|
595
|
+
events.append(
|
|
596
|
+
StopEvent(
|
|
597
|
+
target,
|
|
598
|
+
stack[0],
|
|
599
|
+
command,
|
|
600
|
+
stop_by_breakpoint=_optional_bool(item, "stopByBP"),
|
|
601
|
+
suspended_by_other=_optional_bool(item, "suspendedByOther"),
|
|
602
|
+
stack=stack,
|
|
603
|
+
runtime_error=runtime_error,
|
|
604
|
+
stack_frames=tuple(addressable_frames),
|
|
605
|
+
)
|
|
606
|
+
)
|
|
607
|
+
return events
|
|
608
|
+
|
|
609
|
+
|
|
610
|
+
def parse_ping_target_events(payload: bytes) -> list[DebugTarget]:
|
|
611
|
+
return parse_ping_target_events_from_document(parse_ping_document(payload))
|
|
612
|
+
|
|
613
|
+
|
|
614
|
+
def parse_ping_target_events_from_document(
|
|
615
|
+
root: ElementTree.Element,
|
|
616
|
+
) -> list[DebugTarget]:
|
|
617
|
+
targets: list[DebugTarget] = []
|
|
618
|
+
for item in _children(root, "result"):
|
|
619
|
+
commands = _children(item, "cmdID")
|
|
620
|
+
if not commands or (commands[0].text or "").strip() != "targetStarted":
|
|
621
|
+
continue
|
|
622
|
+
target_nodes = _children(item, "targetID")
|
|
623
|
+
if not target_nodes:
|
|
624
|
+
raise ProtocolError("Target-started event is missing targetID")
|
|
625
|
+
target_node = target_nodes[0]
|
|
626
|
+
targets.append(
|
|
627
|
+
DebugTarget(
|
|
628
|
+
_target_id(target_node),
|
|
629
|
+
_required_text(target_node, "targetType"),
|
|
630
|
+
"Started",
|
|
631
|
+
)
|
|
632
|
+
)
|
|
633
|
+
return targets
|
|
634
|
+
|
|
635
|
+
|
|
636
|
+
def _decode_presentation(value: str) -> str:
|
|
637
|
+
try:
|
|
638
|
+
compact = "".join(value.split())
|
|
639
|
+
return base64.b64decode(compact, validate=True).decode("utf-8")
|
|
640
|
+
except (ValueError, UnicodeDecodeError):
|
|
641
|
+
return value
|
|
642
|
+
|
|
643
|
+
|
|
644
|
+
def _preferred(element: ElementTree.Element, name: str) -> list[ElementTree.Element]:
|
|
645
|
+
direct = _children(element, name)
|
|
646
|
+
return direct if direct else _descendants(element, name)
|
|
647
|
+
|
|
648
|
+
|
|
649
|
+
def _required_preferred_text(element: ElementTree.Element, name: str) -> str:
|
|
650
|
+
matches = _children(element, name)
|
|
651
|
+
if matches and matches[0].text is not None and matches[0].text.strip():
|
|
652
|
+
return matches[0].text.strip()
|
|
653
|
+
return _required_text(element, name)
|
|
654
|
+
|
|
655
|
+
|
|
656
|
+
def _parse_evaluation_node(result: ElementTree.Element) -> EvaluationResult:
|
|
657
|
+
base_nodes = _children(result, "evalExprResBaseData")
|
|
658
|
+
container = base_nodes[0] if base_nodes else result
|
|
659
|
+
value_nodes = _preferred(container, "resultValueInfo")
|
|
660
|
+
if not value_nodes:
|
|
661
|
+
raise ProtocolError("RDBG XML is missing required resultValueInfo")
|
|
662
|
+
value = value_nodes[0]
|
|
663
|
+
error_nodes = _preferred(container, "errorOccurred")
|
|
664
|
+
error_occurred = bool(error_nodes and (error_nodes[0].text or "").lower() == "true")
|
|
665
|
+
error_text_nodes = _preferred(container, "exceptionStr")
|
|
666
|
+
error_text = _decode_presentation(error_text_nodes[0].text or "") if error_text_nodes else ""
|
|
667
|
+
type_nodes = _preferred(value, "typeName")
|
|
668
|
+
if error_occurred and not type_nodes:
|
|
669
|
+
type_name = "Ошибка"
|
|
670
|
+
else:
|
|
671
|
+
type_name = _required_text(value, "typeName")
|
|
672
|
+
presentation_nodes = _preferred(value, "pres")
|
|
673
|
+
type_code_nodes = _preferred(value, "typeCode")
|
|
674
|
+
value_string_nodes = _preferred(value, "valueString")
|
|
675
|
+
value_decimal_nodes = _preferred(value, "valueDecimal")
|
|
676
|
+
collection_size_nodes = _preferred(value, "collectionSize")
|
|
677
|
+
if error_occurred and not presentation_nodes:
|
|
678
|
+
presentation = error_text
|
|
679
|
+
else:
|
|
680
|
+
presentation = (
|
|
681
|
+
_decode_presentation(presentation_nodes[0].text or "")
|
|
682
|
+
if presentation_nodes
|
|
683
|
+
else (
|
|
684
|
+
(value_decimal_nodes[0].text or "").strip()
|
|
685
|
+
if value_decimal_nodes
|
|
686
|
+
else type_name
|
|
687
|
+
)
|
|
688
|
+
)
|
|
689
|
+
collection_rows: list[CollectionRow] = []
|
|
690
|
+
calculation_nodes = _children(container, "calculationResult")
|
|
691
|
+
row_nodes = (
|
|
692
|
+
_children(calculation_nodes[0], "valueOfCollectionInfo")
|
|
693
|
+
if calculation_nodes
|
|
694
|
+
else _descendants(container, "valueOfCollectionInfo")
|
|
695
|
+
)
|
|
696
|
+
for row_index, row_node in enumerate(row_nodes):
|
|
697
|
+
cells: list[CollectionCell] = []
|
|
698
|
+
cell_values: list[tuple[str, ElementTree.Element]] = []
|
|
699
|
+
for property_node in _children(row_node, "valueOfContextPropInfo"):
|
|
700
|
+
property_info_nodes = _children(property_node, "propInfo")
|
|
701
|
+
name_nodes = (
|
|
702
|
+
_children(property_info_nodes[0], "propName")
|
|
703
|
+
if property_info_nodes
|
|
704
|
+
else _descendants(property_node, "propName")
|
|
705
|
+
)
|
|
706
|
+
cell_value_nodes = _children(property_node, "valueInfo")
|
|
707
|
+
if not cell_value_nodes:
|
|
708
|
+
continue
|
|
709
|
+
cell_values.append(
|
|
710
|
+
(
|
|
711
|
+
(
|
|
712
|
+
(name_nodes[0].text or "").strip()
|
|
713
|
+
if name_nodes
|
|
714
|
+
else "Значение"
|
|
715
|
+
),
|
|
716
|
+
cell_value_nodes[0],
|
|
717
|
+
)
|
|
718
|
+
)
|
|
719
|
+
if not cell_values:
|
|
720
|
+
cell_values.extend(
|
|
721
|
+
("Значение", cell_value)
|
|
722
|
+
for cell_value in _children(row_node, "valueInfo")
|
|
723
|
+
)
|
|
724
|
+
for cell_name, cell_value in cell_values:
|
|
725
|
+
cell_type = _required_text(cell_value, "typeName")
|
|
726
|
+
cell_presentations = _children(cell_value, "pres")
|
|
727
|
+
cell_strings = _children(cell_value, "valueString")
|
|
728
|
+
cell_decimals = _children(cell_value, "valueDecimal")
|
|
729
|
+
cell_dates = _children(cell_value, "valueDateTime")
|
|
730
|
+
cell_booleans = _children(cell_value, "valueBoolean")
|
|
731
|
+
cell_presentation = (
|
|
732
|
+
_decode_presentation(cell_presentations[0].text or "")
|
|
733
|
+
if cell_presentations
|
|
734
|
+
else cell_type
|
|
735
|
+
)
|
|
736
|
+
cells.append(
|
|
737
|
+
CollectionCell(
|
|
738
|
+
name=cell_name,
|
|
739
|
+
type_name=cell_type,
|
|
740
|
+
presentation=cell_presentation,
|
|
741
|
+
value_string=(
|
|
742
|
+
_decode_presentation(cell_strings[0].text or "")
|
|
743
|
+
if cell_strings
|
|
744
|
+
else ""
|
|
745
|
+
),
|
|
746
|
+
value_decimal=(
|
|
747
|
+
(cell_decimals[0].text or "").strip()
|
|
748
|
+
if cell_decimals
|
|
749
|
+
else ""
|
|
750
|
+
),
|
|
751
|
+
value_date_time=(
|
|
752
|
+
(cell_dates[0].text or "").strip() if cell_dates else ""
|
|
753
|
+
),
|
|
754
|
+
value_boolean=(
|
|
755
|
+
(cell_booleans[0].text or "").strip().casefold() == "true"
|
|
756
|
+
if cell_booleans
|
|
757
|
+
else None
|
|
758
|
+
),
|
|
759
|
+
)
|
|
760
|
+
)
|
|
761
|
+
collection_rows.append(CollectionRow(row_index, tuple(cells)))
|
|
762
|
+
return EvaluationResult(
|
|
763
|
+
result_id=UUID(_required_preferred_text(container, "expressionResultID")),
|
|
764
|
+
type_name=type_name,
|
|
765
|
+
presentation=presentation,
|
|
766
|
+
error_occurred=error_occurred,
|
|
767
|
+
error_text=error_text,
|
|
768
|
+
type_code=(
|
|
769
|
+
int((type_code_nodes[0].text or "").strip())
|
|
770
|
+
if type_code_nodes and (type_code_nodes[0].text or "").strip()
|
|
771
|
+
else None
|
|
772
|
+
),
|
|
773
|
+
value_string=(
|
|
774
|
+
_decode_presentation(value_string_nodes[0].text or "")
|
|
775
|
+
if value_string_nodes
|
|
776
|
+
else ""
|
|
777
|
+
),
|
|
778
|
+
collection_size=(
|
|
779
|
+
int((collection_size_nodes[0].text or "").strip())
|
|
780
|
+
if collection_size_nodes
|
|
781
|
+
and (collection_size_nodes[0].text or "").strip()
|
|
782
|
+
else None
|
|
783
|
+
),
|
|
784
|
+
collection_rows=tuple(collection_rows),
|
|
785
|
+
value_decimal=(
|
|
786
|
+
(value_decimal_nodes[0].text or "").strip()
|
|
787
|
+
if value_decimal_nodes
|
|
788
|
+
else ""
|
|
789
|
+
),
|
|
790
|
+
)
|
|
791
|
+
|
|
792
|
+
|
|
793
|
+
def parse_eval_response(payload: bytes) -> EvaluationResult | None:
|
|
794
|
+
root = _parse(payload)
|
|
795
|
+
result_nodes = _children(root, "result")
|
|
796
|
+
if not result_nodes:
|
|
797
|
+
if not list(root) and not (root.text or "").strip():
|
|
798
|
+
return None
|
|
799
|
+
raise ProtocolError("RDBG XML is missing required result")
|
|
800
|
+
return _parse_evaluation_node(result_nodes[0])
|
|
801
|
+
|
|
802
|
+
|
|
803
|
+
def parse_eval_result(payload: bytes) -> EvaluationResult:
|
|
804
|
+
result = parse_eval_response(payload)
|
|
805
|
+
if result is None:
|
|
806
|
+
raise ProtocolError("RDBG XML is missing required result")
|
|
807
|
+
return result
|
|
808
|
+
|
|
809
|
+
|
|
810
|
+
def parse_modify_result(payload: bytes) -> ModifyResult:
|
|
811
|
+
root = _parse(payload)
|
|
812
|
+
state_nodes = _children(root, "newValueState")
|
|
813
|
+
if not state_nodes:
|
|
814
|
+
raise ProtocolError("RDBG XML is missing required newValueState")
|
|
815
|
+
state = state_nodes[0]
|
|
816
|
+
state_text = _required_text(state, "evalResultState")
|
|
817
|
+
error_nodes = _descendants(state, "errorOccurred")
|
|
818
|
+
error_text_nodes = _descendants(state, "exceptionStr")
|
|
819
|
+
error_text = (
|
|
820
|
+
_decode_presentation(error_text_nodes[0].text or "")
|
|
821
|
+
if error_text_nodes
|
|
822
|
+
else ""
|
|
823
|
+
)
|
|
824
|
+
error_occurred = (
|
|
825
|
+
state_text.casefold() != "correctly"
|
|
826
|
+
or bool(
|
|
827
|
+
error_nodes
|
|
828
|
+
and (error_nodes[0].text or "").strip().casefold() == "true"
|
|
829
|
+
)
|
|
830
|
+
or bool(error_text)
|
|
831
|
+
)
|
|
832
|
+
value_nodes = _descendants(state, "resultValueInfo")
|
|
833
|
+
if error_occurred:
|
|
834
|
+
type_name = "Ошибка"
|
|
835
|
+
presentation = error_text or state_text
|
|
836
|
+
elif not value_nodes:
|
|
837
|
+
raise ProtocolError("RDBG XML is missing required resultValueInfo")
|
|
838
|
+
else:
|
|
839
|
+
value = value_nodes[0]
|
|
840
|
+
type_name = _required_text(value, "typeName")
|
|
841
|
+
type_code_nodes = _descendants(value, "typeCode")
|
|
842
|
+
value_string_nodes = _descendants(value, "valueString")
|
|
843
|
+
presentation_nodes = _descendants(value, "pres")
|
|
844
|
+
presentation = (
|
|
845
|
+
_decode_presentation(presentation_nodes[0].text or "")
|
|
846
|
+
if presentation_nodes
|
|
847
|
+
else type_name
|
|
848
|
+
)
|
|
849
|
+
return ModifyResult(
|
|
850
|
+
result_id=UUID(_required_text(state, "expressionResultID")),
|
|
851
|
+
type_name=type_name,
|
|
852
|
+
presentation=presentation,
|
|
853
|
+
error_occurred=error_occurred,
|
|
854
|
+
error_text=error_text,
|
|
855
|
+
type_code=(
|
|
856
|
+
int((type_code_nodes[0].text or "").strip())
|
|
857
|
+
if not error_occurred
|
|
858
|
+
and type_code_nodes
|
|
859
|
+
and (type_code_nodes[0].text or "").strip()
|
|
860
|
+
else None
|
|
861
|
+
),
|
|
862
|
+
value_string=(
|
|
863
|
+
_decode_presentation(value_string_nodes[0].text or "")
|
|
864
|
+
if not error_occurred and value_string_nodes
|
|
865
|
+
else ""
|
|
866
|
+
),
|
|
867
|
+
)
|
|
868
|
+
|
|
869
|
+
|
|
870
|
+
def _frame_variable(
|
|
871
|
+
node: ElementTree.Element,
|
|
872
|
+
*,
|
|
873
|
+
name_field: str,
|
|
874
|
+
value_field: str,
|
|
875
|
+
) -> FrameVariable | None:
|
|
876
|
+
name_nodes = _descendants(node, name_field)
|
|
877
|
+
value_nodes = _descendants(node, value_field)
|
|
878
|
+
if not name_nodes or not (name_nodes[0].text or "").strip() or not value_nodes:
|
|
879
|
+
return None
|
|
880
|
+
value = value_nodes[0]
|
|
881
|
+
type_name = _required_text(value, "typeName")
|
|
882
|
+
presentation_nodes = _descendants(value, "pres")
|
|
883
|
+
presentation = (
|
|
884
|
+
_decode_presentation(presentation_nodes[0].text or "")
|
|
885
|
+
if presentation_nodes
|
|
886
|
+
else type_name
|
|
887
|
+
)
|
|
888
|
+
size_nodes = _descendants(value, "collectionSize")
|
|
889
|
+
return FrameVariable(
|
|
890
|
+
name=(name_nodes[0].text or "").strip(),
|
|
891
|
+
type_name=type_name,
|
|
892
|
+
presentation=presentation,
|
|
893
|
+
collection_size=(
|
|
894
|
+
int((size_nodes[0].text or "").strip())
|
|
895
|
+
if size_nodes and (size_nodes[0].text or "").strip()
|
|
896
|
+
else None
|
|
897
|
+
),
|
|
898
|
+
)
|
|
899
|
+
|
|
900
|
+
|
|
901
|
+
def _parse_local_variables_node(result: ElementTree.Element) -> LocalVariablesResult:
|
|
902
|
+
variables: list[FrameVariable] = []
|
|
903
|
+
for node in _descendants(result, "valueOfContextPropInfo"):
|
|
904
|
+
variable = _frame_variable(
|
|
905
|
+
node, name_field="propName", value_field="valueInfo"
|
|
906
|
+
)
|
|
907
|
+
if variable is not None:
|
|
908
|
+
variables.append(variable)
|
|
909
|
+
for node in _descendants(result, "localVariables"):
|
|
910
|
+
variable = _frame_variable(
|
|
911
|
+
node,
|
|
912
|
+
name_field="localVariableName",
|
|
913
|
+
value_field="resultValueInfo",
|
|
914
|
+
)
|
|
915
|
+
if variable is not None:
|
|
916
|
+
variables.append(variable)
|
|
917
|
+
error_nodes = _descendants(result, "errorOccurred")
|
|
918
|
+
error_occurred = bool(
|
|
919
|
+
error_nodes and (error_nodes[0].text or "").strip().lower() == "true"
|
|
920
|
+
)
|
|
921
|
+
error_text_nodes = _descendants(result, "exceptionStr")
|
|
922
|
+
error_text = (
|
|
923
|
+
_decode_presentation(error_text_nodes[0].text or "")
|
|
924
|
+
if error_text_nodes
|
|
925
|
+
else ""
|
|
926
|
+
)
|
|
927
|
+
return LocalVariablesResult(
|
|
928
|
+
result_id=UUID(_required_text(result, "expressionResultID")),
|
|
929
|
+
variables=tuple(variables),
|
|
930
|
+
error_occurred=error_occurred,
|
|
931
|
+
error_text=error_text,
|
|
932
|
+
)
|
|
933
|
+
|
|
934
|
+
|
|
935
|
+
def parse_local_variables_result(payload: bytes) -> LocalVariablesResult:
|
|
936
|
+
root = _parse(payload)
|
|
937
|
+
result_nodes = _children(root, "result")
|
|
938
|
+
if not result_nodes:
|
|
939
|
+
raise ProtocolError("RDBG XML is missing required result")
|
|
940
|
+
return _parse_local_variables_node(result_nodes[0])
|
|
941
|
+
|
|
942
|
+
|
|
943
|
+
def parse_ping_local_variables(payload: bytes) -> list[LocalVariablesResult]:
|
|
944
|
+
return parse_ping_local_variables_from_document(parse_ping_document(payload))
|
|
945
|
+
|
|
946
|
+
|
|
947
|
+
def parse_ping_local_variables_from_document(
|
|
948
|
+
root: ElementTree.Element,
|
|
949
|
+
) -> list[LocalVariablesResult]:
|
|
950
|
+
results: list[LocalVariablesResult] = []
|
|
951
|
+
for item in _children(root, "result"):
|
|
952
|
+
commands = _children(item, "cmdID")
|
|
953
|
+
if not commands or (commands[0].text or "").strip() != "exprEvaluated":
|
|
954
|
+
continue
|
|
955
|
+
if _descendants(item, "valueOfCollectionInfo"):
|
|
956
|
+
continue
|
|
957
|
+
results.append(_parse_local_variables_node(item))
|
|
958
|
+
return results
|
|
959
|
+
|
|
960
|
+
|
|
961
|
+
def parse_ping_evaluations(payload: bytes) -> list[EvaluationResult]:
|
|
962
|
+
return parse_ping_evaluations_from_document(parse_ping_document(payload))
|
|
963
|
+
|
|
964
|
+
|
|
965
|
+
def parse_ping_evaluations_from_document(
|
|
966
|
+
root: ElementTree.Element,
|
|
967
|
+
) -> list[EvaluationResult]:
|
|
968
|
+
evaluations: list[EvaluationResult] = []
|
|
969
|
+
for item in _children(root, "result"):
|
|
970
|
+
commands = _children(item, "cmdID")
|
|
971
|
+
if not commands or (commands[0].text or "").strip() != "exprEvaluated":
|
|
972
|
+
continue
|
|
973
|
+
collection_nodes = _descendants(item, "valueOfCollectionInfo")
|
|
974
|
+
if not collection_nodes and (
|
|
975
|
+
_descendants(item, "valueOfContextPropInfo")
|
|
976
|
+
or _descendants(item, "localVariables")
|
|
977
|
+
):
|
|
978
|
+
continue
|
|
979
|
+
evaluations.append(_parse_evaluation_node(item))
|
|
980
|
+
return evaluations
|