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,311 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
from decimal import Decimal, InvalidOperation
|
|
6
|
+
from typing import Any, Mapping, Protocol
|
|
7
|
+
from uuid import UUID
|
|
8
|
+
|
|
9
|
+
import pandas as pd
|
|
10
|
+
|
|
11
|
+
from onec_runtime.performance_profile import PhaseRecorder
|
|
12
|
+
from onec_runtime.rdbg.models import CollectionCell, EvaluationResult
|
|
13
|
+
from onec_runtime.rdbg.session import RdbgSession
|
|
14
|
+
from onec_runtime.table_materialization import (
|
|
15
|
+
ReferenceMode,
|
|
16
|
+
ReferencePolicy,
|
|
17
|
+
TableMaterializationError,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class TableFrameMaterializer(Protocol):
|
|
22
|
+
def to_df(self, handle: str, policy: ReferencePolicy) -> Any: ...
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class CollectionEvaluationSession(Protocol):
|
|
26
|
+
def evaluate_collection(
|
|
27
|
+
self,
|
|
28
|
+
expression: str,
|
|
29
|
+
*,
|
|
30
|
+
start_index: int,
|
|
31
|
+
page_size: int,
|
|
32
|
+
) -> EvaluationResult: ...
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _number(value: str, *, exact_decimal: str = "") -> int | float:
|
|
36
|
+
if exact_decimal:
|
|
37
|
+
decimal = Decimal(exact_decimal.strip().replace(",", "."))
|
|
38
|
+
return int(decimal) if decimal == decimal.to_integral() else float(decimal)
|
|
39
|
+
normalized = "".join(character for character in value if character.isdecimal() or character in ",.-+")
|
|
40
|
+
if "," in normalized and "." not in normalized:
|
|
41
|
+
# RDBG presentations use comma both as a locale decimal separator and
|
|
42
|
+
# as an en-US group separator. Integer groups of three are IDs/counts.
|
|
43
|
+
left, right = normalized.rsplit(",", 1)
|
|
44
|
+
normalized = left + right if len(right) == 3 else left + "." + right
|
|
45
|
+
elif "," in normalized:
|
|
46
|
+
normalized = normalized.replace(",", "")
|
|
47
|
+
decimal = Decimal(normalized)
|
|
48
|
+
return int(decimal) if decimal == decimal.to_integral() else float(decimal)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def evaluation_to_python(result: EvaluationResult) -> Any:
|
|
52
|
+
value = result.presentation.strip()
|
|
53
|
+
if result.type_name == "Неопределено":
|
|
54
|
+
return None
|
|
55
|
+
if result.type_name == "Число":
|
|
56
|
+
return _number(value, exact_decimal=result.value_decimal)
|
|
57
|
+
if result.type_name == "Булево":
|
|
58
|
+
return value.lower() in {"истина", "true"}
|
|
59
|
+
if result.type_name == "Строка":
|
|
60
|
+
if result.value_string:
|
|
61
|
+
return result.value_string
|
|
62
|
+
return value[1:-1].replace('""', '"') if value.startswith('"') and value.endswith('"') else value
|
|
63
|
+
return value
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _reference_mode(value: str | ReferenceMode) -> ReferenceMode:
|
|
67
|
+
try:
|
|
68
|
+
return ReferenceMode(value)
|
|
69
|
+
except ValueError as error:
|
|
70
|
+
raise TableMaterializationError(f"unknown reference mode: {value}") from error
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _is_reference(cell: CollectionCell) -> bool:
|
|
74
|
+
return "Ссылка." in cell.type_name
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _scalar_cell(cell: CollectionCell) -> object:
|
|
78
|
+
if cell.type_name in {"Неопределено", "Null"}:
|
|
79
|
+
return None
|
|
80
|
+
if cell.type_name == "Число":
|
|
81
|
+
source = cell.value_decimal or cell.presentation
|
|
82
|
+
try:
|
|
83
|
+
value = Decimal(source.replace(",", "."))
|
|
84
|
+
except InvalidOperation as error:
|
|
85
|
+
raise TableMaterializationError(
|
|
86
|
+
f"RDBG number is invalid in column {cell.name}: {source}"
|
|
87
|
+
) from error
|
|
88
|
+
return int(value) if value == value.to_integral() else float(value)
|
|
89
|
+
if cell.type_name == "Булево":
|
|
90
|
+
if cell.value_boolean is not None:
|
|
91
|
+
return cell.value_boolean
|
|
92
|
+
return cell.presentation.strip().casefold() in {"истина", "true"}
|
|
93
|
+
if cell.type_name == "Строка":
|
|
94
|
+
if cell.value_string:
|
|
95
|
+
return cell.value_string
|
|
96
|
+
value = cell.presentation
|
|
97
|
+
return (
|
|
98
|
+
value[1:-1].replace('""', '"')
|
|
99
|
+
if value.startswith('"') and value.endswith('"')
|
|
100
|
+
else value
|
|
101
|
+
)
|
|
102
|
+
if cell.type_name == "Дата":
|
|
103
|
+
source = cell.value_date_time or cell.presentation
|
|
104
|
+
try:
|
|
105
|
+
return datetime.fromisoformat(source)
|
|
106
|
+
except ValueError:
|
|
107
|
+
return source
|
|
108
|
+
return cell.presentation
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
class RdbgCollectionMaterializer:
|
|
112
|
+
def __init__(
|
|
113
|
+
self,
|
|
114
|
+
session: CollectionEvaluationSession,
|
|
115
|
+
*,
|
|
116
|
+
page_size: int = 2400,
|
|
117
|
+
profiler: PhaseRecorder | None = None,
|
|
118
|
+
) -> None:
|
|
119
|
+
if page_size <= 0:
|
|
120
|
+
raise ValueError("page_size must be positive")
|
|
121
|
+
self.session = session
|
|
122
|
+
self.page_size = page_size
|
|
123
|
+
self.profiler = profiler
|
|
124
|
+
|
|
125
|
+
def _profile(self, phase: str, operation, **metadata): # type: ignore[no-untyped-def]
|
|
126
|
+
if self.profiler is None:
|
|
127
|
+
return operation()
|
|
128
|
+
return self.profiler.measure(phase, operation, **metadata)
|
|
129
|
+
|
|
130
|
+
def to_df(self, handle: str, policy: ReferencePolicy) -> pd.DataFrame:
|
|
131
|
+
if not handle:
|
|
132
|
+
raise TableMaterializationError("table handle must not be empty")
|
|
133
|
+
default_mode = _reference_mode(policy.refs)
|
|
134
|
+
overrides = dict(policy.ref_columns or {})
|
|
135
|
+
if not policy.uuid_suffix:
|
|
136
|
+
raise TableMaterializationError("UUID suffix must not be empty")
|
|
137
|
+
expected_columns: tuple[str, ...] | None = None
|
|
138
|
+
reference_columns: set[str] = set()
|
|
139
|
+
values: dict[str, list[object]] = {}
|
|
140
|
+
uuid_values: dict[str, list[object]] = {}
|
|
141
|
+
total: int | None = None
|
|
142
|
+
start_index = 0
|
|
143
|
+
while total is None or start_index < total:
|
|
144
|
+
page = self.session.evaluate_collection(
|
|
145
|
+
handle,
|
|
146
|
+
start_index=start_index,
|
|
147
|
+
page_size=self.page_size,
|
|
148
|
+
)
|
|
149
|
+
if page.error_occurred:
|
|
150
|
+
raise TableMaterializationError(page.error_text or page.presentation)
|
|
151
|
+
if page.collection_size is None or page.collection_size < 0:
|
|
152
|
+
raise TableMaterializationError(
|
|
153
|
+
"RDBG collection result has no valid collection size"
|
|
154
|
+
)
|
|
155
|
+
if total is None:
|
|
156
|
+
total = page.collection_size
|
|
157
|
+
elif page.collection_size != total:
|
|
158
|
+
raise TableMaterializationError(
|
|
159
|
+
"RDBG collection size changed during materialization"
|
|
160
|
+
)
|
|
161
|
+
expected_count = min(self.page_size, total - start_index)
|
|
162
|
+
if len(page.collection_rows) != expected_count:
|
|
163
|
+
raise TableMaterializationError(
|
|
164
|
+
"RDBG collection page row count mismatch: "
|
|
165
|
+
f"start={start_index}, expected={expected_count}, "
|
|
166
|
+
f"observed={len(page.collection_rows)}"
|
|
167
|
+
)
|
|
168
|
+
def convert_page() -> int:
|
|
169
|
+
nonlocal expected_columns, values, reference_columns
|
|
170
|
+
cell_count = 0
|
|
171
|
+
for offset, row in enumerate(page.collection_rows):
|
|
172
|
+
expected_index = start_index + offset
|
|
173
|
+
if row.index != expected_index:
|
|
174
|
+
raise TableMaterializationError(
|
|
175
|
+
f"RDBG collection row index mismatch: expected {expected_index}, "
|
|
176
|
+
f"got {row.index}"
|
|
177
|
+
)
|
|
178
|
+
names = tuple(cell.name for cell in row.cells)
|
|
179
|
+
if len(set(names)) != len(names):
|
|
180
|
+
raise TableMaterializationError(
|
|
181
|
+
f"RDBG collection row {row.index} has duplicate columns"
|
|
182
|
+
)
|
|
183
|
+
if expected_columns is None:
|
|
184
|
+
expected_columns = names
|
|
185
|
+
values = {name: [] for name in names}
|
|
186
|
+
reference_columns = {
|
|
187
|
+
cell.name for cell in row.cells if _is_reference(cell)
|
|
188
|
+
}
|
|
189
|
+
unknown = set(overrides) - reference_columns
|
|
190
|
+
if unknown:
|
|
191
|
+
raise TableMaterializationError(
|
|
192
|
+
"unknown reference column: " + ", ".join(sorted(unknown))
|
|
193
|
+
)
|
|
194
|
+
for name in reference_columns:
|
|
195
|
+
mode = _reference_mode(overrides.get(name, default_mode))
|
|
196
|
+
if mode is ReferenceMode.BOTH:
|
|
197
|
+
generated = name + policy.uuid_suffix
|
|
198
|
+
if generated in names:
|
|
199
|
+
raise TableMaterializationError(
|
|
200
|
+
f"generated UUID column collision: {generated}"
|
|
201
|
+
)
|
|
202
|
+
uuid_values[name] = []
|
|
203
|
+
elif names != expected_columns:
|
|
204
|
+
raise TableMaterializationError(
|
|
205
|
+
f"RDBG collection schema changed at row {row.index}"
|
|
206
|
+
)
|
|
207
|
+
cell_count += len(row.cells)
|
|
208
|
+
for cell in row.cells:
|
|
209
|
+
if not _is_reference(cell):
|
|
210
|
+
values[cell.name].append(_scalar_cell(cell))
|
|
211
|
+
continue
|
|
212
|
+
mode = _reference_mode(overrides.get(cell.name, default_mode))
|
|
213
|
+
values[cell.name].append(cell.presentation)
|
|
214
|
+
if mode in {ReferenceMode.UUID, ReferenceMode.BOTH}:
|
|
215
|
+
if not cell.value_string:
|
|
216
|
+
raise TableMaterializationError(
|
|
217
|
+
f"RDBG did not return UUID for reference column {cell.name}"
|
|
218
|
+
)
|
|
219
|
+
try:
|
|
220
|
+
identifier = UUID(cell.value_string)
|
|
221
|
+
except ValueError as error:
|
|
222
|
+
raise TableMaterializationError(
|
|
223
|
+
f"RDBG returned invalid UUID for reference column {cell.name}"
|
|
224
|
+
) from error
|
|
225
|
+
uuid_values[cell.name].append(identifier)
|
|
226
|
+
return cell_count
|
|
227
|
+
|
|
228
|
+
self._profile(
|
|
229
|
+
"dataframe.convert_page",
|
|
230
|
+
convert_page,
|
|
231
|
+
page_start=start_index,
|
|
232
|
+
item_count=lambda count: count,
|
|
233
|
+
)
|
|
234
|
+
start_index += len(page.collection_rows)
|
|
235
|
+
if total == 0:
|
|
236
|
+
break
|
|
237
|
+
|
|
238
|
+
def build_dataframe() -> pd.DataFrame:
|
|
239
|
+
result: dict[str, pd.Series] = {}
|
|
240
|
+
for name in expected_columns or ():
|
|
241
|
+
if name not in reference_columns:
|
|
242
|
+
result[name] = pd.Series(values[name])
|
|
243
|
+
continue
|
|
244
|
+
mode = _reference_mode(overrides.get(name, default_mode))
|
|
245
|
+
if mode is ReferenceMode.UUID:
|
|
246
|
+
result[name] = pd.Series(uuid_values[name], dtype="object")
|
|
247
|
+
else:
|
|
248
|
+
result[name] = pd.Series(values[name], dtype="string")
|
|
249
|
+
if mode is ReferenceMode.BOTH:
|
|
250
|
+
result[name + policy.uuid_suffix] = pd.Series(
|
|
251
|
+
uuid_values[name], dtype="object"
|
|
252
|
+
)
|
|
253
|
+
frame = pd.DataFrame(result)
|
|
254
|
+
frame.attrs["onec_transport"] = "rdbg-collection"
|
|
255
|
+
frame.attrs["rdbg_page_size"] = self.page_size
|
|
256
|
+
frame.attrs["reference_modes"] = {
|
|
257
|
+
name: _reference_mode(overrides.get(name, default_mode)).value
|
|
258
|
+
for name in reference_columns
|
|
259
|
+
}
|
|
260
|
+
return frame
|
|
261
|
+
|
|
262
|
+
return self._profile(
|
|
263
|
+
"dataframe.build",
|
|
264
|
+
build_dataframe,
|
|
265
|
+
item_count=lambda frame: len(frame.index),
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
@dataclass(frozen=True)
|
|
270
|
+
class OnecTableValue:
|
|
271
|
+
session: RdbgSession
|
|
272
|
+
expression: str
|
|
273
|
+
materializer: TableFrameMaterializer | None = None
|
|
274
|
+
|
|
275
|
+
def _eval(self, suffix: str = "") -> Any:
|
|
276
|
+
result = self.session.evaluate(self.expression + suffix)
|
|
277
|
+
if result.error_occurred:
|
|
278
|
+
raise ValueError(result.error_text)
|
|
279
|
+
return evaluation_to_python(result)
|
|
280
|
+
|
|
281
|
+
def to_df(
|
|
282
|
+
self,
|
|
283
|
+
*,
|
|
284
|
+
refs: str | ReferenceMode = ReferenceMode.PRESENTATION,
|
|
285
|
+
ref_columns: Mapping[str, str | ReferenceMode] | None = None,
|
|
286
|
+
uuid_suffix: str = "__uuid",
|
|
287
|
+
) -> Any:
|
|
288
|
+
materializer = self.materializer or RdbgCollectionMaterializer(self.session)
|
|
289
|
+
return materializer.to_df(
|
|
290
|
+
self.expression,
|
|
291
|
+
ReferencePolicy(refs, ref_columns, uuid_suffix),
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
def to_df_legacy(self): # type: ignore[no-untyped-def]
|
|
295
|
+
"""Compatibility-only RDBG cell snapshot; use ``to_df`` for server data."""
|
|
296
|
+
import pandas as pd
|
|
297
|
+
|
|
298
|
+
column_count = int(self._eval(".Колонки.Количество()"))
|
|
299
|
+
row_count = int(self._eval(".Количество()"))
|
|
300
|
+
columns = [
|
|
301
|
+
str(self._eval(f".Колонки[{index}].Имя"))
|
|
302
|
+
for index in range(column_count)
|
|
303
|
+
]
|
|
304
|
+
rows: list[dict[str, Any]] = []
|
|
305
|
+
for row_index in range(row_count):
|
|
306
|
+
row: dict[str, Any] = {}
|
|
307
|
+
for column in columns:
|
|
308
|
+
escaped = column.replace('"', '""')
|
|
309
|
+
row[column] = self._eval(f'[{row_index}]["{escaped}"]')
|
|
310
|
+
rows.append(row)
|
|
311
|
+
return pd.DataFrame(rows, columns=columns)
|