astreum 0.2.31__py3-none-any.whl → 0.2.33__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.

Potentially problematic release.


This version of astreum might be problematic. Click here for more details.

astreum/_node.py CHANGED
@@ -1,5 +1,5 @@
1
1
  from __future__ import annotations
2
- from typing import Dict, List, Optional
2
+ from typing import Dict, List, Optional, Tuple, Union
3
3
  import uuid
4
4
  import threading
5
5
 
@@ -74,14 +74,14 @@ class Expr:
74
74
  return self.value
75
75
 
76
76
  class Error:
77
- def __init__(self, message: str, origin: Optional['Expr'] = None):
78
- self.message = message
77
+ def __init__(self, topic: str, origin: Optional['Expr'] = None):
78
+ self.topic = topic
79
79
  self.origin = origin
80
80
 
81
81
  def __repr__(self):
82
82
  if self.origin is None:
83
- return f'(error "{self.message}")'
84
- return f'(error "{self.message}" in {self.origin})'
83
+ return f'({self.topic} err)'
84
+ return f'({self.origin} {self.topic} err)'
85
85
 
86
86
  class Env:
87
87
  def __init__(
@@ -93,34 +93,24 @@ class Env:
93
93
  self.parent_id = parent_id
94
94
 
95
95
  class Meter:
96
- def __init__(self, prices: Dict[bytes, int], enabled: bool):
96
+ def __init__(self, enabled: bool = True, limit: Optional[int] = None):
97
97
  self.enabled = enabled
98
- self.prices = prices
99
- self.total = 0
98
+ self.limit: Optional[int] = limit
99
+ self.used: int = 0
100
100
 
101
- def charge(self, op: bytes, size: int):
101
+ def charge_bytes(self, n: int) -> bool:
102
102
  if not self.enabled:
103
- return
104
- self.total += self.prices.get(op, 0) * max(1, size)
103
+ return True
104
+ if n < 0:
105
+ n = 0
106
+ if self.limit is not None and (self.used + n) >= self.limit:
107
+ return False
108
+ self.used += n
109
+ return True
105
110
 
106
111
  class Node:
107
- OPS = {
108
- b"add", b"nand", b"jump",
109
- b"heap_get", b"heap_set",
110
- b"storage_get", b"storage_set",
111
- }
112
-
113
112
  def __init__(self):
114
113
  self.environments: Dict[uuid.UUID, Env] = {}
115
- self.machine_costs: Dict[bytes, int] = {
116
- b"add": 1,
117
- b"nand": 1,
118
- b"jump": 1,
119
- b"heap_set": 1,
120
- b"heap_get": 1,
121
- b"storage_set": 1,
122
- b"storage_get": 1,
123
- }
124
114
  self.in_memory_storage: Dict[bytes, bytes] = {}
125
115
  self.machine_environments_lock = threading.RLock()
126
116
 
@@ -149,11 +139,9 @@ class Node:
149
139
  self.in_memory_storage[key] = value
150
140
 
151
141
  # ---- Eval ----
152
- def low_eval(self, code: List[bytes], metered: bool = False) -> bytes:
142
+ def low_eval(self, code: List[bytes], meter: Meter) -> Union[bytes, Expr.Error]:
153
143
 
154
- heap: Dict[int, bytes] = []
155
-
156
- meter = Meter(self.machine_costs, enabled=metered)
144
+ heap: Dict[bytes, bytes] = {}
157
145
 
158
146
  stack: List[bytes] = []
159
147
  pc = 0
@@ -161,85 +149,110 @@ class Node:
161
149
  while True:
162
150
  if pc >= len(code):
163
151
  if len(stack) != 1:
164
- raise RuntimeError(f"bad stack state at end: {stack}")
152
+ return Expr.Error("bad stack")
165
153
  return stack.pop()
166
154
 
167
155
  tok = code[pc]
168
156
  pc += 1
169
157
 
170
- # opcode?
171
- if tok in self.OPS:
172
- # ---------- ADD ----------
173
- if tok == b"add":
174
- b_b = stack.pop()
175
- a_b = stack.pop()
176
- a_i = tc_to_int(a_b)
177
- b_i = tc_to_int(b_b)
178
- res_i = a_i + b_i
179
- width = max(len(a_b), len(b_b), min_tc_width(res_i))
180
- res_b = int_to_tc(res_i, width)
181
- meter.charge(b"add", bytes_touched(a_b, b_b, res_b))
182
- stack.append(res_b)
183
- continue
184
-
185
- # ---------- NAND ----------
186
- if tok == b"nand":
187
- b_b = stack.pop()
188
- a_b = stack.pop()
189
- res_b = nand_bytes(a_b, b_b)
190
- meter.charge(b"nand", bytes_touched(a_b, b_b, res_b))
191
- stack.append(res_b)
192
- continue
193
-
194
- # ---------- JUMP ----------
195
- if tok == b"jump":
196
- tgt_b = stack.pop()
197
- tgt_i = tc_to_int(tgt_b)
198
- meter.charge(b"jump", 0)
199
- pc = tgt_i
200
- continue
201
-
202
- # ---------- HEAP GET ----------
203
- if tok == b"heap_get":
204
- key = stack.pop()
205
- val = heap.get(key) or b""
206
- meter.charge(b"heap_get", len(val))
207
- stack.append(val)
208
- continue
209
-
210
- # ---------- HEAP SET ----------
211
- if tok == b"heap_set":
212
- val = stack.pop()
213
- key = stack.pop()
214
- ok = heap[key] = val
215
- if not ok:
216
- raise RuntimeError("heap_set failed (env missing)")
217
- meter.charge(b"heap_set", len(val))
218
- continue
219
-
220
- # ---------- STORAGE GET ----------
221
- if tok == b"storage_get":
222
- key = stack.pop()
223
- val = self._local_get(key) or b""
224
- meter.charge(b"storage_get", len(val))
225
- stack.append(val)
226
- continue
227
-
228
- # ---------- STORAGE SET ----------
229
- if tok == b"storage_set":
230
- val = stack.pop()
231
- key = stack.pop()
232
- self._local_set(key, val)
233
- meter.charge(b"storage_set", len(val))
234
- continue
235
-
236
- # unreachable
158
+ # ---------- ADD ----------
159
+ if tok == b"add":
160
+ if len(stack) < 2:
161
+ return Expr.Error("underflow")
162
+ b_b = stack.pop()
163
+ a_b = stack.pop()
164
+ a_i = tc_to_int(a_b)
165
+ b_i = tc_to_int(b_b)
166
+ res_i = a_i + b_i
167
+ width = max(len(a_b), len(b_b), min_tc_width(res_i))
168
+ res_b = int_to_tc(res_i, width)
169
+ # charge for both operands' byte widths
170
+ if not meter.charge_bytes(len(a_b) + len(b_b)):
171
+ return Expr.Error("meter limit")
172
+ stack.append(res_b)
173
+ continue
174
+
175
+ # ---------- NAND ----------
176
+ if tok == b"nand":
177
+ if len(stack) < 2:
178
+ return Expr.Error("underflow")
179
+ b_b = stack.pop()
180
+ a_b = stack.pop()
181
+ res_b = nand_bytes(a_b, b_b)
182
+ # bitwise cost: 2 * max(len(a), len(b))
183
+ if not meter.charge_bytes(2 * max(len(a_b), len(b_b), 1)):
184
+ return Expr.Error("meter limit")
185
+ stack.append(res_b)
186
+ continue
187
+
188
+ # ---------- JUMP ----------
189
+ if tok == b"jump":
190
+ if len(stack) < 1:
191
+ return Expr.Error("underflow")
192
+ tgt_b = stack.pop()
193
+ if not meter.charge_bytes(1):
194
+ return Expr.Error("meter limit")
195
+ tgt_i = tc_to_int(tgt_b)
196
+ if tgt_i < 0 or tgt_i >= len(code):
197
+ return Expr.Error("bad jump")
198
+ pc = tgt_i
199
+ continue
200
+
201
+ # ---------- HEAP GET ----------
202
+ if tok == b"heap_get":
203
+ if len(stack) < 1:
204
+ return Expr.Error("underflow")
205
+ key = stack.pop()
206
+ val = heap.get(key) or b""
207
+ # get cost: 1
208
+ if not meter.charge_bytes(1):
209
+ return Expr.Error("meter limit")
210
+ stack.append(val)
211
+ continue
212
+
213
+ # ---------- HEAP SET ----------
214
+ if tok == b"heap_set":
215
+ if len(stack) < 2:
216
+ return Expr.Error("underflow")
217
+ val = stack.pop()
218
+ key = stack.pop()
219
+ if not meter.charge_bytes(len(val)):
220
+ return Expr.Error("meter limit")
221
+ heap[key] = val
222
+ continue
223
+
224
+ # ---------- STORAGE GET ----------
225
+ if tok == b"storage_get":
226
+ if len(stack) < 1:
227
+ return Expr.Error("underflow")
228
+ key = stack.pop()
229
+ val = self._local_get(key) or b""
230
+ if not meter.charge_bytes(1):
231
+ return Expr.Error("meter limit")
232
+ stack.append(val)
237
233
  continue
238
234
 
235
+ # ---------- STORAGE SET ----------
236
+ if tok == b"storage_set":
237
+ if len(stack) < 2:
238
+ return Expr.Error("underflow")
239
+ val = stack.pop()
240
+ key = stack.pop()
241
+ if not meter.charge_bytes(len(val)):
242
+ return Expr.Error("meter limit")
243
+ self._local_set(key, val)
244
+ continue
245
+
246
+ # if no opcode matched above, treat token as literal
247
+
239
248
  # not an opcode → literal blob
240
249
  stack.append(tok)
241
250
 
242
- def high_eval(self, env_id: uuid.UUID, expr: Expr) -> Expr:
251
+ def high_eval(self, env_id: uuid.UUID, expr: Expr, meter = None) -> Expr:
252
+
253
+ if meter is None:
254
+ meter = Meter()
255
+
243
256
  # ---------- atoms ----------
244
257
  if isinstance(expr, Expr.Error):
245
258
  return expr
@@ -257,7 +270,7 @@ class Node:
257
270
  if len(expr.elements) == 0:
258
271
  return expr
259
272
  if len(expr.elements) == 1:
260
- return self.high_eval(env_id, expr.elements[0])
273
+ return self.high_eval(env_id=env_id, expr=expr.elements[0], meter=meter)
261
274
 
262
275
  tail = expr.elements[-1]
263
276
 
@@ -269,70 +282,98 @@ class Node:
269
282
  if not isinstance(name_e, Expr.Symbol):
270
283
  return Expr.Error("def name must be symbol", origin=name_e)
271
284
  value_e = expr.elements[-3]
272
- value_res = self.high_eval(env_id=env_id, expr=value_e)
285
+ value_res = self.high_eval(env_id=env_id, expr=value_e, meter=meter)
273
286
  if isinstance(value_res, Expr.Error):
274
287
  return value_res
275
288
  self.env_set(env_id, name_e.value.encode(), value_res)
276
289
  return value_res
277
290
 
278
- # ---------- (... (body params sk)) LOW-LEVEL CALL ----------
291
+ # ---- LOW-LEVEL call: ( arg1 arg2 ... ( (body) sk ) ) ----
279
292
  if isinstance(tail, Expr.ListExpr):
280
- fn_form = tail
281
- if (len(fn_form.elements) >= 3
282
- and isinstance(fn_form.elements[-1], Expr.Symbol)
283
- and fn_form.elements[-1].value == "sk"):
284
-
285
- body_expr = fn_form.elements[-3]
286
- params_expr = fn_form.elements[-2]
287
-
293
+ inner = tail.elements
294
+ if len(inner) >= 2 and isinstance(inner[-1], Expr.Symbol) and inner[-1].value == "sk":
295
+ body_expr = inner[-2]
288
296
  if not isinstance(body_expr, Expr.ListExpr):
289
297
  return Expr.Error("sk body must be list", origin=body_expr)
290
- if not isinstance(params_expr, Expr.ListExpr):
291
- return Expr.Error("sk params must be list", origin=params_expr)
292
298
 
293
- # params bytes keys
294
- params: List[bytes] = []
295
- for p in params_expr.elements:
296
- if not isinstance(p, Expr.Symbol):
297
- return Expr.Error("sk param must be symbol", origin=p)
298
- params.append(p.value.encode())
299
+ # helper: turn an Expr into a contiguous bytes buffer
300
+ def to_bytes(v: Expr) -> Union[bytes, Expr.Error]:
301
+ if isinstance(v, Expr.Byte):
302
+ return bytes([v.value & 0xFF])
303
+ if isinstance(v, Expr.ListExpr):
304
+ # expect a list of Expr.Byte
305
+ out: bytearray = bytearray()
306
+ for el in v.elements:
307
+ if isinstance(el, Expr.Byte):
308
+ out.append(el.value & 0xFF)
309
+ else:
310
+ return Expr.Error("byte list must contain only Byte", origin=el)
311
+ return bytes(out)
312
+ if isinstance(v, Expr.Error):
313
+ return v
314
+ return Expr.Error("argument must resolve to Byte or (Byte ...)", origin=v)
299
315
 
300
- # args: preceding items; MUST resolve to Expr.Byte
316
+ # resolve ALL preceding args into bytes (can be Byte or List[Byte])
301
317
  args_exprs = expr.elements[:-1]
302
- if len(args_exprs) != len(params):
303
- return Expr.Error("arity mismatch", origin=expr)
304
-
305
318
  arg_bytes: List[bytes] = []
306
319
  for a in args_exprs:
307
- v = self.high_eval(env_id, a)
320
+ v = self.high_eval(env_id=env_id, expr=a, meter=meter)
308
321
  if isinstance(v, Expr.Error):
309
322
  return v
310
- if not isinstance(v, Expr.Byte):
311
- return Expr.Error("argument must resolve to Byte", origin=a)
312
- arg_bytes.append(bytes([v.value & 0xFF]))
323
+ vb = to_bytes(v)
324
+ if isinstance(vb, Expr.Error):
325
+ return vb
326
+ arg_bytes.append(vb)
313
327
 
314
- subst: Dict[bytes, bytes] = dict(zip(params, arg_bytes))
315
-
316
- # build low-level code with param substitution
328
+ # build low-level code with $0-based placeholders ($0 = first arg)
317
329
  code: List[bytes] = []
318
- for tok in body_expr.elements:
330
+
331
+ def emit(tok: Expr) -> Union[None, Expr.Error]:
319
332
  if isinstance(tok, Expr.Symbol):
320
- sb = tok.value.encode()
321
- code.append(subst.get(sb, sb))
322
- elif isinstance(tok, Expr.Byte):
333
+ name = tok.value
334
+ if name.startswith("$"):
335
+ idx_s = name[1:]
336
+ if not idx_s.isdigit():
337
+ return Expr.Error("invalid sk placeholder", origin=tok)
338
+ idx = int(idx_s) # $0 is first
339
+ if idx < 0 or idx >= len(arg_bytes):
340
+ return Expr.Error("arity mismatch in sk placeholder", origin=tok)
341
+ code.append(arg_bytes[idx])
342
+ return None
343
+ code.append(name.encode())
344
+ return None
345
+
346
+ if isinstance(tok, Expr.Byte):
323
347
  code.append(bytes([tok.value & 0xFF]))
324
- elif isinstance(tok, Expr.ListExpr):
325
- rv = self.high_eval(env_id, tok)
348
+ return None
349
+
350
+ if isinstance(tok, Expr.ListExpr):
351
+ rv = self.high_eval(env_id, tok, meter=meter)
326
352
  if isinstance(rv, Expr.Error):
327
353
  return rv
328
- if not isinstance(rv, Expr.Byte):
329
- return Expr.Error("nested list must resolve to Byte", origin=tok)
330
- code.append(bytes([rv.value & 0xFF]))
331
- else:
332
- return Expr.Error("invalid token in sk body", origin=tok)
354
+ rb = to_bytes(rv)
355
+ if isinstance(rb, Expr.Error):
356
+ return rb
357
+ code.append(rb)
358
+ return None
359
+
360
+ if isinstance(tok, Expr.Error):
361
+ return tok
362
+
363
+ return Expr.Error("invalid token in sk body", origin=tok)
364
+
365
+ for t in body_expr.elements:
366
+ err = emit(t)
367
+ if isinstance(err, Expr.Error):
368
+ return err
369
+
370
+ # Execute low-level code built from sk-body using the caller's meter
371
+ res = self.low_eval(code, meter=meter)
372
+ if isinstance(res, Expr.Error):
373
+ return res
374
+ return Expr.ListExpr([Expr.Byte(b) for b in res])
375
+
333
376
 
334
- res_bytes = self.low_eval(code, metered=False)
335
- return Expr.ListExpr([Expr.Byte(b) for b in res_bytes])
336
377
 
337
378
  # ---------- (... (body params fn)) HIGH-LEVEL CALL ----------
338
379
  if isinstance(tail, Expr.ListExpr):
@@ -361,7 +402,7 @@ class Node:
361
402
 
362
403
  arg_bytes: List[bytes] = []
363
404
  for a in args_exprs:
364
- v = self.high_eval(env_id, a)
405
+ v = self.high_eval(env_id, a, meter=meter)
365
406
  if isinstance(v, Expr.Error):
366
407
  return v
367
408
  if not isinstance(v, Expr.Byte):
@@ -374,9 +415,78 @@ class Node:
374
415
  for name_b, val_b in zip(params, arg_bytes):
375
416
  self.env_set(child_env, name_b, Expr.Byte(val_b[0]))
376
417
 
377
- # evaluate HL body
378
- return self.high_eval(child_env, body_expr)
418
+ # evaluate HL body, metered from the top
419
+ return self.high_eval(child_env, body_expr, meter=meter)
379
420
 
380
421
  # ---------- default: resolve each element and return list ----------
381
- resolved: List[Expr] = [self.high_eval(env_id, e) for e in expr.elements]
422
+ resolved: List[Expr] = [self.high_eval(env_id, e, meter=meter) for e in expr.elements]
382
423
  return Expr.ListExpr(resolved)
424
+
425
+ # ===============================
426
+ # 3. Lightweight Parser for Expr (postfix, limited forms)
427
+ # ===============================
428
+
429
+ class ParseError(Exception):
430
+ pass
431
+
432
+ def tokenize(source: str) -> List[str]:
433
+ """Tokenize a minimal Lispeum subset for this module.
434
+
435
+ Supports:
436
+ - Integers (e.g., -1, 0, 255) → Byte
437
+ - Symbols (e.g., add, nand, def, fn, sk, int.sub, $0)
438
+ - Lists using parentheses
439
+ """
440
+ tokens: List[str] = []
441
+ cur: List[str] = []
442
+ for ch in source:
443
+ if ch.isspace():
444
+ if cur:
445
+ tokens.append("".join(cur))
446
+ cur = []
447
+ continue
448
+ if ch in ("(", ")"):
449
+ if cur:
450
+ tokens.append("".join(cur))
451
+ cur = []
452
+ tokens.append(ch)
453
+ continue
454
+ cur.append(ch)
455
+ if cur:
456
+ tokens.append("".join(cur))
457
+ return tokens
458
+
459
+ def _parse_one(tokens: List[str], pos: int = 0) -> Tuple[Expr, int]:
460
+ if pos >= len(tokens):
461
+ raise ParseError("unexpected end")
462
+ tok = tokens[pos]
463
+
464
+ if tok == '(': # list
465
+ items: List[Expr] = []
466
+ i = pos + 1
467
+ while i < len(tokens):
468
+ if tokens[i] == ')':
469
+ # special-case error form at close: (origin topic err) or (topic err)
470
+ if len(items) >= 3 and isinstance(items[-1], Expr.Symbol) and items[-1].value == 'err' and isinstance(items[-2], Expr.Symbol):
471
+ return Expr.Error(items[-2].value, origin=items[-3]), i + 1
472
+ if len(items) == 2 and isinstance(items[-1], Expr.Symbol) and items[-1].value == 'err' and isinstance(items[-2], Expr.Symbol):
473
+ return Expr.Error(items[-2].value), i + 1
474
+ return Expr.ListExpr(items), i + 1
475
+ expr, i = _parse_one(tokens, i)
476
+ items.append(expr)
477
+ raise ParseError("expected ')'")
478
+
479
+ if tok == ')':
480
+ raise ParseError("unexpected ')'")
481
+
482
+ # try integer → Byte
483
+ try:
484
+ n = int(tok)
485
+ return Expr.Byte(n), pos + 1
486
+ except ValueError:
487
+ return Expr.Symbol(tok), pos + 1
488
+
489
+ def parse(tokens: List[str]) -> Tuple[Expr, List[str]]:
490
+ """Parse tokens into an Expr and return (expr, remaining_tokens)."""
491
+ expr, next_pos = _parse_one(tokens, 0)
492
+ return expr, tokens[next_pos:]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: astreum
3
- Version: 0.2.31
3
+ Version: 0.2.33
4
4
  Summary: Python library to interact with the Astreum blockchain and its Lispeum virtual machine.
5
5
  Author-email: "Roy R. O. Okello" <roy@stelar.xyz>
6
6
  Project-URL: Homepage, https://github.com/astreum/lib
@@ -76,24 +76,45 @@ node = Node(config)
76
76
  The Lispeum virtual machine (VM) is embedded inside `astreum.Node`. You feed it Lispeum source text, and the node tokenizes, parses, and **evaluates** the resulting AST inside an isolated environment.
77
77
 
78
78
  ```python
79
- from astreum.node import Node
80
- from astreum.machine.tokenizer import tokenize
81
- from astreum.machine.parser import parse
79
+ # Define a named function int.add (stack body) and call it with bytes 1 and 2
80
+
81
+ import uuid
82
+ from astreum._node import Node, Env, Expr
83
+
84
+ # 1) Spin‑up a stand‑alone VM
85
+ node = Node()
86
+
87
+ # 2) Create an environment (simple manual setup)
88
+ env_id = uuid.uuid4()
89
+ node.environments[env_id] = Env()
90
+
91
+ # 3) Build a function value using a low‑level stack body via `sk`.
92
+ # Body does: $0 $1 add (i.e., a + b)
93
+ low_body = Expr.ListExpr([
94
+ Expr.Symbol("$0"), # a (first arg)
95
+ Expr.Symbol("$1"), # b (second arg)
96
+ Expr.Symbol("add"),
97
+ ])
82
98
 
83
- # 1. Spin‑up a stand‑alone VM (machine‑only node).
84
- node = Node({"machine-only": True})
99
+ fn_body = Expr.ListExpr([
100
+ Expr.Symbol("a"),
101
+ Expr.Symbol("b"),
102
+ Expr.ListExpr([low_body, Expr.Symbol("sk")]),
103
+ ])
85
104
 
86
- # 2. Create an environment.
87
- env_id = node.machine_create_environment()
105
+ params = Expr.ListExpr([Expr.Symbol("a"), Expr.Symbol("b")])
106
+ int_add_fn = Expr.ListExpr([fn_body, params, Expr.Symbol("fn")])
88
107
 
89
- # 3. Convert Lispeum source Expr AST.
90
- source = '(+ 1 (* 2 3))'
91
- expr, _ = parse(tokenize(source))
108
+ # 4) Store under the name "int.add"
109
+ node.env_set(env_id, b"int.add", int_add_fn)
92
110
 
93
- # 4. Evaluate
94
- result = node.machine_expr_eval(env_id=env_id, expr=expr) # -> Expr.Integer(7)
111
+ # 5) Retrieve the function and call it with bytes 1 and 2
112
+ bound = node.env_get(env_id, b"int.add")
113
+ call = Expr.ListExpr([Expr.Byte(1), Expr.Byte(2), bound])
114
+ res = node.high_eval(env_id, call)
95
115
 
96
- print(result.value) # 7
116
+ # sk returns a list of bytes; for 1+2 expect a single byte with value 3
117
+ print([b.value for b in res.elements]) # [3]
97
118
  ```
98
119
 
99
120
  ### Handling errors
@@ -1,5 +1,5 @@
1
1
  astreum/__init__.py,sha256=y2Ok3EY_FstcmlVASr80lGR_0w-dH-SXDCCQFmL6uwA,28
2
- astreum/_node.py,sha256=ovOrboz7HYPLM2PdDat8O_Z62u-7FamMqi5c4lJfNmA,14499
2
+ astreum/_node.py,sha256=cJSzj7N4unkOH5T_nDa7bKy_jBD-p0oJTDlAXjuFFYw,18773
3
3
  astreum/format.py,sha256=X4tG5GGPweNCE54bHYkLFiuLTbmpy5upO_s1Cef-MGA,2711
4
4
  astreum/node.py,sha256=SuVm1b0QWl1FpDUaLRH1fiFYnXCrPs6qYeUQlPDae8w,38358
5
5
  astreum/crypto/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -27,8 +27,8 @@ astreum/relay/setup.py,sha256=ynvGaJdlDtw_f5LLiow2Wo7IRzUjvgk8eSr1Sv4_zTg,2090
27
27
  astreum/storage/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
28
28
  astreum/storage/object.py,sha256=knFlvw_tpcC4twSu1DGNpHX31wlANN8E5dgEqIfU--Q,2041
29
29
  astreum/storage/setup.py,sha256=1-9ztEFI_BvRDvAA0lAn4mFya8iq65THTArlj--M3Hg,626
30
- astreum-0.2.31.dist-info/licenses/LICENSE,sha256=gYBvRDP-cPLmTyJhvZ346QkrYW_eleke4Z2Yyyu43eQ,1089
31
- astreum-0.2.31.dist-info/METADATA,sha256=485DWO4gUtf7DGSsbpZg4zOiIc5yzkQE_VC6q_Zdi30,5478
32
- astreum-0.2.31.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
33
- astreum-0.2.31.dist-info/top_level.txt,sha256=1EG1GmkOk3NPmUA98FZNdKouhRyget-KiFiMk0i2Uz0,8
34
- astreum-0.2.31.dist-info/RECORD,,
30
+ astreum-0.2.33.dist-info/licenses/LICENSE,sha256=gYBvRDP-cPLmTyJhvZ346QkrYW_eleke4Z2Yyyu43eQ,1089
31
+ astreum-0.2.33.dist-info/METADATA,sha256=t-sfdHamxd73CFIfgiXrl5zph6CnZRtlwCRCqVrQGVA,6149
32
+ astreum-0.2.33.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
33
+ astreum-0.2.33.dist-info/top_level.txt,sha256=1EG1GmkOk3NPmUA98FZNdKouhRyget-KiFiMk0i2Uz0,8
34
+ astreum-0.2.33.dist-info/RECORD,,