zexus 1.7.1 → 1.7.2
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.
- package/README.md +3 -3
- package/package.json +1 -1
- package/src/__init__.py +7 -0
- package/src/zexus/__init__.py +1 -1
- package/src/zexus/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/capability_system.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/debug_sanitizer.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/environment.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/error_reporter.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/input_validation.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/lexer.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/module_cache.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/module_manager.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/object.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/security.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/security_enforcement.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/syntax_validator.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/zexus_ast.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/zexus_token.cpython-312.pyc +0 -0
- package/src/zexus/access_control_system/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/zexus/access_control_system/__pycache__/access_control.cpython-312.pyc +0 -0
- package/src/zexus/advanced_types.py +17 -2
- package/src/zexus/blockchain/__init__.py +411 -0
- package/src/zexus/blockchain/accelerator.py +1160 -0
- package/src/zexus/blockchain/chain.py +660 -0
- package/src/zexus/blockchain/consensus.py +821 -0
- package/src/zexus/blockchain/contract_vm.py +1019 -0
- package/src/zexus/blockchain/crypto.py +79 -14
- package/src/zexus/blockchain/events.py +526 -0
- package/src/zexus/blockchain/loadtest.py +721 -0
- package/src/zexus/blockchain/monitoring.py +350 -0
- package/src/zexus/blockchain/mpt.py +716 -0
- package/src/zexus/blockchain/multichain.py +951 -0
- package/src/zexus/blockchain/multiprocess_executor.py +338 -0
- package/src/zexus/blockchain/network.py +886 -0
- package/src/zexus/blockchain/node.py +666 -0
- package/src/zexus/blockchain/rpc.py +1203 -0
- package/src/zexus/blockchain/rust_bridge.py +421 -0
- package/src/zexus/blockchain/storage.py +423 -0
- package/src/zexus/blockchain/tokens.py +750 -0
- package/src/zexus/blockchain/upgradeable.py +1004 -0
- package/src/zexus/blockchain/verification.py +1602 -0
- package/src/zexus/blockchain/wallet.py +621 -0
- package/src/zexus/cli/__pycache__/main.cpython-312.pyc +0 -0
- package/src/zexus/cli/main.py +300 -20
- package/src/zexus/cli/zpm.py +1 -1
- package/src/zexus/compiler/__pycache__/bytecode.cpython-312.pyc +0 -0
- package/src/zexus/compiler/__pycache__/lexer.cpython-312.pyc +0 -0
- package/src/zexus/compiler/__pycache__/parser.cpython-312.pyc +0 -0
- package/src/zexus/compiler/__pycache__/semantic.cpython-312.pyc +0 -0
- package/src/zexus/compiler/__pycache__/zexus_ast.cpython-312.pyc +0 -0
- package/src/zexus/compiler/lexer.py +10 -5
- package/src/zexus/concurrency_system.py +79 -0
- package/src/zexus/config.py +54 -0
- package/src/zexus/crypto_bridge.py +244 -8
- package/src/zexus/dap/__init__.py +10 -0
- package/src/zexus/dap/__main__.py +4 -0
- package/src/zexus/dap/dap_server.py +391 -0
- package/src/zexus/dap/debug_engine.py +298 -0
- package/src/zexus/environment.py +10 -1
- package/src/zexus/evaluator/__pycache__/bytecode_compiler.cpython-312.pyc +0 -0
- package/src/zexus/evaluator/__pycache__/core.cpython-312.pyc +0 -0
- package/src/zexus/evaluator/__pycache__/expressions.cpython-312.pyc +0 -0
- package/src/zexus/evaluator/__pycache__/functions.cpython-312.pyc +0 -0
- package/src/zexus/evaluator/__pycache__/resource_limiter.cpython-312.pyc +0 -0
- package/src/zexus/evaluator/__pycache__/statements.cpython-312.pyc +0 -0
- package/src/zexus/evaluator/__pycache__/unified_execution.cpython-312.pyc +0 -0
- package/src/zexus/evaluator/__pycache__/utils.cpython-312.pyc +0 -0
- package/src/zexus/evaluator/bytecode_compiler.py +441 -37
- package/src/zexus/evaluator/core.py +560 -49
- package/src/zexus/evaluator/expressions.py +122 -49
- package/src/zexus/evaluator/functions.py +417 -16
- package/src/zexus/evaluator/statements.py +521 -118
- package/src/zexus/evaluator/unified_execution.py +573 -72
- package/src/zexus/evaluator/utils.py +14 -2
- package/src/zexus/event_loop.py +186 -0
- package/src/zexus/lexer.py +742 -486
- package/src/zexus/lsp/__init__.py +1 -1
- package/src/zexus/lsp/definition_provider.py +163 -9
- package/src/zexus/lsp/server.py +22 -8
- package/src/zexus/lsp/symbol_provider.py +182 -9
- package/src/zexus/module_cache.py +237 -9
- package/src/zexus/object.py +64 -6
- package/src/zexus/parser/__pycache__/parser.cpython-312.pyc +0 -0
- package/src/zexus/parser/__pycache__/strategy_context.cpython-312.pyc +0 -0
- package/src/zexus/parser/__pycache__/strategy_structural.cpython-312.pyc +0 -0
- package/src/zexus/parser/parser.py +786 -285
- package/src/zexus/parser/strategy_context.py +407 -66
- package/src/zexus/parser/strategy_structural.py +117 -19
- package/src/zexus/persistence.py +15 -1
- package/src/zexus/renderer/__init__.py +15 -0
- package/src/zexus/renderer/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/zexus/renderer/__pycache__/backend.cpython-312.pyc +0 -0
- package/src/zexus/renderer/__pycache__/canvas.cpython-312.pyc +0 -0
- package/src/zexus/renderer/__pycache__/color_system.cpython-312.pyc +0 -0
- package/src/zexus/renderer/__pycache__/layout.cpython-312.pyc +0 -0
- package/src/zexus/renderer/__pycache__/main_renderer.cpython-312.pyc +0 -0
- package/src/zexus/renderer/__pycache__/painter.cpython-312.pyc +0 -0
- package/src/zexus/renderer/tk_backend.py +208 -0
- package/src/zexus/renderer/web_backend.py +260 -0
- package/src/zexus/runtime/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/zexus/runtime/__pycache__/async_runtime.cpython-312.pyc +0 -0
- package/src/zexus/runtime/__pycache__/load_manager.cpython-312.pyc +0 -0
- package/src/zexus/runtime/file_flags.py +137 -0
- package/src/zexus/safety/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/zexus/safety/__pycache__/memory_safety.cpython-312.pyc +0 -0
- package/src/zexus/security.py +424 -34
- package/src/zexus/stdlib/fs.py +23 -18
- package/src/zexus/stdlib/http.py +289 -186
- package/src/zexus/stdlib/sockets.py +207 -163
- package/src/zexus/stdlib/websockets.py +282 -0
- package/src/zexus/stdlib_integration.py +369 -2
- package/src/zexus/strategy_recovery.py +6 -3
- package/src/zexus/type_checker.py +423 -0
- package/src/zexus/virtual_filesystem.py +189 -2
- package/src/zexus/vm/__init__.py +113 -3
- package/src/zexus/vm/__pycache__/async_optimizer.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/bytecode.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/bytecode_converter.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/cache.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/compiler.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/gas_metering.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/jit.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/parallel_vm.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/vm.cpython-312.pyc +0 -0
- package/src/zexus/vm/async_optimizer.py +14 -1
- package/src/zexus/vm/binary_bytecode.py +659 -0
- package/src/zexus/vm/bytecode.py +28 -1
- package/src/zexus/vm/bytecode_converter.py +26 -12
- package/src/zexus/vm/cabi.c +1985 -0
- package/src/zexus/vm/cabi.cpython-312-x86_64-linux-gnu.so +0 -0
- package/src/zexus/vm/cabi.h +127 -0
- package/src/zexus/vm/cache.py +557 -17
- package/src/zexus/vm/compiler.py +703 -5
- package/src/zexus/vm/fastops.c +15743 -0
- package/src/zexus/vm/fastops.cpython-312-x86_64-linux-gnu.so +0 -0
- package/src/zexus/vm/fastops.pyx +288 -0
- package/src/zexus/vm/gas_metering.py +50 -9
- package/src/zexus/vm/jit.py +83 -2
- package/src/zexus/vm/native_jit_backend.py +1816 -0
- package/src/zexus/vm/native_runtime.cpp +1388 -0
- package/src/zexus/vm/native_runtime.cpython-312-x86_64-linux-gnu.so +0 -0
- package/src/zexus/vm/optimizer.py +161 -11
- package/src/zexus/vm/parallel_vm.py +118 -42
- package/src/zexus/vm/peephole_optimizer.py +82 -4
- package/src/zexus/vm/profiler.py +38 -18
- package/src/zexus/vm/register_allocator.py +16 -5
- package/src/zexus/vm/register_vm.py +8 -5
- package/src/zexus/vm/vm.py +3411 -573
- package/src/zexus/vm/wasm_compiler.py +658 -0
- package/src/zexus/zexus_ast.py +63 -11
- package/src/zexus/zexus_token.py +13 -5
- package/src/zexus/zpm/installer.py +55 -15
- package/src/zexus/zpm/package_manager.py +1 -1
- package/src/zexus/zpm/registry.py +257 -28
- package/src/zexus.egg-info/PKG-INFO +7 -4
- package/src/zexus.egg-info/SOURCES.txt +116 -9
- package/src/zexus.egg-info/entry_points.txt +1 -0
- package/src/zexus.egg-info/requires.txt +4 -0
|
@@ -11,16 +11,85 @@ from ..object import (
|
|
|
11
11
|
Integer, Float, String, List, Map,
|
|
12
12
|
EvaluationError, Builtin, DateTime
|
|
13
13
|
)
|
|
14
|
+
from ..config import config as zexus_config
|
|
14
15
|
from .utils import is_error, debug_log, NULL, TRUE, FALSE, is_truthy, _python_to_zexus
|
|
15
16
|
|
|
16
17
|
class ExpressionEvaluatorMixin:
|
|
17
18
|
"""Handles evaluation of expressions: Literals, Math, Logic, Identifiers."""
|
|
18
19
|
|
|
20
|
+
def eval_property_access_expression(self, node, env, stack_trace=None):
|
|
21
|
+
obj = self.eval_node(node.object, env, stack_trace)
|
|
22
|
+
if is_error(obj): return obj
|
|
23
|
+
|
|
24
|
+
from ..object import String, Map, List, EvaluationError
|
|
25
|
+
|
|
26
|
+
if node.computed:
|
|
27
|
+
# Index notation: obj[expr]
|
|
28
|
+
idx = self.eval_node(node.property, env, stack_trace)
|
|
29
|
+
if is_error(idx): return idx
|
|
30
|
+
|
|
31
|
+
if isinstance(obj, List):
|
|
32
|
+
return obj.get(idx)
|
|
33
|
+
elif isinstance(obj, Map):
|
|
34
|
+
key = idx
|
|
35
|
+
if isinstance(key, str): key = String(key)
|
|
36
|
+
return obj.get(key) or NULL
|
|
37
|
+
elif isinstance(obj, String):
|
|
38
|
+
return obj.get(idx)
|
|
39
|
+
else:
|
|
40
|
+
# Fallback for Python objects
|
|
41
|
+
try:
|
|
42
|
+
raw_idx = idx.value if hasattr(idx, 'value') else idx
|
|
43
|
+
return obj[raw_idx]
|
|
44
|
+
except (IndexError, KeyError, TypeError):
|
|
45
|
+
return NULL
|
|
46
|
+
else:
|
|
47
|
+
# Dot notation: obj.prop
|
|
48
|
+
if not hasattr(node.property, 'value'):
|
|
49
|
+
return EvaluationError(f"Invalid property identifier: {node.property}")
|
|
50
|
+
|
|
51
|
+
prop_name = str(node.property.value)
|
|
52
|
+
|
|
53
|
+
# Check security restrictions (redact, read-only, etc.)
|
|
54
|
+
try:
|
|
55
|
+
from ..security import get_security_context
|
|
56
|
+
ctx = get_security_context()
|
|
57
|
+
target = f"{getattr(node.object, 'value', str(node.object))}.{prop_name}"
|
|
58
|
+
restriction = ctx.get_restriction(target)
|
|
59
|
+
if restriction:
|
|
60
|
+
rule = restriction.get('restriction')
|
|
61
|
+
if rule == 'redact':
|
|
62
|
+
return String('***REDACTED***')
|
|
63
|
+
except Exception:
|
|
64
|
+
pass
|
|
65
|
+
|
|
66
|
+
if isinstance(obj, Map):
|
|
67
|
+
return obj.get(String(prop_name)) or NULL
|
|
68
|
+
elif isinstance(obj, dict):
|
|
69
|
+
return obj.get(prop_name, NULL)
|
|
70
|
+
elif hasattr(obj, 'get') and hasattr(obj, 'set') and callable(getattr(obj, 'get', None)):
|
|
71
|
+
# Contract-like objects (e.g., SmartContract) expose state via get/set.
|
|
72
|
+
# This makes `contract.state_var` reliable and deterministic.
|
|
73
|
+
try:
|
|
74
|
+
res = obj.get(prop_name)
|
|
75
|
+
return res if res is not None else NULL
|
|
76
|
+
except Exception:
|
|
77
|
+
return NULL
|
|
78
|
+
else:
|
|
79
|
+
# Try getattr (for data classes, etc.)
|
|
80
|
+
try:
|
|
81
|
+
res = getattr(obj, prop_name, NULL)
|
|
82
|
+
return res
|
|
83
|
+
except Exception:
|
|
84
|
+
return NULL
|
|
85
|
+
|
|
19
86
|
def eval_identifier(self, node, env):
|
|
20
|
-
|
|
87
|
+
name = node.value
|
|
88
|
+
if zexus_config.fast_debug_enabled:
|
|
89
|
+
debug_log("eval_identifier", f"Looking up: {name}")
|
|
21
90
|
|
|
22
91
|
# Special case: 'this' keyword should be treated like ThisExpression
|
|
23
|
-
if
|
|
92
|
+
if name == "this":
|
|
24
93
|
# Look for contract instance first
|
|
25
94
|
contract_instance = env.get("__contract_instance__")
|
|
26
95
|
if contract_instance is not None:
|
|
@@ -32,21 +101,23 @@ class ExpressionEvaluatorMixin:
|
|
|
32
101
|
return data_instance
|
|
33
102
|
|
|
34
103
|
# First, check environment for user-defined variables (including DATA dataclasses)
|
|
35
|
-
val = env.get(
|
|
104
|
+
val = env.get(name)
|
|
36
105
|
if val:
|
|
37
|
-
|
|
106
|
+
if zexus_config.fast_debug_enabled:
|
|
107
|
+
debug_log(" Found in environment", f"{name} = {val}")
|
|
38
108
|
return val
|
|
39
109
|
|
|
40
110
|
# Check builtins (self.builtins should be defined in FunctionEvaluatorMixin)
|
|
41
111
|
if hasattr(self, 'builtins'):
|
|
42
|
-
builtin = self.builtins.get(
|
|
112
|
+
builtin = self.builtins.get(name)
|
|
43
113
|
if builtin:
|
|
44
|
-
|
|
114
|
+
if zexus_config.fast_debug_enabled:
|
|
115
|
+
debug_log(" Found builtin", f"{name} = {builtin}")
|
|
45
116
|
return builtin
|
|
46
117
|
|
|
47
118
|
# Special handling for TX - ONLY if not already defined by user
|
|
48
119
|
# This provides blockchain transaction context when TX is not a user dataclass
|
|
49
|
-
if
|
|
120
|
+
if name == "TX":
|
|
50
121
|
from ..blockchain.transaction import get_current_tx, create_tx_context
|
|
51
122
|
tx = get_current_tx()
|
|
52
123
|
if tx is None:
|
|
@@ -156,6 +227,12 @@ class ExpressionEvaluatorMixin:
|
|
|
156
227
|
suggestion="Check your divisor value. Modulo operation requires a non-zero divisor."
|
|
157
228
|
)
|
|
158
229
|
return Integer(left_val % right_val)
|
|
230
|
+
elif operator == "**":
|
|
231
|
+
if right_val < 0:
|
|
232
|
+
# Negative exponent returns float
|
|
233
|
+
return Float(left_val ** right_val)
|
|
234
|
+
result = left_val ** right_val
|
|
235
|
+
return check_overflow(result, "exponentiation")
|
|
159
236
|
elif operator == "<":
|
|
160
237
|
return TRUE if left_val < right_val else FALSE
|
|
161
238
|
elif operator == ">":
|
|
@@ -197,6 +274,11 @@ class ExpressionEvaluatorMixin:
|
|
|
197
274
|
return TRUE if left_val == right_val else FALSE
|
|
198
275
|
elif operator == "!=":
|
|
199
276
|
return TRUE if left_val != right_val else FALSE
|
|
277
|
+
elif operator == "**":
|
|
278
|
+
try:
|
|
279
|
+
return Float(left_val ** right_val)
|
|
280
|
+
except (OverflowError, ValueError) as e:
|
|
281
|
+
return EvaluationError(f"Exponentiation error: {e}")
|
|
200
282
|
|
|
201
283
|
return EvaluationError(f"Unknown float operator: {operator}")
|
|
202
284
|
|
|
@@ -363,7 +445,7 @@ class ExpressionEvaluatorMixin:
|
|
|
363
445
|
|
|
364
446
|
# SECURITY FIX #8: Strict Type Checking for Arithmetic
|
|
365
447
|
# All arithmetic operations require numeric types (Integer or Float)
|
|
366
|
-
elif operator in ("*", "-", "/", "%"):
|
|
448
|
+
elif operator in ("*", "-", "/", "%", "**"):
|
|
367
449
|
# Get type names for error messages
|
|
368
450
|
left_type = type(left).__name__.replace("Obj", "").upper()
|
|
369
451
|
right_type = type(right).__name__.replace("Obj", "").upper()
|
|
@@ -400,9 +482,11 @@ class ExpressionEvaluatorMixin:
|
|
|
400
482
|
if r_val == 0:
|
|
401
483
|
return EvaluationError("Modulo by zero")
|
|
402
484
|
result = l_val % r_val
|
|
485
|
+
elif operator == "**":
|
|
486
|
+
result = l_val ** r_val
|
|
403
487
|
|
|
404
488
|
# Return Integer if result is whole number, Float otherwise
|
|
405
|
-
if result == int(result) and operator
|
|
489
|
+
if result == int(result) and operator not in ("/", "**"): # Division/power always returns float
|
|
406
490
|
return Integer(int(result))
|
|
407
491
|
return Float(result)
|
|
408
492
|
except Exception as e:
|
|
@@ -413,6 +497,10 @@ class ExpressionEvaluatorMixin:
|
|
|
413
497
|
|
|
414
498
|
# Comparison with mixed numeric types (Integer/Float comparison allowed)
|
|
415
499
|
elif operator in ("<", ">", "<=", ">="):
|
|
500
|
+
# Safe null handling: Any comparison with NULL is False (except != handled above)
|
|
501
|
+
if left == NULL or right == NULL:
|
|
502
|
+
return FALSE
|
|
503
|
+
|
|
416
504
|
if isinstance(left, (Integer, Float)) and isinstance(right, (Integer, Float)):
|
|
417
505
|
l_val = float(left.value)
|
|
418
506
|
r_val = float(right.value)
|
|
@@ -443,12 +531,11 @@ class ExpressionEvaluatorMixin:
|
|
|
443
531
|
|
|
444
532
|
if operator == "!":
|
|
445
533
|
# !true = false, !false = true, !null = true, !anything_else = false
|
|
446
|
-
|
|
534
|
+
# Use is_truthy for robust comparison (handles non-singleton BooleanObj)
|
|
535
|
+
if is_truthy(right):
|
|
447
536
|
return FALSE
|
|
448
|
-
elif right == FALSE or right == NULL:
|
|
449
|
-
return TRUE
|
|
450
537
|
else:
|
|
451
|
-
return
|
|
538
|
+
return TRUE
|
|
452
539
|
elif operator == "-":
|
|
453
540
|
if isinstance(right, Integer):
|
|
454
541
|
return Integer(-right.value)
|
|
@@ -1032,42 +1119,38 @@ class ExpressionEvaluatorMixin:
|
|
|
1032
1119
|
def eval_async_expression(self, node, env, stack_trace):
|
|
1033
1120
|
"""Evaluate async expression: async <expression>
|
|
1034
1121
|
|
|
1035
|
-
|
|
1036
|
-
|
|
1122
|
+
Schedules the expression on the shared Zexus event loop.
|
|
1123
|
+
For call expressions, evaluation is deferred entirely to the loop.
|
|
1124
|
+
For coroutine results, driving is done inside a loop task.
|
|
1037
1125
|
"""
|
|
1038
|
-
import
|
|
1126
|
+
from ..event_loop import spawn as _spawn
|
|
1127
|
+
import asyncio
|
|
1039
1128
|
import sys
|
|
1040
|
-
|
|
1041
|
-
# For call expressions, we need to defer evaluation to the
|
|
1042
|
-
# Otherwise evaluating here will execute the action in the main thread
|
|
1129
|
+
|
|
1130
|
+
# For call expressions, we need to defer evaluation to the event loop
|
|
1043
1131
|
if type(node.expression).__name__ == 'CallExpression':
|
|
1044
|
-
def
|
|
1132
|
+
async def _run_call():
|
|
1045
1133
|
try:
|
|
1046
1134
|
result = self.eval_node(node.expression, env, stack_trace)
|
|
1047
|
-
|
|
1135
|
+
|
|
1048
1136
|
# If it's a Coroutine (from async action), execute it
|
|
1049
1137
|
if hasattr(result, '__class__') and result.__class__.__name__ == 'Coroutine':
|
|
1050
1138
|
try:
|
|
1051
|
-
# Prime the generator
|
|
1052
1139
|
next(result.generator)
|
|
1053
|
-
# Execute until completion
|
|
1054
1140
|
while True:
|
|
1055
1141
|
next(result.generator)
|
|
1056
1142
|
except StopIteration:
|
|
1057
|
-
pass
|
|
1058
|
-
|
|
1143
|
+
pass
|
|
1059
1144
|
except StopIteration:
|
|
1060
|
-
pass
|
|
1145
|
+
pass
|
|
1061
1146
|
except Exception as e:
|
|
1062
|
-
import sys
|
|
1063
1147
|
print(f"[ASYNC ERROR] {type(e).__name__}: {str(e)}", file=sys.stderr, flush=True)
|
|
1064
1148
|
import traceback
|
|
1065
1149
|
traceback.print_exc(file=sys.stderr)
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
thread.start()
|
|
1150
|
+
|
|
1151
|
+
_spawn(_run_call())
|
|
1069
1152
|
return NULL
|
|
1070
|
-
|
|
1153
|
+
|
|
1071
1154
|
# For other expressions, evaluate first then check if it's a Coroutine
|
|
1072
1155
|
previous_allow = getattr(self, "_allow_coroutine_result", False)
|
|
1073
1156
|
self._allow_coroutine_result = True
|
|
@@ -1075,35 +1158,25 @@ class ExpressionEvaluatorMixin:
|
|
|
1075
1158
|
result = self.eval_node(node.expression, env, stack_trace)
|
|
1076
1159
|
finally:
|
|
1077
1160
|
self._allow_coroutine_result = previous_allow
|
|
1078
|
-
|
|
1161
|
+
|
|
1079
1162
|
if is_error(result):
|
|
1080
1163
|
return result
|
|
1081
|
-
|
|
1082
|
-
#
|
|
1083
|
-
|
|
1084
|
-
# If it's a Coroutine (from calling an async action), execute it in a thread
|
|
1164
|
+
|
|
1165
|
+
# If it's a Coroutine, drive it on the shared event loop
|
|
1085
1166
|
if hasattr(result, '__class__') and result.__class__.__name__ == 'Coroutine':
|
|
1086
|
-
def
|
|
1167
|
+
async def _drive_coroutine():
|
|
1087
1168
|
try:
|
|
1088
|
-
# Prime the generator
|
|
1089
1169
|
next(result.generator)
|
|
1090
|
-
# Execute until completion
|
|
1091
1170
|
while True:
|
|
1092
1171
|
next(result.generator)
|
|
1093
1172
|
except StopIteration:
|
|
1094
|
-
pass
|
|
1173
|
+
pass
|
|
1095
1174
|
except Exception as e:
|
|
1096
|
-
import sys
|
|
1097
1175
|
print(f"[ASYNC ERROR] {type(e).__name__}: {str(e)}", file=sys.stderr, flush=True)
|
|
1098
1176
|
import traceback
|
|
1099
1177
|
traceback.print_exc(file=sys.stderr)
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
thread.start()
|
|
1178
|
+
|
|
1179
|
+
_spawn(_drive_coroutine())
|
|
1103
1180
|
return NULL
|
|
1104
|
-
|
|
1105
|
-
# For any other result (including NULL from regular actions),
|
|
1106
|
-
# we can't execute it asynchronously since it already executed.
|
|
1107
|
-
# Just return NULL to indicate "async operation initiated"
|
|
1108
|
-
# print(f"[ASYNC EXPR] Result is not a coroutine, returning NULL", file=sys.stderr)
|
|
1181
|
+
|
|
1109
1182
|
return NULL
|