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

@@ -1,254 +1,352 @@
1
- import threading
2
- from typing import Dict, Optional
3
- import uuid
4
-
5
- from astreum.lispeum.special.definition import handle_definition
6
- from astreum.lispeum.special.list.all import handle_list_all
7
- from astreum.lispeum.special.list.any import handle_list_any
8
- from astreum.lispeum.special.list.fold import handle_list_fold
9
- from astreum.lispeum.special.list.get import handle_list_get
10
- from astreum.lispeum.special.list.insert import handle_list_insert
11
- from astreum.lispeum.special.list.map import handle_list_map
12
- from astreum.lispeum.special.list.position import handle_list_position
13
- from astreum.lispeum.special.list.remove import handle_list_remove
14
- from astreum.machine.environment import Environment
15
- from astreum.lispeum.expression import Expr
16
- from astreum.lispeum.tokenizer import tokenize
17
- from astreum.lispeum.parser import parse
18
-
19
- class AstreumMachine:
20
- def __init__(self, node: 'Node' = None):
21
- self.global_env = Environment(node=node)
22
- self.sessions: Dict[str, Environment] = {}
23
- self.lock = threading.Lock()
24
-
25
-
26
- def create_session(self) -> str:
27
- session_id = str(uuid.uuid4())
28
- with self.lock:
29
- self.sessions[session_id] = Environment(parent=self.global_env)
30
- return session_id
31
-
32
- def terminate_session(self, session_id: str) -> bool:
33
- with self.lock:
34
- if session_id in self.sessions:
35
- del self.sessions[session_id]
36
- return True
37
- else:
38
- return False
39
-
40
- def get_session_env(self, session_id: str) -> Optional[Environment]:
41
- with self.lock:
42
- return self.sessions.get(session_id, None)
43
-
44
- def evaluate_code(self, code: str, session_id: str) -> Expr:
45
- session_env = self.get_session_env(session_id)
46
- if session_env is None:
47
- raise ValueError(f"Session ID {session_id} not found.")
48
-
49
- try:
50
- tkns = tokenize(input=code)
51
- expr, _ = parse(tokens=tkns)
52
- result = self.evaluate_expression(expr, session_env)
53
- return result
54
-
55
- except Exception as e:
56
- raise ValueError(e)
57
-
58
- def evaluate_expression(self, expr: Expr, env: Environment) -> Expr:
59
- if isinstance(expr, Expr.Boolean) or isinstance(expr, Expr.Integer) or isinstance(expr, Expr.String) or isinstance(expr, Expr.Error):
60
- return expr
61
-
62
- elif isinstance(expr, Expr.Symbol):
63
- value = env.get(expr.value)
64
-
65
- if value:
66
- return value
67
- else:
68
- return Expr.Error(
69
- category="NameError",
70
- message=f"Variable '{expr.value}' not found in environments."
71
- )
72
-
73
- elif isinstance(expr, Expr.ListExpr):
74
-
75
- if len(expr.elements) == 0:
76
- return expr
77
-
78
- if len(expr.elements) == 1:
79
- return self.evaluate_expression(expr=expr.elements[0], env=env)
80
-
81
- first = expr.elements[0]
82
-
83
- if isinstance(first, Expr.Symbol):
84
-
85
- first_symbol_value = env.get(first.value)
86
-
87
- if first_symbol_value and not isinstance(first_symbol_value, Expr.Function):
88
- evaluated_elements = [self.evaluate_expression(e, env) for e in expr.elements]
89
- return Expr.ListExpr(evaluated_elements)
90
-
91
- elif first.value == "def":
92
- return handle_definition(
93
- machine=self,
94
- args=expr.elements[1:],
95
- env=env
96
- )
97
-
98
- # List
99
- elif first.value == "list.new":
100
- return Expr.ListExpr([self.evaluate_expression(arg, env) for arg in expr.elements[1:]])
101
-
102
- elif first.value == "list.get":
103
- args = expr.elements[1:]
104
- if len(args) != 2:
105
- return Expr.Error(
106
- category="SyntaxError",
107
- message="list.get expects exactly two arguments: a list and an index"
108
- )
109
- list_obj = self.evaluate_expression(args[0], env)
110
- index = self.evaluate_expression(args[1], env)
111
- return handle_list_get(self, list_obj, index, env)
112
-
113
- elif first.value == "list.insert":
114
- args = expr.elements[1:]
115
- if len(args) != 3:
116
- return Expr.ListExpr([
117
- Expr.ListExpr([]),
118
- Expr.String("list.insert expects exactly three arguments: a list, an index, and a value")
119
- ])
120
-
121
- return handle_list_insert(
122
- list=self.evaluate_expression(args[0], env),
123
- index=self.evaluate_expression(args[1], env),
124
- value=self.evaluate_expression(args[2], env),
125
- )
126
-
127
- elif first.value == "list.remove":
128
- args = expr.elements[1:]
129
- if len(args) != 2:
130
- return Expr.ListExpr([
131
- Expr.ListExpr([]),
132
- Expr.String("list.remove expects exactly two arguments: a list and an index")
133
- ])
134
-
135
- return handle_list_remove(
136
- list=self.evaluate_expression(args[0], env),
137
- index=self.evaluate_expression(args[1], env),
138
- )
139
-
140
- elif first.value == "list.length":
141
- args = expr.elements[1:]
142
- if len(args) != 1:
143
- return Expr.ListExpr([
144
- Expr.ListExpr([]),
145
- Expr.String("list.length expects exactly one argument: a list")
146
- ])
147
-
148
- list_obj = self.evaluate_expression(args[0], env)
149
- if not isinstance(list_obj, Expr.ListExpr):
150
- return Expr.ListExpr([
151
- Expr.ListExpr([]),
152
- Expr.String("Argument must be a list")
153
- ])
154
-
155
- return Expr.ListExpr([
156
- Expr.Integer(len(list_obj.elements)),
157
- Expr.ListExpr([])
158
- ])
159
-
160
- elif first.value == "list.fold":
161
- if len(args) != 3:
162
- return Expr.ListExpr([
163
- Expr.ListExpr([]),
164
- Expr.String("list.fold expects exactly three arguments: a list, an initial value, and a function")
165
- ])
166
-
167
- return handle_list_fold(
168
- machine=self,
169
- list=self.evaluate_expression(args[0], env),
170
- initial=self.evaluate_expression(args[1], env),
171
- func=self.evaluate_expression(args[2], env),
172
- env=env,
173
- )
174
-
175
- elif first.value == "list.map":
176
- if len(args) != 2:
177
- return Expr.ListExpr([
178
- Expr.ListExpr([]),
179
- Expr.String("list.map expects exactly two arguments: a list and a function")
180
- ])
181
-
182
- return handle_list_map(
183
- machine=self,
184
- list=self.evaluate_expression(args[0], env),
185
- func=self.evaluate_expression(args[1], env),
186
- env=env,
187
- )
188
-
189
- elif first.value == "list.position":
190
- if len(args) != 2:
191
- return Expr.ListExpr([
192
- Expr.ListExpr([]),
193
- Expr.String("list.position expects exactly two arguments: a list and a function")
194
- ])
195
-
196
- return handle_list_position(
197
- machine=self,
198
- list=self.evaluate_expression(args[0], env),
199
- predicate=self.evaluate_expression(args[1], env),
200
- env=env,
201
- )
202
-
203
- elif first.value == "list.any":
204
- if len(args) != 2:
205
- return Expr.ListExpr([
206
- Expr.ListExpr([]),
207
- Expr.String("list.any expects exactly two arguments: a list and a function")
208
- ])
209
-
210
- return handle_list_any(
211
- machine=self,
212
- list=self.evaluate_expression(args[0], env),
213
- predicate=self.evaluate_expression(args[1], env),
214
- env=env,
215
- )
216
-
217
- elif first.value == "list.all":
218
- if len(args) != 2:
219
- return Expr.ListExpr([
220
- Expr.ListExpr([]),
221
- Expr.String("list.all expects exactly two arguments: a list and a function")
222
- ])
223
-
224
- return handle_list_all(
225
- machine=self,
226
- list=self.evaluate_expression(args[0], env),
227
- predicate=self.evaluate_expression(args[1], env),
228
- env=env,
229
- )
230
-
231
- # Number
232
- elif first.value == "+":
233
- evaluated_args = [self.evaluate_expression(arg, env) for arg in expr.elements[1:]]
234
-
235
- # Check for non-integer arguments
236
- if not all(isinstance(arg, Expr.Integer) for arg in evaluated_args):
237
- return Expr.Error(
238
- category="TypeError",
239
- message="All arguments to + must be integers"
240
- )
241
-
242
- # Sum up the integer values
243
- result = sum(arg.value for arg in evaluated_args)
244
- return Expr.Integer(result)
245
-
246
- else:
247
- evaluated_elements = [self.evaluate_expression(e, env) for e in expr.elements]
248
- return Expr.ListExpr(evaluated_elements)
249
-
250
- elif isinstance(expr, Expr.Function):
251
- return expr
252
-
253
- else:
254
- raise ValueError(f"Unknown expression type: {type(expr)}")
1
+ import threading
2
+ from typing import Dict, Optional
3
+ import uuid
4
+
5
+ from astreum.lispeum.special.definition import handle_definition
6
+ from astreum.lispeum.special.list.all import handle_list_all
7
+ from astreum.lispeum.special.list.any import handle_list_any
8
+ from astreum.lispeum.special.list.fold import handle_list_fold
9
+ from astreum.lispeum.special.list.get import handle_list_get
10
+ from astreum.lispeum.special.list.insert import handle_list_insert
11
+ from astreum.lispeum.special.list.map import handle_list_map
12
+ from astreum.lispeum.special.list.position import handle_list_position
13
+ from astreum.lispeum.special.list.remove import handle_list_remove
14
+ from astreum.machine.environment import Environment
15
+ from astreum.lispeum.expression import Expr
16
+ from astreum.lispeum.tokenizer import tokenize
17
+ from astreum.lispeum.parser import parse
18
+
19
+ class AstreumMachine:
20
+ def __init__(self, node: 'Node' = None):
21
+ self.global_env = Environment(node=node)
22
+ self.sessions: Dict[str, Environment] = {}
23
+ self.lock = threading.Lock()
24
+
25
+
26
+ def create_session(self) -> str:
27
+ session_id = str(uuid.uuid4())
28
+ with self.lock:
29
+ self.sessions[session_id] = Environment(parent=self.global_env)
30
+ return session_id
31
+
32
+ def terminate_session(self, session_id: str) -> bool:
33
+ with self.lock:
34
+ if session_id in self.sessions:
35
+ del self.sessions[session_id]
36
+ return True
37
+ else:
38
+ return False
39
+
40
+ def get_session_env(self, session_id: str) -> Optional[Environment]:
41
+ with self.lock:
42
+ return self.sessions.get(session_id, None)
43
+
44
+ def evaluate_code(self, code: str, session_id: str) -> Expr:
45
+ session_env = self.get_session_env(session_id)
46
+ if session_env is None:
47
+ raise ValueError(f"Session ID {session_id} not found.")
48
+
49
+ try:
50
+ tkns = tokenize(input=code)
51
+ expr, _ = parse(tokens=tkns)
52
+ result = self.evaluate_expression(expr, session_env)
53
+ return result
54
+
55
+ except Exception as e:
56
+ raise ValueError(e)
57
+
58
+ def evaluate_expression(self, expr: Expr, env: Environment) -> Expr:
59
+ if isinstance(expr, Expr.Boolean) or isinstance(expr, Expr.Integer) or isinstance(expr, Expr.String) or isinstance(expr, Expr.Error):
60
+ return expr
61
+
62
+ elif isinstance(expr, Expr.Symbol):
63
+ value = env.get(expr.value)
64
+
65
+ if value:
66
+ return value
67
+ else:
68
+ return Expr.Error(
69
+ category="NameError",
70
+ message=f"Variable '{expr.value}' not found in environments."
71
+ )
72
+
73
+ elif isinstance(expr, Expr.ListExpr):
74
+
75
+ if len(expr.elements) == 0:
76
+ return expr
77
+
78
+ if len(expr.elements) == 1:
79
+ return self.evaluate_expression(expr=expr.elements[0], env=env)
80
+
81
+ first = expr.elements[0]
82
+
83
+ if isinstance(first, Expr.Symbol):
84
+
85
+ first_symbol_value = env.get(first.value)
86
+
87
+ if first_symbol_value and not isinstance(first_symbol_value, Expr.Function):
88
+ evaluated_elements = [self.evaluate_expression(e, env) for e in expr.elements]
89
+ return Expr.ListExpr(evaluated_elements)
90
+
91
+ elif first.value == "def":
92
+ return handle_definition(
93
+ machine=self,
94
+ args=expr.elements[1:],
95
+ env=env
96
+ )
97
+
98
+ # List
99
+ elif first.value == "list.new":
100
+ return Expr.ListExpr([self.evaluate_expression(arg, env) for arg in expr.elements[1:]])
101
+
102
+ elif first.value == "list.get":
103
+ args = expr.elements[1:]
104
+ if len(args) != 2:
105
+ return Expr.Error(
106
+ category="SyntaxError",
107
+ message="list.get expects exactly two arguments: a list and an index"
108
+ )
109
+ list_obj = self.evaluate_expression(args[0], env)
110
+ index = self.evaluate_expression(args[1], env)
111
+ return handle_list_get(self, list_obj, index, env)
112
+
113
+ elif first.value == "list.insert":
114
+ args = expr.elements[1:]
115
+ if len(args) != 3:
116
+ return Expr.ListExpr([
117
+ Expr.ListExpr([]),
118
+ Expr.String("list.insert expects exactly three arguments: a list, an index, and a value")
119
+ ])
120
+
121
+ return handle_list_insert(
122
+ list=self.evaluate_expression(args[0], env),
123
+ index=self.evaluate_expression(args[1], env),
124
+ value=self.evaluate_expression(args[2], env),
125
+ )
126
+
127
+ elif first.value == "list.remove":
128
+ args = expr.elements[1:]
129
+ if len(args) != 2:
130
+ return Expr.ListExpr([
131
+ Expr.ListExpr([]),
132
+ Expr.String("list.remove expects exactly two arguments: a list and an index")
133
+ ])
134
+
135
+ return handle_list_remove(
136
+ list=self.evaluate_expression(args[0], env),
137
+ index=self.evaluate_expression(args[1], env),
138
+ )
139
+
140
+ elif first.value == "list.length":
141
+ args = expr.elements[1:]
142
+ if len(args) != 1:
143
+ return Expr.ListExpr([
144
+ Expr.ListExpr([]),
145
+ Expr.String("list.length expects exactly one argument: a list")
146
+ ])
147
+
148
+ list_obj = self.evaluate_expression(args[0], env)
149
+ if not isinstance(list_obj, Expr.ListExpr):
150
+ return Expr.ListExpr([
151
+ Expr.ListExpr([]),
152
+ Expr.String("Argument must be a list")
153
+ ])
154
+
155
+ return Expr.ListExpr([
156
+ Expr.Integer(len(list_obj.elements)),
157
+ Expr.ListExpr([])
158
+ ])
159
+
160
+ elif first.value == "list.fold":
161
+ if len(args) != 3:
162
+ return Expr.ListExpr([
163
+ Expr.ListExpr([]),
164
+ Expr.String("list.fold expects exactly three arguments: a list, an initial value, and a function")
165
+ ])
166
+
167
+ return handle_list_fold(
168
+ machine=self,
169
+ list=self.evaluate_expression(args[0], env),
170
+ initial=self.evaluate_expression(args[1], env),
171
+ func=self.evaluate_expression(args[2], env),
172
+ env=env,
173
+ )
174
+
175
+ elif first.value == "list.map":
176
+ if len(args) != 2:
177
+ return Expr.ListExpr([
178
+ Expr.ListExpr([]),
179
+ Expr.String("list.map expects exactly two arguments: a list and a function")
180
+ ])
181
+
182
+ return handle_list_map(
183
+ machine=self,
184
+ list=self.evaluate_expression(args[0], env),
185
+ func=self.evaluate_expression(args[1], env),
186
+ env=env,
187
+ )
188
+
189
+ elif first.value == "list.position":
190
+ if len(args) != 2:
191
+ return Expr.ListExpr([
192
+ Expr.ListExpr([]),
193
+ Expr.String("list.position expects exactly two arguments: a list and a function")
194
+ ])
195
+
196
+ return handle_list_position(
197
+ machine=self,
198
+ list=self.evaluate_expression(args[0], env),
199
+ predicate=self.evaluate_expression(args[1], env),
200
+ env=env,
201
+ )
202
+
203
+ elif first.value == "list.any":
204
+ if len(args) != 2:
205
+ return Expr.ListExpr([
206
+ Expr.ListExpr([]),
207
+ Expr.String("list.any expects exactly two arguments: a list and a function")
208
+ ])
209
+
210
+ return handle_list_any(
211
+ machine=self,
212
+ list=self.evaluate_expression(args[0], env),
213
+ predicate=self.evaluate_expression(args[1], env),
214
+ env=env,
215
+ )
216
+
217
+ elif first.value == "list.all":
218
+ if len(args) != 2:
219
+ return Expr.ListExpr([
220
+ Expr.ListExpr([]),
221
+ Expr.String("list.all expects exactly two arguments: a list and a function")
222
+ ])
223
+
224
+ return handle_list_all(
225
+ machine=self,
226
+ list=self.evaluate_expression(args[0], env),
227
+ predicate=self.evaluate_expression(args[1], env),
228
+ env=env,
229
+ )
230
+
231
+ # Number
232
+ elif first.value == "+":
233
+ evaluated_args = [self.evaluate_expression(arg, env) for arg in expr.elements[1:]]
234
+
235
+ # Check for non-integer arguments
236
+ if not all(isinstance(arg, Expr.Integer) for arg in evaluated_args):
237
+ return Expr.Error(
238
+ category="TypeError",
239
+ message="All arguments to + must be integers"
240
+ )
241
+
242
+ # Sum up the integer values
243
+ result = sum(arg.value for arg in evaluated_args)
244
+ return Expr.Integer(result)
245
+
246
+ # Subtraction
247
+ elif first.value == "-":
248
+ evaluated_args = [self.evaluate_expression(arg, env) for arg in expr.elements[1:]]
249
+
250
+ # Check for non-integer arguments
251
+ if not all(isinstance(arg, Expr.Integer) for arg in evaluated_args):
252
+ return Expr.Error(
253
+ category="TypeError",
254
+ message="All arguments to - must be integers"
255
+ )
256
+
257
+ # With only one argument, negate it
258
+ if len(evaluated_args) == 1:
259
+ return Expr.Integer(-evaluated_args[0].value)
260
+
261
+ # With multiple arguments, subtract all from the first
262
+ result = evaluated_args[0].value
263
+ for arg in evaluated_args[1:]:
264
+ result -= arg.value
265
+
266
+ return Expr.Integer(result)
267
+
268
+ # Multiplication
269
+ elif first.value == "*":
270
+ evaluated_args = [self.evaluate_expression(arg, env) for arg in expr.elements[1:]]
271
+
272
+ # Check for non-integer arguments
273
+ if not all(isinstance(arg, Expr.Integer) for arg in evaluated_args):
274
+ return Expr.Error(
275
+ category="TypeError",
276
+ message="All arguments to * must be integers"
277
+ )
278
+
279
+ # Multiply all values
280
+ result = 1
281
+ for arg in evaluated_args:
282
+ result *= arg.value
283
+
284
+ return Expr.Integer(result)
285
+
286
+ # Division (integer division)
287
+ elif first.value == "/":
288
+ evaluated_args = [self.evaluate_expression(arg, env) for arg in expr.elements[1:]]
289
+
290
+ # Check for non-integer arguments
291
+ if not all(isinstance(arg, Expr.Integer) for arg in evaluated_args):
292
+ return Expr.Error(
293
+ category="TypeError",
294
+ message="All arguments to / must be integers"
295
+ )
296
+
297
+ # Need exactly two arguments
298
+ if len(evaluated_args) != 2:
299
+ return Expr.Error(
300
+ category="ArgumentError",
301
+ message="The / operation requires exactly two arguments"
302
+ )
303
+
304
+ dividend = evaluated_args[0].value
305
+ divisor = evaluated_args[1].value
306
+
307
+ if divisor == 0:
308
+ return Expr.Error(
309
+ category="DivisionError",
310
+ message="Division by zero"
311
+ )
312
+
313
+ return Expr.Integer(dividend // divisor) # Integer division
314
+
315
+ # Remainder (modulo)
316
+ elif first.value == "%":
317
+ evaluated_args = [self.evaluate_expression(arg, env) for arg in expr.elements[1:]]
318
+
319
+ # Check for non-integer arguments
320
+ if not all(isinstance(arg, Expr.Integer) for arg in evaluated_args):
321
+ return Expr.Error(
322
+ category="TypeError",
323
+ message="All arguments to % must be integers"
324
+ )
325
+
326
+ # Need exactly two arguments
327
+ if len(evaluated_args) != 2:
328
+ return Expr.Error(
329
+ category="ArgumentError",
330
+ message="The % operation requires exactly two arguments"
331
+ )
332
+
333
+ dividend = evaluated_args[0].value
334
+ divisor = evaluated_args[1].value
335
+
336
+ if divisor == 0:
337
+ return Expr.Error(
338
+ category="DivisionError",
339
+ message="Modulo by zero"
340
+ )
341
+
342
+ return Expr.Integer(dividend % divisor)
343
+
344
+ else:
345
+ evaluated_elements = [self.evaluate_expression(e, env) for e in expr.elements]
346
+ return Expr.ListExpr(evaluated_elements)
347
+
348
+ elif isinstance(expr, Expr.Function):
349
+ return expr
350
+
351
+ else:
352
+ raise ValueError(f"Unknown expression type: {type(expr)}")
astreum/node/__init__.py CHANGED
@@ -13,16 +13,17 @@ from astreum.lispeum.storage import store_expr, get_expr_from_storage
13
13
 
14
14
  class Node:
15
15
  def __init__(self, config: dict):
16
- self.config = config
17
- self.relay = Relay(config)
16
+ # Ensure config is a dictionary, but allow it to be None
17
+ self.config = config if config is not None else {}
18
+ self.relay = Relay(self.config)
18
19
  # Get the node_id from relay instead of generating our own
19
20
  self.node_id = self.relay.node_id
20
- self.storage = Storage(config)
21
+ self.storage = Storage(self.config)
21
22
  self.storage.node = self # Set the storage node reference to self
22
23
 
23
24
  # Latest block of the chain this node is following
24
25
  self.latest_block = None
25
- self.followed_chain_id = config.get('followed_chain_id', None)
26
+ self.followed_chain_id = self.config.get('followed_chain_id', None)
26
27
 
27
28
  # Initialize machine
28
29
  self.machine = AstreumMachine(node=self)
astreum/node/models.py CHANGED
@@ -15,8 +15,21 @@ class Storage:
15
15
  def __init__(self, config: dict):
16
16
  self.max_space = config.get('max_storage_space', 1024 * 1024 * 1024) # Default 1GB
17
17
  self.current_space = 0
18
- self.storage_path = Path(config.get('storage_path', 'storage'))
19
- self.storage_path.mkdir(parents=True, exist_ok=True)
18
+
19
+ # Check if storage_path is provided in config
20
+ storage_path = config.get('storage_path')
21
+ self.use_memory_storage = storage_path is None
22
+
23
+ # Initialize in-memory storage if no path provided
24
+ self.memory_storage = {} if self.use_memory_storage else None
25
+
26
+ # Only create storage path if not using memory storage
27
+ if not self.use_memory_storage:
28
+ self.storage_path = Path(storage_path)
29
+ self.storage_path.mkdir(parents=True, exist_ok=True)
30
+ # Calculate current space usage
31
+ self.current_space = sum(f.stat().st_size for f in self.storage_path.glob('*') if f.is_file())
32
+
20
33
  self.max_object_recursion = config.get('max_object_recursion', 50)
21
34
  self.network_request_timeout = config.get('network_request_timeout', 5.0) # Default 5 second timeout
22
35
  self.node = None # Will be set by the Node after initialization
@@ -24,9 +37,6 @@ class Storage:
24
37
  # In-progress requests tracking
25
38
  self.pending_requests = {} # hash -> (start_time, event)
26
39
  self.request_lock = threading.Lock()
27
-
28
- # Calculate current space usage
29
- self.current_space = sum(f.stat().st_size for f in self.storage_path.glob('*') if f.is_file())
30
40
 
31
41
  def put(self, data_hash: bytes, data: bytes) -> bool:
32
42
  """Store data with its hash. Returns True if successful, False if space limit exceeded."""
@@ -34,6 +44,14 @@ class Storage:
34
44
  if self.current_space + data_size > self.max_space:
35
45
  return False
36
46
 
47
+ # If using memory storage, store in dictionary
48
+ if self.use_memory_storage:
49
+ if data_hash not in self.memory_storage:
50
+ self.memory_storage[data_hash] = data
51
+ self.current_space += data_size
52
+ return True
53
+
54
+ # Otherwise use file storage
37
55
  file_path = self.storage_path / data_hash.hex()
38
56
 
39
57
  # Don't store if already exists
@@ -54,6 +72,11 @@ class Storage:
54
72
 
55
73
  def _local_get(self, data_hash: bytes) -> Optional[bytes]:
56
74
  """Get data from local storage only, no network requests."""
75
+ # If using memory storage, get from dictionary
76
+ if self.use_memory_storage:
77
+ return self.memory_storage.get(data_hash)
78
+
79
+ # Otherwise use file storage
57
80
  file_path = self.storage_path / data_hash.hex()
58
81
  if file_path.exists():
59
82
  return file_path.read_bytes()
@@ -144,6 +167,8 @@ class Storage:
144
167
 
145
168
  def contains(self, data_hash: bytes) -> bool:
146
169
  """Check if data exists in storage."""
170
+ if self.use_memory_storage:
171
+ return data_hash in self.memory_storage
147
172
  return (self.storage_path / data_hash.hex()).exists()
148
173
 
149
174
  def get_recursive(self, root_hash: bytes, max_depth: Optional[int] = None,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: astreum
3
- Version: 0.1.9
3
+ Version: 0.1.12
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
@@ -11,8 +11,8 @@ Classifier: Operating System :: OS Independent
11
11
  Requires-Python: >=3.8
12
12
  Description-Content-Type: text/markdown
13
13
  License-File: LICENSE
14
- Requires-Dist: pycryptodomex<4.0.0,>=3.14.1
15
- Requires-Dist: cryptography<40.0.0,>=39.0.0
14
+ Requires-Dist: pycryptodomex==3.21.0
15
+ Requires-Dist: cryptography==44.0.2
16
16
 
17
17
  # lib
18
18
 
@@ -17,11 +17,11 @@ astreum/lispeum/special/list/position.py,sha256=1EcD5fzfjUCUhHCk8iotFoWdeE9Z2Rjj
17
17
  astreum/lispeum/special/list/remove.py,sha256=EPhkWFwyKG8HyWPheVvtxCvMaEGvODFUbx7h1fli_hg,690
18
18
  astreum/lispeum/special/number/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
19
  astreum/lispeum/special/number/addition.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
- astreum/machine/__init__.py,sha256=5Sx-_MEYFG-089q2G8JtLp9_ELiPYnYIXhagjy6C4w0,11048
20
+ astreum/machine/__init__.py,sha256=GOdZl1tS9uIJHbq5WVcplifMDPDLQroX7CVew-K2YbA,15262
21
21
  astreum/machine/environment.py,sha256=K0084U6B7wwjrDZ9b2_7cEcbBzsB7UOy_Zpbrr7B3GY,834
22
22
  astreum/machine/error.py,sha256=MvqBaZZt33rNELNhUJ2lER3TE3aS8WVqsWF2hz2AwoA,38
23
- astreum/node/__init__.py,sha256=wvmMMACicwhJ-ipSLpR1rsEUXWuHaZ8fnsBCkUA_RA8,22387
24
- astreum/node/models.py,sha256=MScDCNEpR-SfaFsRSU567mVuZRhH1Lqc7gmlqb3j-nI,10980
23
+ astreum/node/__init__.py,sha256=EdGMa9pargAdB7KzjKBkhuFJ3G3wZF2VeIGkYFOCLws,22497
24
+ astreum/node/models.py,sha256=9Uf2_u55uxWG0ujjySvFJUO5Ub-EzlMnnMJWcgJHjHk,11980
25
25
  astreum/node/relay/__init__.py,sha256=gQNYDxllkjZqE6bvgPOYkKu3QHkjPFn11RvtP-BxtKI,13994
26
26
  astreum/node/relay/bucket.py,sha256=pcmollbbM-xeHlmDxLZnzvf0Ut-9v9RoN6SijYiQuu8,2893
27
27
  astreum/node/relay/envelope.py,sha256=TfkynttoPX7smvMV7xEAdtIlfz-Z-EZjuhZ826csZxA,10078
@@ -30,8 +30,8 @@ astreum/node/relay/peer.py,sha256=DlvTR9j0BZQ1dW-p_9UGgfLvQqwNdpNLMSCYEW4FhyI,58
30
30
  astreum/node/relay/route.py,sha256=fyOSsAe1mfsCVeN6LtQ_OEUEb1FiC5dobZBEJKNGU9U,5814
31
31
  astreum/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
32
32
  astreum/utils/bytes_format.py,sha256=X4tG5GGPweNCE54bHYkLFiuLTbmpy5upO_s1Cef-MGA,2711
33
- astreum-0.1.9.dist-info/LICENSE,sha256=gYBvRDP-cPLmTyJhvZ346QkrYW_eleke4Z2Yyyu43eQ,1089
34
- astreum-0.1.9.dist-info/METADATA,sha256=giKzIaeLdFUp_6AKqh1YxeS3epMjYpzlT8Dya6Gg3uY,2956
35
- astreum-0.1.9.dist-info/WHEEL,sha256=52BFRY2Up02UkjOa29eZOS2VxUrpPORXg1pkohGGUS8,91
36
- astreum-0.1.9.dist-info/top_level.txt,sha256=1EG1GmkOk3NPmUA98FZNdKouhRyget-KiFiMk0i2Uz0,8
37
- astreum-0.1.9.dist-info/RECORD,,
33
+ astreum-0.1.12.dist-info/LICENSE,sha256=gYBvRDP-cPLmTyJhvZ346QkrYW_eleke4Z2Yyyu43eQ,1089
34
+ astreum-0.1.12.dist-info/METADATA,sha256=LnGyI9YKLRNRWLolnyuHDgg0MKPSyLSwJho4FCTg1to,2942
35
+ astreum-0.1.12.dist-info/WHEEL,sha256=52BFRY2Up02UkjOa29eZOS2VxUrpPORXg1pkohGGUS8,91
36
+ astreum-0.1.12.dist-info/top_level.txt,sha256=1EG1GmkOk3NPmUA98FZNdKouhRyget-KiFiMk0i2Uz0,8
37
+ astreum-0.1.12.dist-info/RECORD,,