zexus 1.8.1 → 1.8.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/bin/zexus +12 -2
- package/bin/zpics +12 -2
- package/bin/zpm +12 -2
- package/bin/zx +12 -2
- package/bin/zx-deploy +12 -2
- package/bin/zx-dev +12 -2
- package/bin/zx-run +12 -2
- package/package.json +1 -1
- package/scripts/postinstall.js +192 -41
- package/src/zexus/__init__.py +1 -1
- package/src/zexus/cli/main.py +1 -1
- package/src/zexus/cli/zpm.py +1 -1
- package/src/zexus/parser/parser.py +34 -9
- package/src/zexus/parser/strategy_context.py +91 -2
- package/src/zexus/vm/compiler.py +469 -48
- package/src/zexus/vm/vm.py +249 -32
- package/src/zexus/zpm/package_manager.py +1 -1
- package/src/zexus.egg-info/PKG-INFO +1 -1
- package/src/zexus.egg-info/SOURCES.txt +9 -0
- package/src/zexus.egg-info/entry_points.txt +8 -0
package/src/zexus/vm/vm.py
CHANGED
|
@@ -31,6 +31,7 @@ from ..object import (
|
|
|
31
31
|
Map as ZMap,
|
|
32
32
|
Null as ZNull,
|
|
33
33
|
EvaluationError as ZEvaluationError,
|
|
34
|
+
DateTime as ZDateTime,
|
|
34
35
|
)
|
|
35
36
|
|
|
36
37
|
# ==================== Backend / Optional Imports ====================
|
|
@@ -782,6 +783,9 @@ class VM:
|
|
|
782
783
|
vm.enable_fast_loop = getattr(parent_vm, "enable_fast_loop", False)
|
|
783
784
|
vm.fast_loop_threshold = getattr(parent_vm, "fast_loop_threshold", 512)
|
|
784
785
|
vm._fast_loop_stats = {"used": False, "reason": ""}
|
|
786
|
+
# SECURITY (H9): Call-depth tracking for child VMs
|
|
787
|
+
vm._call_depth = 0
|
|
788
|
+
vm._MAX_CALL_DEPTH = getattr(parent_vm, "_MAX_CALL_DEPTH", 256)
|
|
785
789
|
|
|
786
790
|
# Settings
|
|
787
791
|
vm.worker_count = parent_vm.worker_count
|
|
@@ -921,6 +925,82 @@ class VM:
|
|
|
921
925
|
self.builtins["__vm_use_module__"] = self._vm_use_module
|
|
922
926
|
if "__vm_from_module__" not in self.builtins:
|
|
923
927
|
self.builtins["__vm_from_module__"] = self._vm_from_module
|
|
928
|
+
# Channel builtins for VM concurrency support
|
|
929
|
+
if "__create_channel__" not in self.builtins:
|
|
930
|
+
self.builtins["__create_channel__"] = self._vm_create_channel
|
|
931
|
+
if "send" not in self.builtins:
|
|
932
|
+
self.builtins["send"] = self._vm_send
|
|
933
|
+
if "receive" not in self.builtins:
|
|
934
|
+
self.builtins["receive"] = self._vm_receive
|
|
935
|
+
if "close_channel" not in self.builtins:
|
|
936
|
+
self.builtins["close_channel"] = self._vm_close_channel
|
|
937
|
+
|
|
938
|
+
# --- Channel builtins for VM ---
|
|
939
|
+
def _vm_create_channel(self, name, element_type=None, capacity=0):
|
|
940
|
+
"""Create a channel and store it in the VM environment."""
|
|
941
|
+
try:
|
|
942
|
+
from ..concurrency_system import Channel
|
|
943
|
+
except ImportError:
|
|
944
|
+
# Fallback: use a simple queue-based channel
|
|
945
|
+
import queue as _queue_mod
|
|
946
|
+
|
|
947
|
+
class _SimpleChannel:
|
|
948
|
+
def __init__(self, name, capacity=0):
|
|
949
|
+
self.name = name
|
|
950
|
+
self._queue = _queue_mod.Queue(maxsize=capacity if capacity else 0)
|
|
951
|
+
self._closed = False
|
|
952
|
+
|
|
953
|
+
def send(self, value, timeout=5.0):
|
|
954
|
+
if self._closed:
|
|
955
|
+
raise RuntimeError(f"Channel '{self.name}' is closed")
|
|
956
|
+
self._queue.put(value, timeout=timeout)
|
|
957
|
+
|
|
958
|
+
def receive(self, timeout=5.0):
|
|
959
|
+
if self._closed and self._queue.empty():
|
|
960
|
+
return None
|
|
961
|
+
try:
|
|
962
|
+
return self._queue.get(timeout=timeout)
|
|
963
|
+
except _queue_mod.Empty:
|
|
964
|
+
return None
|
|
965
|
+
|
|
966
|
+
def close(self):
|
|
967
|
+
self._closed = True
|
|
968
|
+
|
|
969
|
+
Channel = _SimpleChannel
|
|
970
|
+
|
|
971
|
+
cap = int(capacity) if capacity else 0
|
|
972
|
+
ch = Channel(name=name, capacity=cap) if element_type is None else Channel(name=name, element_type=element_type, capacity=cap)
|
|
973
|
+
self.env[name] = ch
|
|
974
|
+
return ch
|
|
975
|
+
|
|
976
|
+
def _vm_send(self, channel, value):
|
|
977
|
+
"""Send value to channel."""
|
|
978
|
+
if channel is None or not hasattr(channel, 'send'):
|
|
979
|
+
return None
|
|
980
|
+
try:
|
|
981
|
+
channel.send(value, timeout=5.0)
|
|
982
|
+
except Exception:
|
|
983
|
+
pass
|
|
984
|
+
return None
|
|
985
|
+
|
|
986
|
+
def _vm_receive(self, channel):
|
|
987
|
+
"""Receive value from channel."""
|
|
988
|
+
if channel is None or not hasattr(channel, 'receive'):
|
|
989
|
+
return None
|
|
990
|
+
try:
|
|
991
|
+
return channel.receive(timeout=5.0)
|
|
992
|
+
except Exception:
|
|
993
|
+
return None
|
|
994
|
+
|
|
995
|
+
def _vm_close_channel(self, channel):
|
|
996
|
+
"""Close a channel."""
|
|
997
|
+
if channel is None or not hasattr(channel, 'close'):
|
|
998
|
+
return None
|
|
999
|
+
try:
|
|
1000
|
+
channel.close()
|
|
1001
|
+
except Exception:
|
|
1002
|
+
pass
|
|
1003
|
+
return None
|
|
924
1004
|
|
|
925
1005
|
def _vm_use_module(self, spec):
|
|
926
1006
|
if spec is None:
|
|
@@ -2027,15 +2107,32 @@ class VM:
|
|
|
2027
2107
|
a = stack_pop() if stack else 0
|
|
2028
2108
|
if hasattr(a, 'value'): a = a.value
|
|
2029
2109
|
if hasattr(b, 'value'): b = b.value
|
|
2030
|
-
|
|
2110
|
+
# Type coercion: if either operand is a string, convert both to string
|
|
2111
|
+
if isinstance(a, str) or isinstance(b, str):
|
|
2112
|
+
stack_append(str(a if a is not None else '') + str(b if b is not None else ''))
|
|
2113
|
+
else:
|
|
2114
|
+
if a is None: a = 0
|
|
2115
|
+
if b is None: b = 0
|
|
2116
|
+
stack_append(a + b)
|
|
2031
2117
|
elif op_name == "SUB":
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
if
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2118
|
+
b_raw = stack_pop() if stack else 0
|
|
2119
|
+
a_raw = stack_pop() if stack else 0
|
|
2120
|
+
# DateTime arithmetic
|
|
2121
|
+
if isinstance(a_raw, ZDateTime) and isinstance(b_raw, ZDateTime):
|
|
2122
|
+
stack_append(a_raw.timestamp - b_raw.timestamp)
|
|
2123
|
+
elif isinstance(a_raw, ZDateTime):
|
|
2124
|
+
b = b_raw.value if hasattr(b_raw, 'value') else b_raw
|
|
2125
|
+
if b is None: b = 0
|
|
2126
|
+
stack_append(ZDateTime(a_raw.timestamp - float(b)))
|
|
2127
|
+
else:
|
|
2128
|
+
a = a_raw.value if hasattr(a_raw, 'value') else a_raw
|
|
2129
|
+
b = b_raw.value if hasattr(b_raw, 'value') else b_raw
|
|
2130
|
+
if a is None: a = 0
|
|
2131
|
+
if b is None: b = 0
|
|
2132
|
+
try:
|
|
2133
|
+
stack_append(a - b)
|
|
2134
|
+
except TypeError:
|
|
2135
|
+
stack_append(0)
|
|
2039
2136
|
elif op_name == "MUL":
|
|
2040
2137
|
b = stack_pop() if stack else 0
|
|
2041
2138
|
a = stack_pop() if stack else 0
|
|
@@ -2578,6 +2675,48 @@ class VM:
|
|
|
2578
2675
|
if caller and caller not in allowed:
|
|
2579
2676
|
raise ZEvaluationError(f"Access denied: '{r_key}' restricted to allowed addresses")
|
|
2580
2677
|
|
|
2678
|
+
elif op_name == "SPAWN":
|
|
2679
|
+
# Sync SPAWN: schedule the function to run in a background thread
|
|
2680
|
+
import threading
|
|
2681
|
+
val = stack_pop() if stack else None
|
|
2682
|
+
if callable(val):
|
|
2683
|
+
# val is a callable — run it in a daemon thread
|
|
2684
|
+
t = threading.Thread(target=val, daemon=True)
|
|
2685
|
+
t.start()
|
|
2686
|
+
stack_append(t)
|
|
2687
|
+
elif isinstance(val, dict) and "bytecode" in val:
|
|
2688
|
+
# val is a function descriptor — execute its bytecode in a thread
|
|
2689
|
+
func_desc = val
|
|
2690
|
+
def _run_func_desc(fd=func_desc):
|
|
2691
|
+
try:
|
|
2692
|
+
child_vm = VM.create_child(parent_vm=self, env=dict(env))
|
|
2693
|
+
child_vm._run_stack_bytecode_sync(fd["bytecode"], debug=False)
|
|
2694
|
+
self._return_vm_to_pool(child_vm)
|
|
2695
|
+
except Exception:
|
|
2696
|
+
pass
|
|
2697
|
+
t = threading.Thread(target=_run_func_desc, daemon=True)
|
|
2698
|
+
t.start()
|
|
2699
|
+
stack_append(t)
|
|
2700
|
+
else:
|
|
2701
|
+
# Already a result (not spawnable) — just push it back
|
|
2702
|
+
stack_append(val)
|
|
2703
|
+
|
|
2704
|
+
elif op_name == "AWAIT":
|
|
2705
|
+
import threading
|
|
2706
|
+
val = stack_pop() if stack else None
|
|
2707
|
+
if isinstance(val, threading.Thread):
|
|
2708
|
+
val.join(timeout=30)
|
|
2709
|
+
stack_append(None)
|
|
2710
|
+
elif isinstance(val, str) and val in self._tasks:
|
|
2711
|
+
# asyncio task — run in event loop
|
|
2712
|
+
try:
|
|
2713
|
+
result = self._run_coroutine_sync(self._tasks[val])
|
|
2714
|
+
stack_append(result)
|
|
2715
|
+
except Exception:
|
|
2716
|
+
stack_append(None)
|
|
2717
|
+
else:
|
|
2718
|
+
stack_append(val)
|
|
2719
|
+
|
|
2581
2720
|
else:
|
|
2582
2721
|
# Truly unknown op — fallback to async path
|
|
2583
2722
|
return self._run_coroutine_sync(self._run_stack_bytecode(bytecode, debug))
|
|
@@ -2693,16 +2832,18 @@ class VM:
|
|
|
2693
2832
|
if fn is None:
|
|
2694
2833
|
return None
|
|
2695
2834
|
# SECURITY (H9): Enforce call-depth limit
|
|
2696
|
-
|
|
2835
|
+
call_depth = getattr(self, '_call_depth', 0)
|
|
2836
|
+
max_depth = getattr(self, '_MAX_CALL_DEPTH', 256)
|
|
2837
|
+
if call_depth >= max_depth:
|
|
2697
2838
|
raise RecursionError(
|
|
2698
|
-
f"VM call depth exceeded maximum ({
|
|
2839
|
+
f"VM call depth exceeded maximum ({max_depth}). "
|
|
2699
2840
|
"Possible infinite recursion."
|
|
2700
2841
|
)
|
|
2701
|
-
self._call_depth
|
|
2842
|
+
self._call_depth = call_depth + 1
|
|
2702
2843
|
try:
|
|
2703
2844
|
return self._invoke_callable_sync_inner(fn, args)
|
|
2704
2845
|
finally:
|
|
2705
|
-
self._call_depth
|
|
2846
|
+
self._call_depth = getattr(self, '_call_depth', 1) - 1
|
|
2706
2847
|
|
|
2707
2848
|
def _invoke_callable_sync_inner(self, fn, args):
|
|
2708
2849
|
"""Inner implementation of callable invocation."""
|
|
@@ -3074,22 +3215,57 @@ class VM:
|
|
|
3074
3215
|
|
|
3075
3216
|
def _binary_op(func):
|
|
3076
3217
|
def wrapper(_):
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3218
|
+
b_raw = stack.pop() if stack else 0
|
|
3219
|
+
a_raw = stack.pop() if stack else 0
|
|
3220
|
+
# Keep raw objects for DateTime-aware arithmetic
|
|
3221
|
+
a = _unwrap(a_raw)
|
|
3222
|
+
b = _unwrap(b_raw)
|
|
3081
3223
|
if isinstance(a, ZEvaluationError):
|
|
3082
3224
|
stack.append(a)
|
|
3083
3225
|
return
|
|
3084
3226
|
if isinstance(b, ZEvaluationError):
|
|
3085
3227
|
stack.append(b)
|
|
3086
3228
|
return
|
|
3229
|
+
# DateTime arithmetic
|
|
3230
|
+
if isinstance(a_raw, ZDateTime) or isinstance(b_raw, ZDateTime):
|
|
3231
|
+
a_dt = a_raw if isinstance(a_raw, ZDateTime) else None
|
|
3232
|
+
b_dt = b_raw if isinstance(b_raw, ZDateTime) else None
|
|
3233
|
+
if a_dt and b_dt:
|
|
3234
|
+
# DateTime - DateTime = seconds difference
|
|
3235
|
+
stack.append(a_dt.timestamp - b_dt.timestamp)
|
|
3236
|
+
elif a_dt:
|
|
3237
|
+
# DateTime +/- number
|
|
3238
|
+
val = float(b) if b is not None else 0.0
|
|
3239
|
+
try:
|
|
3240
|
+
result = func(a_dt.timestamp, val)
|
|
3241
|
+
stack.append(ZDateTime(result))
|
|
3242
|
+
except Exception:
|
|
3243
|
+
stack.append(0)
|
|
3244
|
+
else:
|
|
3245
|
+
val = float(a) if a is not None else 0.0
|
|
3246
|
+
try:
|
|
3247
|
+
result = func(val, b_dt.timestamp)
|
|
3248
|
+
stack.append(result)
|
|
3249
|
+
except Exception:
|
|
3250
|
+
stack.append(0)
|
|
3251
|
+
return
|
|
3252
|
+
if a is None: a = 0
|
|
3253
|
+
if b is None: b = 0
|
|
3254
|
+
# Type coercion for string concatenation in ADD
|
|
3255
|
+
if isinstance(a, str) or isinstance(b, str):
|
|
3256
|
+
try:
|
|
3257
|
+
stack.append(func(a, b))
|
|
3258
|
+
except TypeError:
|
|
3259
|
+
stack.append(str(a) + str(b))
|
|
3260
|
+
return
|
|
3087
3261
|
try:
|
|
3088
3262
|
stack.append(func(a, b))
|
|
3089
|
-
except
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3263
|
+
except TypeError:
|
|
3264
|
+
# Graceful fallback — coerce to strings
|
|
3265
|
+
try:
|
|
3266
|
+
stack.append(str(a) + str(b))
|
|
3267
|
+
except Exception:
|
|
3268
|
+
stack.append(0)
|
|
3093
3269
|
return wrapper
|
|
3094
3270
|
|
|
3095
3271
|
def _binary_bool_op(func):
|
|
@@ -3394,19 +3570,41 @@ class VM:
|
|
|
3394
3570
|
stack.append(-a)
|
|
3395
3571
|
|
|
3396
3572
|
def _op_add(_):
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3573
|
+
b_raw = stack_pop() if stack else 0
|
|
3574
|
+
a_raw = stack_pop() if stack else 0
|
|
3575
|
+
# DateTime arithmetic
|
|
3576
|
+
if isinstance(a_raw, ZDateTime) or isinstance(b_raw, ZDateTime):
|
|
3577
|
+
a_dt = a_raw if isinstance(a_raw, ZDateTime) else None
|
|
3578
|
+
b_dt = b_raw if isinstance(b_raw, ZDateTime) else None
|
|
3579
|
+
if a_dt and b_dt:
|
|
3580
|
+
stack_append(a_dt.timestamp + b_dt.timestamp)
|
|
3581
|
+
elif a_dt:
|
|
3582
|
+
b = _unwrap(b_raw)
|
|
3583
|
+
if b is None: b = 0
|
|
3584
|
+
stack_append(ZDateTime(a_dt.timestamp + float(b)))
|
|
3585
|
+
else:
|
|
3586
|
+
a = _unwrap(a_raw)
|
|
3587
|
+
if a is None: a = 0
|
|
3588
|
+
stack_append(ZDateTime(b_raw.timestamp + float(a)))
|
|
3589
|
+
return
|
|
3590
|
+
b = _unwrap(b_raw)
|
|
3591
|
+
a = _unwrap(a_raw)
|
|
3403
3592
|
if isinstance(a, ZEvaluationError):
|
|
3404
3593
|
stack_append(a)
|
|
3405
3594
|
return
|
|
3406
3595
|
if isinstance(b, ZEvaluationError):
|
|
3407
3596
|
stack_append(b)
|
|
3408
3597
|
return
|
|
3409
|
-
|
|
3598
|
+
# Type coercion: if either operand is a string, convert both to string
|
|
3599
|
+
if isinstance(a, str) or isinstance(b, str):
|
|
3600
|
+
stack_append(str(a if a is not None else '') + str(b if b is not None else ''))
|
|
3601
|
+
else:
|
|
3602
|
+
if a is None: a = 0
|
|
3603
|
+
if b is None: b = 0
|
|
3604
|
+
try:
|
|
3605
|
+
stack_append(a + b)
|
|
3606
|
+
except TypeError:
|
|
3607
|
+
stack_append(str(a) + str(b))
|
|
3410
3608
|
|
|
3411
3609
|
def _op_not(_):
|
|
3412
3610
|
a = stack.pop() if stack else False
|
|
@@ -4135,12 +4333,31 @@ class VM:
|
|
|
4135
4333
|
# Auto-unwrap evaluator objects
|
|
4136
4334
|
if hasattr(a, 'value'): a = a.value
|
|
4137
4335
|
if hasattr(b, 'value'): b = b.value
|
|
4138
|
-
|
|
4336
|
+
# Type coercion: if either operand is a string, convert both to string
|
|
4337
|
+
if isinstance(a, str) or isinstance(b, str):
|
|
4338
|
+
stack.append(str(a if a is not None else '') + str(b if b is not None else ''))
|
|
4339
|
+
else:
|
|
4340
|
+
if a is None: a = 0
|
|
4341
|
+
if b is None: b = 0
|
|
4342
|
+
stack.append(a + b)
|
|
4139
4343
|
elif op_name == "SUB":
|
|
4140
|
-
|
|
4141
|
-
|
|
4142
|
-
if
|
|
4143
|
-
|
|
4344
|
+
b_raw = stack.pop() if stack else 0; a_raw = stack.pop() if stack else 0
|
|
4345
|
+
# DateTime arithmetic
|
|
4346
|
+
if isinstance(a_raw, ZDateTime) and isinstance(b_raw, ZDateTime):
|
|
4347
|
+
stack.append(a_raw.timestamp - b_raw.timestamp)
|
|
4348
|
+
elif isinstance(a_raw, ZDateTime):
|
|
4349
|
+
b = b_raw.value if hasattr(b_raw, 'value') else b_raw
|
|
4350
|
+
if b is None: b = 0
|
|
4351
|
+
stack.append(ZDateTime(a_raw.timestamp - float(b)))
|
|
4352
|
+
else:
|
|
4353
|
+
a = a_raw.value if hasattr(a_raw, 'value') else a_raw
|
|
4354
|
+
b = b_raw.value if hasattr(b_raw, 'value') else b_raw
|
|
4355
|
+
if a is None: a = 0
|
|
4356
|
+
if b is None: b = 0
|
|
4357
|
+
try:
|
|
4358
|
+
stack.append(a - b)
|
|
4359
|
+
except TypeError:
|
|
4360
|
+
stack.append(0)
|
|
4144
4361
|
elif op_name == "MUL":
|
|
4145
4362
|
b = stack.pop() if stack else 0; a = stack.pop() if stack else 0
|
|
4146
4363
|
if hasattr(a, 'value'): a = a.value
|
|
@@ -23,7 +23,7 @@ class PackageManager:
|
|
|
23
23
|
self.installer = PackageInstaller(self.zpm_dir)
|
|
24
24
|
self.publisher = PackagePublisher(self.registry)
|
|
25
25
|
|
|
26
|
-
def init(self, name: str = None, version: str = "1.8.
|
|
26
|
+
def init(self, name: str = None, version: str = "1.8.2") -> Dict:
|
|
27
27
|
"""Initialize a new Zexus project with package.json"""
|
|
28
28
|
if self.config_file.exists():
|
|
29
29
|
print(f"⚠️ {self.config_file} already exists")
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
.gitattributes
|
|
2
2
|
.gitignore
|
|
3
|
+
.npmignore
|
|
3
4
|
ADDITIONAL_FIXES_1.6.7.md
|
|
4
5
|
BLOCKCHAIN_MODULE_FULL_SOURCE.md
|
|
5
6
|
CHANGELOG.md
|
|
@@ -165,18 +166,25 @@ bin/zx-dev
|
|
|
165
166
|
bin/zx-run
|
|
166
167
|
blockchain_demo/address_prefix.zx
|
|
167
168
|
blockchain_demo/dex.zx
|
|
169
|
+
blockchain_demo/dex.zxc
|
|
168
170
|
blockchain_demo/governance.zx
|
|
171
|
+
blockchain_demo/governance.zxc
|
|
169
172
|
blockchain_demo/main.zx
|
|
170
173
|
blockchain_demo/multichain_bridge.zx
|
|
171
174
|
blockchain_demo/nft.zx
|
|
175
|
+
blockchain_demo/nft.zxc
|
|
172
176
|
blockchain_demo/staking.zx
|
|
177
|
+
blockchain_demo/staking.zxc
|
|
173
178
|
blockchain_demo/test_import_closure.zx
|
|
174
179
|
blockchain_demo/test_module.zx
|
|
175
180
|
blockchain_demo/test_utils_import.zx
|
|
176
181
|
blockchain_demo/test_utils_import2.zx
|
|
177
182
|
blockchain_demo/token.zx
|
|
183
|
+
blockchain_demo/token.zxc
|
|
178
184
|
blockchain_demo/utils.zx
|
|
185
|
+
blockchain_demo/utils.zxc
|
|
179
186
|
blockchain_demo/wallet.zx
|
|
187
|
+
blockchain_demo/wallet.zxc
|
|
180
188
|
blockchain_test/PARSER_FIX_DATA_DECLARATIONS.md
|
|
181
189
|
blockchain_test/PERFORMANCE_ANALYSIS.md
|
|
182
190
|
blockchain_test/PERFORMANCE_FINAL_REPORT.md
|
|
@@ -583,6 +591,7 @@ src/zexus/zexus_token.py
|
|
|
583
591
|
src/zexus.egg-info/PKG-INFO
|
|
584
592
|
src/zexus.egg-info/SOURCES.txt
|
|
585
593
|
src/zexus.egg-info/dependency_links.txt
|
|
594
|
+
src/zexus.egg-info/entry_points.txt
|
|
586
595
|
src/zexus.egg-info/not-zip-safe
|
|
587
596
|
src/zexus.egg-info/requires.txt
|
|
588
597
|
src/zexus.egg-info/top_level.txt
|