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.
Files changed (87) hide show
  1. onec_interactive_runtime_core-0.1.19.dist-info/METADATA +12 -0
  2. onec_interactive_runtime_core-0.1.19.dist-info/RECORD +87 -0
  3. onec_interactive_runtime_core-0.1.19.dist-info/WHEEL +4 -0
  4. onec_interactive_runtime_core-0.1.19.dist-info/licenses/COPYRIGHT +9 -0
  5. onec_interactive_runtime_core-0.1.19.dist-info/licenses/LICENSE +232 -0
  6. onec_interactive_runtime_core-0.1.19.dist-info/licenses/THIRD_PARTY_NOTICES.txt +32 -0
  7. onec_runtime/AGENTS.md +7 -0
  8. onec_runtime/__init__.py +3 -0
  9. onec_runtime/artifacts.py +59 -0
  10. onec_runtime/bootstrap.py +310 -0
  11. onec_runtime/breakpoint_workspace.py +249 -0
  12. onec_runtime/bsl/__init__.py +139 -0
  13. onec_runtime/bsl/diagnostics.py +1046 -0
  14. onec_runtime/bsl/full_ast_worker_projection.py +522 -0
  15. onec_runtime/bsl/generated_semantic_parser.py +6322 -0
  16. onec_runtime/bsl/lexer.py +164 -0
  17. onec_runtime/bsl/module_catalog.py +420 -0
  18. onec_runtime/bsl/module_delta.py +1079 -0
  19. onec_runtime/bsl/module_universe.py +1877 -0
  20. onec_runtime/bsl/notebook_cells.py +232 -0
  21. onec_runtime/bsl/notebook_method_globals.py +74 -0
  22. onec_runtime/bsl/notebook_methods.py +175 -0
  23. onec_runtime/bsl/parser_artifact_identity.py +119 -0
  24. onec_runtime/bsl/parser_target.py +388 -0
  25. onec_runtime/bsl/preprocessor.py +16 -0
  26. onec_runtime/bsl/semantic_lowering.py +1539 -0
  27. onec_runtime/bsl/source_maps.py +2411 -0
  28. onec_runtime/bsl/worker_dependency_resolver.py +289 -0
  29. onec_runtime/bsl/worker_preprocessor.py +247 -0
  30. onec_runtime/bsl/worker_projection_model.py +137 -0
  31. onec_runtime/bsl/worker_reload_source_map.py +1697 -0
  32. onec_runtime/capture.py +113 -0
  33. onec_runtime/capture_source.py +453 -0
  34. onec_runtime/compact_table.py +376 -0
  35. onec_runtime/compact_table_backend.py +500 -0
  36. onec_runtime/config.py +198 -0
  37. onec_runtime/configurator_agent.py +520 -0
  38. onec_runtime/controller_worker.py +400 -0
  39. onec_runtime/credentials.py +18 -0
  40. onec_runtime/epf_container.py +196 -0
  41. onec_runtime/errors.py +205 -0
  42. onec_runtime/experiment.py +130 -0
  43. onec_runtime/extension_bundle.py +944 -0
  44. onec_runtime/extension_lifecycle.py +402 -0
  45. onec_runtime/extension_state.py +222 -0
  46. onec_runtime/fault_injection.py +30 -0
  47. onec_runtime/kernel.py +179 -0
  48. onec_runtime/lease.py +123 -0
  49. onec_runtime/observation.py +287 -0
  50. onec_runtime/performance_profile.py +245 -0
  51. onec_runtime/privacy.py +263 -0
  52. onec_runtime/processes.py +265 -0
  53. onec_runtime/prototype_runtime.py +2644 -0
  54. onec_runtime/rdbg/__init__.py +1 -0
  55. onec_runtime/rdbg/models.py +130 -0
  56. onec_runtime/rdbg/reconnect.py +59 -0
  57. onec_runtime/rdbg/session.py +913 -0
  58. onec_runtime/rdbg/transport.py +138 -0
  59. onec_runtime/rdbg/xml_codec.py +980 -0
  60. onec_runtime/recovery.py +106 -0
  61. onec_runtime/recovery_journal.py +79 -0
  62. onec_runtime/resources/extension/OnecInteractiveRuntime.cfe +0 -0
  63. onec_runtime/resources/extension/extension-manifest.json +108 -0
  64. onec_runtime/runtime_api.py +5272 -0
  65. onec_runtime/runtime_contracts.py +196 -0
  66. onec_runtime/server_worker.py +2034 -0
  67. onec_runtime/session.py +2123 -0
  68. onec_runtime/startup_diagnostics.py +45 -0
  69. onec_runtime/stop_routing.py +65 -0
  70. onec_runtime/supervised_processes.py +232 -0
  71. onec_runtime/supervised_runtime.py +512 -0
  72. onec_runtime/supervisor.py +363 -0
  73. onec_runtime/supervisor_model.py +148 -0
  74. onec_runtime/supervisor_protocol.py +77 -0
  75. onec_runtime/table_materialization.py +471 -0
  76. onec_runtime/table_transfer_backend.py +203 -0
  77. onec_runtime/table_value.py +311 -0
  78. onec_runtime/toolchain.py +730 -0
  79. onec_runtime/value_materialization.py +432 -0
  80. onec_runtime/value_transfer_backend.py +199 -0
  81. onec_runtime/worker_breakpoints.py +1431 -0
  82. onec_runtime/worker_epf.py +313 -0
  83. onec_runtime/worker_epf_template.py +53 -0
  84. onec_runtime/worker_stage_protocol.py +516 -0
  85. onec_runtime/worker_universe.py +3772 -0
  86. onec_runtime_build/__init__.py +1 -0
  87. 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)