pythagoras 0.20.6__py3-none-any.whl → 0.20.8__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.
pythagoras/.DS_Store ADDED
Binary file
@@ -11,14 +11,11 @@ provides various supporting services that help manage the application's
11
11
  state and behavior.
12
12
 
13
13
  A Pythagoras-based application can have multiple portals.
14
- There is usually a current (default) portal, accessible via
15
- get_current_portal().
14
+ There is usually a current active portal, accessible via
15
+ get_active_portal().
16
16
 
17
- BasicPortal is a base class for all portal objects. It provides foundational
18
- functionality for managing the portal stack and for accessing the current
19
- portal. It keeps track of all portals created in the system and manages
20
- the stack of entered ('active') portals. It also provides a method to
21
- clear all portals and their state. The class is not intended to be used directly.
17
+ BasicPortal is a base class for all portal objects.
18
+ The class is not intended to be used directly.
22
19
  Instead, it should be subclassed to provide additional functionality.
23
20
 
24
21
  BasicPortal's subclasses are expected to provide access to
@@ -1,3 +1,10 @@
1
+ """This modukle provides foundational functionality for wotk with portals,
2
+ specifically for managing the portal stack and for accessing the current
3
+ portal. It keeps track of all portals created in the system and manages
4
+ the stack of entered ('active') portals. It also provides a method to
5
+ clear all portals and their state.
6
+ """
7
+
1
8
  from __future__ import annotations
2
9
 
3
10
  import random
@@ -84,7 +91,7 @@ def get_all_known_portals() -> list[BasicPortal]:
84
91
 
85
92
 
86
93
  def get_number_of_portals_in_active_stack() -> int:
87
- """Get the number of currently active portals."""
94
+ """Get the number of portals in a stack."""
88
95
  global _active_portals_stack
89
96
  return len(set(_active_portals_stack))
90
97
 
@@ -124,6 +131,7 @@ def get_active_portal() -> BasicPortal:
124
131
 
125
132
 
126
133
  def get_nonactive_portals() -> list[BasicPortal]:
134
+ """Get a list of all portals that are not in the active stack."""
127
135
  active_portal_str_id = get_active_portal()._str_id
128
136
  found_portals = []
129
137
  for portal_str_id, portal in _all_known_portals.items():
@@ -160,13 +168,9 @@ class BasicPortal(NotPicklable,ParameterizableClass, metaclass = PostInitMeta):
160
168
 
161
169
  A Pythagoras-based application can have multiple portals,
162
170
  and there is usually a current (default) portal, accessible via
163
- get_current_portal().
171
+ get_active_portal().
164
172
 
165
- BasicPortal is a base class for all portal objects. It provides foundational
166
- functionality for managing the portal stack and for accessing the current
167
- portal. It keeps track of all portals created in the system and manages
168
- the stack of entered ('active') portals. It also provides a method to
169
- clear all portals and their state.
173
+ BasicPortal is a base class for all portal objects.
170
174
 
171
175
  The class is not intended to be used directly. Instead, it should
172
176
  be subclassed to provide additional functionality.
@@ -175,12 +179,11 @@ class BasicPortal(NotPicklable,ParameterizableClass, metaclass = PostInitMeta):
175
179
  _entropy_infuser = random.Random()
176
180
 
177
181
  _root_dict: PersiDict | None
178
- _str_id_cache: PortalStrID | None
182
+ _str_id_cache: PortalStrID
179
183
 
180
184
 
181
185
  def __init__(self, root_dict:PersiDict|str|None = None):
182
186
  ParameterizableClass.__init__(self)
183
- self._str_id_cache = None
184
187
  if root_dict is None:
185
188
  root_dict = get_default_portal_base_dir()
186
189
  if not isinstance(root_dict, PersiDict):
@@ -193,6 +196,7 @@ class BasicPortal(NotPicklable,ParameterizableClass, metaclass = PostInitMeta):
193
196
 
194
197
 
195
198
  def _post_init_hook(self) -> None:
199
+ """This method is always called after all __init__() methods"""
196
200
  global _most_recently_created_portal, _all_known_portals
197
201
  _all_known_portals[self._str_id] = self
198
202
  _most_recently_created_portal = self
@@ -216,7 +220,6 @@ class BasicPortal(NotPicklable,ParameterizableClass, metaclass = PostInitMeta):
216
220
 
217
221
  def get_linked_objects(self, target_class: type | None = None) -> list[PortalAwareClass]:
218
222
  """Get the list of objects, linked to the portal"""
219
-
220
223
  found_linked_objects_ids = self._get_linked_objects_ids(target_class)
221
224
  found_linked_objects = []
222
225
  for obj_str_id in found_linked_objects_ids:
@@ -243,7 +246,11 @@ class BasicPortal(NotPicklable,ParameterizableClass, metaclass = PostInitMeta):
243
246
 
244
247
  @property
245
248
  def is_active(self) -> bool:
246
- """Check if the portal is the current one"""
249
+ """Check if the portal is the current one.
250
+
251
+ The 'active' portal is the innermost portal
252
+ in the stack of portal's 'with' statements.
253
+ """
247
254
  return (len(_active_portals_stack) > 0
248
255
  and _active_portals_stack[-1]._str_id == self._str_id)
249
256
 
@@ -257,28 +264,40 @@ class BasicPortal(NotPicklable,ParameterizableClass, metaclass = PostInitMeta):
257
264
 
258
265
  @property
259
266
  def _ephemeral_param_names(self) -> set[str]:
260
- """Get the names of the portal's ephemeral parameters"""
267
+ """Get the names of the portal's ephemeral parameters.
268
+
269
+ Portal's ephemeral parameters are not stored persistently.
270
+ They affect behaviour of a portal object in an application session,
271
+ but they do not affect behaviour of the actual portal across multiple runs.
272
+ """
261
273
  return set()
262
274
 
263
275
 
264
276
  @property
265
277
  def _str_id(self) -> PortalStrID:
266
- """Get the portal's persistent hash"""
267
- if hasattr(self,"_str_id_cache") and self._str_id_cache is not None:
268
- return self._str_id_cache
269
- else:
278
+ """Get the portal's persistent hash.
279
+
280
+ It's an internal hash used by Pythagoras and is different from .__hash__()
281
+ """
282
+ if not hasattr(self,"_str_id_cache"):
270
283
  params = self.get_portable_params()
271
284
  ephemeral_names = self._ephemeral_param_names
272
285
  nonephemeral_params = {k:params[k] for k in params
273
286
  if k not in ephemeral_names}
274
287
  self._str_id_cache = PortalStrID(
275
288
  get_hash_signature(nonephemeral_params))
276
- return self._str_id_cache
289
+ return self._str_id_cache
277
290
 
278
291
 
279
292
  def _invalidate_cache(self) -> None:
280
- """Invalidate object's caches"""
281
- self._str_id_cache = None
293
+ """Invalidate the object's attribute cache.
294
+
295
+ If the object's attribute named ATTR is cached,
296
+ its cached value will be stored in an attribute named _ATTR_cache
297
+ This method should delete all such attributes.
298
+ """
299
+ if hasattr(self, "_str_id_cache"):
300
+ del self._str_id_cache
282
301
 
283
302
 
284
303
  def describe(self) -> pd.DataFrame:
@@ -297,7 +316,7 @@ class BasicPortal(NotPicklable,ParameterizableClass, metaclass = PostInitMeta):
297
316
 
298
317
 
299
318
  def __enter__(self):
300
- """Set the portal as the current one"""
319
+ """Set the portal as the active one and add it to the stack"""
301
320
  global _active_portals_stack, _active_portals_counters_stack
302
321
  if (len(_active_portals_stack) == 0 or
303
322
  id(_active_portals_stack[-1]) != id(self)):
@@ -309,7 +328,7 @@ class BasicPortal(NotPicklable,ParameterizableClass, metaclass = PostInitMeta):
309
328
 
310
329
 
311
330
  def __exit__(self, exc_type, exc_val, exc_tb):
312
- """Remove the portal from the current context"""
331
+ """Pop the portal from the stack of active ones"""
313
332
  global _active_portals_stack, _active_portals_counters_stack
314
333
  assert _active_portals_stack[-1] == self, (
315
334
  "Inconsistent state of the portal stack. "
@@ -324,10 +343,9 @@ class BasicPortal(NotPicklable,ParameterizableClass, metaclass = PostInitMeta):
324
343
 
325
344
  def _clear(self) -> None:
326
345
  """Clear and invalidate the portal's state"""
346
+ self._invalidate_cache()
327
347
  self._root_dict = None
328
- self._str_id_cache = None
329
348
  self._entropy_infuser = None
330
- # self._linked_objects = None
331
349
 
332
350
 
333
351
  _all_activated_portal_aware_objects: dict[PObjectStrID, PortalAwareClass] = dict()
@@ -342,36 +360,25 @@ def get_number_of_linked_portal_aware_objects() -> int:
342
360
  class PortalAwareClass(metaclass = PostInitMeta):
343
361
  """A base class for objects that need to access a portal.
344
362
 
345
- The class enables functionality for saving and loading its objects.
346
- When a portal-aware object is saved (pickled), the portal data is not saved,
347
- and the object is pickled as if it were a regular object.
348
- After the object is unpickled, the portal is restored to the current portal.
349
-
350
- The "current" portal is the innermost portal
351
- in the stack of portal "with" statements. It means that
352
- a portal-aware object can only be unpickled from within a portal context.
353
-
354
- A portal-aware object accepts a portal as an input parameter
355
- for its constructor. It also supports late portal binding: it
356
- can be created with `portal=None`, and its portal will be set later
357
- to the current portal.
363
+ These objects either always work with a currently active portal,
364
+ or they have a preferred (linked) portal which they activate every
365
+ time their methods are called.
358
366
  """
359
367
 
360
368
  # _linked_portal: BasicPortal | None
361
369
  _linked_portal_at_init: BasicPortal|None
362
- _hash_id_cache: PObjectStrID | None
370
+ _hash_id_cache: PObjectStrID
363
371
  _visited_portals: set[str] | None
364
372
 
365
373
  def __init__(self, portal:BasicPortal|None=None):
366
374
  assert portal is None or isinstance(portal, BasicPortal)
367
- # self._linked_portal = portal
368
375
  self._linked_portal_at_init = portal
369
- self._hash_id_cache = None
376
+ # self._hash_id_cache = None
370
377
  self._visited_portals = set()
371
378
 
372
379
 
373
380
  def _post_init_hook(self):
374
- """ This method is called after the object is fully initialized."""
381
+ """ This method is called after all object's .__init__() methods."""
375
382
  global _all_links_from_objects_to_portals
376
383
  global _all_activated_portal_aware_objects
377
384
  if self._linked_portal_at_init is not None:
@@ -382,8 +389,19 @@ class PortalAwareClass(metaclass = PostInitMeta):
382
389
 
383
390
  @property
384
391
  def _linked_portal(self) -> BasicPortal | None:
392
+ """Get the portal's preferred (linked) portal.
393
+
394
+ If the linked portal is not None, the object will activate
395
+ this portal every time the object's methods are called.
396
+ If it's None, the object will never try to change the active portal.
397
+ """
385
398
  global _all_links_from_objects_to_portals, _all_known_portals
386
399
  portal = None
400
+ if self._linked_portal_at_init is not None and len(self._visited_portals)==0:
401
+ _all_links_from_objects_to_portals[self._str_id
402
+ ] = self._linked_portal_at_init._str_id
403
+ self._first_visit_to_portal(self._linked_portal_at_init)
404
+ portal = self._linked_portal_at_init
387
405
  if self._str_id in _all_links_from_objects_to_portals:
388
406
  portal_str_id = _all_links_from_objects_to_portals[self._str_id]
389
407
  assert isinstance(portal_str_id, str)
@@ -393,8 +411,11 @@ class PortalAwareClass(metaclass = PostInitMeta):
393
411
 
394
412
  @property
395
413
  def portal(self) -> BasicPortal:
396
- # if self._linked_portal is None:
397
- # self._try_to_find_linked_portal_and_register_there()
414
+ """Get the portal which the object's methods will be using.
415
+
416
+ It's either the object's linked portal or
417
+ (if the linked portal is None) the currently active portal.
418
+ """
398
419
  global _all_links_from_objects_to_portals, _all_known_portals
399
420
  portal_to_use = self._linked_portal
400
421
  if portal_to_use is None:
@@ -405,30 +426,21 @@ class PortalAwareClass(metaclass = PostInitMeta):
405
426
  return portal_to_use
406
427
 
407
428
  def _first_visit_to_portal(self, portal: BasicPortal) -> None:
429
+ """Register an object in a portal that the object has not seen before."""
408
430
  global _all_activated_portal_aware_objects
409
431
  _all_activated_portal_aware_objects[self._str_id] = self
410
432
  self._visited_portals.add(portal._str_id)
411
433
 
412
434
 
413
- # @portal.setter
414
- # def portal(self, new_portal: BasicPortal|None) -> None:
415
- # """Set the portal to the given one."""
416
- # assert new_portal is not None
417
- # assert not hasattr(self, "_linked_portal") or self._linked_portal is None
418
- # self._linked_portal = new_portal
419
- # self._try_to_find_linked_portal_and_register_there()
420
- # if new_portal._str_id not in self._visited_portals:
421
- # self._first_visit_to_portal(new_portal)
422
-
423
-
424
435
  @property
425
436
  def _str_id(self) -> PObjectStrID:
426
- """Return the hash ID of the portal-aware object."""
427
- if hasattr(self, "_str_id_cache") and self._hash_id_cache is not None:
428
- return self._hash_id_cache
429
- else:
437
+ """Return the hash ID of the portal-aware object.
438
+
439
+ It's an internal hash used by Pythagoras and is different from .__hash__()
440
+ """
441
+ if not hasattr(self, "_str_id_cache"):
430
442
  self._hash_id_cache = PObjectStrID(get_hash_signature(self))
431
- return self._hash_id_cache
443
+ return self._hash_id_cache
432
444
 
433
445
 
434
446
  @abstractmethod
@@ -445,19 +457,26 @@ class PortalAwareClass(metaclass = PostInitMeta):
445
457
 
446
458
  @abstractmethod
447
459
  def __setstate__(self, state):
448
- """This method is called when the object is unpickled.
449
-
450
- """
460
+ """This method is called when the object is unpickled."""
451
461
  self._invalidate_cache()
452
462
  self._visited_portals = set()
463
+ self._linked_portal_at_init = None
453
464
 
454
465
 
455
466
  def _invalidate_cache(self):
456
- self._hash_id_cache = None
467
+ """Invalidate the object's attribute cache.
468
+
469
+ If the object's attribute named ATTR is cached,
470
+ its cached value will be stored in an attribute named _ATTR_cache
471
+ This method should delete all such attributes.
472
+ """
473
+ if hasattr(self, "_hash_id_cache"):
474
+ del self._hash_id_cache
457
475
 
458
476
 
459
477
  @property
460
478
  def is_activated(self) -> bool:
479
+ """Return True if the object has been registered in at least one of the portals."""
461
480
  global _all_activated_portal_aware_objects
462
481
  if len(self._visited_portals) >=1:
463
482
  assert self._str_id in _all_activated_portal_aware_objects
@@ -466,6 +485,10 @@ class PortalAwareClass(metaclass = PostInitMeta):
466
485
 
467
486
 
468
487
  def _deactivate(self):
488
+ """Mark the object as not activated.
489
+
490
+ Empty the list of portals it has been registered into.
491
+ """
469
492
  global _all_activated_portal_aware_objects
470
493
  assert self.is_activated
471
494
  del _all_activated_portal_aware_objects[self._str_id]
@@ -473,8 +496,6 @@ class PortalAwareClass(metaclass = PostInitMeta):
473
496
  self._visited_portals = set()
474
497
 
475
498
 
476
-
477
-
478
499
  def _clear_all_portals() -> None:
479
500
  """Remove all information about all the portals from the system."""
480
501
  global _all_known_portals, _active_portals_stack
@@ -1,6 +1,8 @@
1
1
  class NotPicklable:
2
2
  def __getstate__(self):
3
+ """This method is called when the object is pickled."""
3
4
  raise TypeError(f"{type(self).__name__} cannot be pickled")
4
5
 
5
6
  def __setstate__(self, state):
7
+ """This method is called when the object is unpickled."""
6
8
  raise TypeError(f"{type(self).__name__} cannot be unpickled")
@@ -24,8 +24,10 @@ class ordinary:
24
24
 
25
25
 
26
26
  def __getstate__(self):
27
+ """This method is called when the object is pickled."""
27
28
  raise TypeError("Decorators cannot be pickled.")
28
29
 
29
30
 
30
31
  def __setstate__(self, state):
32
+ """This method is called when the object is unpickled."""
31
33
  raise TypeError("Decorators cannot be pickled.")
@@ -122,22 +122,23 @@ class OrdinaryFn(PortalAwareClass):
122
122
 
123
123
  @property
124
124
  def portal(self) -> OrdinaryCodePortal:
125
- return PortalAwareClass.portal.__get__(self)
125
+ """Get the portal which the function's methods will be using.
126
126
 
127
+ It's either the function's linked portal or
128
+ (if the linked portal is None) the currently active portal.
129
+ """
130
+ return super().portal
127
131
 
128
- # @portal.setter
129
- # def portal(self, new_portal: OrdinaryCodePortal) -> None:
130
- # if not isinstance(new_portal, OrdinaryCodePortal):
131
- # raise TypeError("portal must be a OrdinaryCodePortal instance")
132
- # PortalAwareClass.portal.__set__(self, new_portal)
133
132
 
134
133
  @property
135
134
  def source_code(self) -> str:
135
+ """Get the source code of the function."""
136
136
  return self._source_code
137
137
 
138
138
 
139
139
  @property
140
140
  def name(self) -> str:
141
+ """Get the name of the function."""
141
142
  if not hasattr(self, "_name_cache") or self._name_cache is None:
142
143
  self._name_cache = get_function_name_from_source(self.source_code)
143
144
  return self._name_cache
@@ -145,7 +146,8 @@ class OrdinaryFn(PortalAwareClass):
145
146
 
146
147
  @property
147
148
  def hash_signature(self):
148
- if not hasattr(self, "_hash_signature_cache") or self._hash_signature_cache is None:
149
+ """Get the hash signature of the function."""
150
+ if not hasattr(self, "_hash_signature_cache"):
149
151
  self._hash_signature_cache = get_hash_signature(self)
150
152
  return self._hash_signature_cache
151
153
 
@@ -196,6 +198,12 @@ class OrdinaryFn(PortalAwareClass):
196
198
 
197
199
 
198
200
  def _invalidate_cache(self):
201
+ """Invalidate the object's attribute cache.
202
+
203
+ If the object's attribute named ATTR is cached,
204
+ its cached value will be stored in an attribute named _ATTR_cache
205
+ This method should delete all such attributes.
206
+ """
199
207
  if hasattr(self, "_compiled_code_cache"):
200
208
  del self._compiled_code_cache
201
209
  if hasattr(self, "_tmp_fn_name_cache"):
@@ -245,11 +253,13 @@ class OrdinaryFn(PortalAwareClass):
245
253
 
246
254
 
247
255
  def __getstate__(self):
256
+ """This method is called when the object is pickled."""
248
257
  state = dict(source_code=self._source_code)
249
258
  return state
250
259
 
251
260
 
252
261
  def __setstate__(self, state):
262
+ """This method is called when the object is unpickled."""
253
263
  PortalAwareClass.__setstate__(self, state)
254
264
  self._source_code = state["source_code"]
255
265
 
@@ -249,24 +249,32 @@ class StorableFn(OrdinaryFn):
249
249
 
250
250
  @property
251
251
  def addr(self) -> ValueAddr:
252
- with self.portal:
253
- if not hasattr(self, "_addr_cache") or self._addr_cache is None:
252
+ if not hasattr(self, "_addr_cache"):
253
+ with self.portal:
254
254
  self._addr_cache = ValueAddr(self)
255
- return self._addr_cache
255
+ return self._addr_cache
256
256
 
257
257
 
258
258
  def _invalidate_cache(self):
259
+ """Invalidate the object's attribute cache.
260
+
261
+ If the object's attribute named ATTR is cached,
262
+ its cached value will be stored in an attribute named _ATTR_cache
263
+ This method should delete all such attributes.
264
+ """
259
265
  if hasattr(self, "_addr_cache"):
260
266
  del self._addr_cache
261
267
  super()._invalidate_cache()
262
268
 
263
269
 
264
270
  def __setstate__(self, state):
271
+ """This method is called when the object is unpickled."""
265
272
  super().__setstate__(state)
266
273
  self._ephemeral_config_params_at_init = dict()
267
274
 
268
275
 
269
276
  def __getstate__(self):
277
+ """This method is called when the object is pickled."""
270
278
  return super().__getstate__()
271
279
 
272
280
 
@@ -382,6 +390,12 @@ class HashAddr(SafeStrTuple):
382
390
 
383
391
 
384
392
  def _invalidate_cache(self):
393
+ """Invalidate the object's attribute cache.
394
+
395
+ If the object's attribute named ATTR is cached,
396
+ its cached value will be stored in an attribute named _ATTR_cache
397
+ This method should delete all such attributes.
398
+ """
385
399
  pass
386
400
 
387
401
 
@@ -397,10 +411,11 @@ class ValueAddr(HashAddr):
397
411
  It makes it easier for humans to interpret an address,
398
412
  and further decreases collision risk.
399
413
  """
400
- _containing_portals_cache: set[str]
414
+ _containing_portals: set[str]
401
415
  _value_cache: Any
402
416
 
403
417
  def __init__(self, data: Any, store: bool = True):
418
+ self._containing_portals = set()
404
419
 
405
420
  if hasattr(data, "get_ValueAddr"):
406
421
  data_value_addr = data.get_ValueAddr()
@@ -422,20 +437,24 @@ class ValueAddr(HashAddr):
422
437
  , hash_signature=hash_signature)
423
438
 
424
439
  self._value_cache = data
425
- self._containing_portals_cache = set()
426
440
 
427
441
  if store:
428
442
  portal = get_active_data_portal()
429
443
  portal._value_store[self] = data
430
- self._containing_portals_cache.add(portal._str_id)
444
+ self._containing_portals.add(portal._str_id)
431
445
 
432
446
 
433
447
  def _invalidate_cache(self):
434
- super()._invalidate_cache()
448
+ """Invalidate the object's attribute cache.
449
+
450
+ If the object's attribute named ATTR is cached,
451
+ its cached value will be stored in an attribute named _ATTR_cache
452
+ This method should delete all such attributes.
453
+ """
435
454
  if hasattr(self, "_value_cache"):
436
455
  del self._value_cache
437
- if hasattr(self, "_containing_portals_cache"):
438
- del self._containing_portals_cache
456
+ self._containing_portals = set()
457
+ super()._invalidate_cache()
439
458
 
440
459
 
441
460
  def get_ValueAddr(self):
@@ -446,11 +465,11 @@ class ValueAddr(HashAddr):
446
465
  def _ready_in_active_portal(self) -> bool:
447
466
  portal = get_active_data_portal()
448
467
  portal_id = portal._str_id
449
- if portal_id in self._containing_portals_cache:
468
+ if portal_id in self._containing_portals:
450
469
  return True
451
470
  result = self in portal._value_store
452
471
  if result:
453
- self._containing_portals_cache.add(portal_id)
472
+ self._containing_portals.add(portal_id)
454
473
  return result
455
474
 
456
475
 
@@ -461,7 +480,7 @@ class ValueAddr(HashAddr):
461
480
  value = portal._value_store[self]
462
481
  get_active_data_portal()._value_store[self] = value
463
482
  new_ids = {portal._str_id, get_active_portal()._str_id}
464
- self._containing_portals_cache |= new_ids
483
+ self._containing_portals |= new_ids
465
484
  self._value_cache = value
466
485
  return True
467
486
  return False
@@ -470,9 +489,6 @@ class ValueAddr(HashAddr):
470
489
  @property
471
490
  def ready(self) -> bool:
472
491
  """Check if address points to a value that is ready to be retrieved."""
473
- if not hasattr(self, "_containing_portals_cache"):
474
- self._containing_portals_cache = set()
475
-
476
492
  if self._ready_in_active_portal:
477
493
  return True
478
494
  if self._ready_in_nonactive_portals:
@@ -484,16 +500,16 @@ class ValueAddr(HashAddr):
484
500
  """Retrieve value, referenced by the address, from the current portal"""
485
501
 
486
502
  if hasattr(self, "_value_cache"):
487
- if get_active_portal()._str_id in self._containing_portals_cache:
503
+ if get_active_portal()._str_id in self._containing_portals:
488
504
  return self._value_cache
489
505
  else:
490
506
  get_active_data_portal()._value_store[self] = self._value_cache
491
- self._containing_portals_cache |= {get_active_portal()._str_id}
507
+ self._containing_portals |= {get_active_portal()._str_id}
492
508
  return self._value_cache
493
509
 
494
510
  value = get_active_data_portal()._value_store[self]
495
511
  self._value_cache = value
496
- self._containing_portals_cache |= {get_active_portal()._str_id}
512
+ self._containing_portals |= {get_active_portal()._str_id}
497
513
  return value
498
514
 
499
515
 
@@ -506,7 +522,7 @@ class ValueAddr(HashAddr):
506
522
  get_active_data_portal()._value_store[self] = value
507
523
  self._value_cache = value
508
524
  new_ids = {portal._str_id, get_active_portal()._str_id}
509
- self._containing_portals_cache |= new_ids
525
+ self._containing_portals |= new_ids
510
526
  return value
511
527
  except:
512
528
  continue
@@ -520,9 +536,6 @@ class ValueAddr(HashAddr):
520
536
  ) -> T:
521
537
  """Retrieve value, referenced by the address from any available portal"""
522
538
 
523
- if not hasattr(self, "_containing_portals_cache"):
524
- self._containing_portals_cache = set()
525
-
526
539
  try:
527
540
  result = self._get_from_active_portal()
528
541
  except:
@@ -536,10 +549,29 @@ class ValueAddr(HashAddr):
536
549
 
537
550
 
538
551
  def __getstate__(self):
552
+ """This method is called when the object is pickled."""
539
553
  state = dict(strings=self.strings)
540
554
  return state
541
555
 
542
556
 
543
557
  def __setstate__(self, state):
558
+ """This method is called when the object is unpickled."""
544
559
  self._invalidate_cache()
545
- self.strings = state["strings"]
560
+ self.strings = state["strings"]
561
+ self._containing_portals = set()
562
+
563
+
564
+ @classmethod
565
+ def from_strings(cls, *
566
+ , prefix: str
567
+ , hash_signature: str
568
+ , assert_readiness: bool = True
569
+ ) -> HashAddr:
570
+ """(Re)construct address from text representations of prefix and hash"""
571
+
572
+ address = super().from_strings(prefix=prefix, hash_signature=hash_signature, assert_readiness=False)
573
+ address._containing_portals = set()
574
+ if assert_readiness:
575
+ if not address.ready:
576
+ raise ValueError("Address is not ready for retrieving data")
577
+ return address
@@ -111,6 +111,7 @@ class LoggingFnCallSignature:
111
111
 
112
112
 
113
113
  def __getstate__(self):
114
+ """This method is called when the object is pickled."""
114
115
  state = dict(
115
116
  _fn_addr=self._fn_addr
116
117
  , _kwargs_addr=self._kwargs_addr)
@@ -118,12 +119,19 @@ class LoggingFnCallSignature:
118
119
 
119
120
 
120
121
  def __setstate__(self, state):
122
+ """This method is called when the object is unpickled."""
121
123
  self._invalidate_cache()
122
124
  self._fn_addr = state["_fn_addr"]
123
125
  self._kwargs_addr = state["_kwargs_addr"]
124
126
 
125
127
 
126
128
  def _invalidate_cache(self):
129
+ """Invalidate the object's attribute cache.
130
+
131
+ If the object's attribute named ATTR is cached,
132
+ its cached value will be stored in an attribute named _ATTR_cache
133
+ This method should delete all such attributes.
134
+ """
127
135
  if hasattr(self, "_fn_cache"):
128
136
  del self._fn_cache
129
137
  if hasattr(self, "_fn_name_cache"):
@@ -34,11 +34,13 @@ class SafeFn(LoggingFn):
34
34
 
35
35
 
36
36
  def __getstate__(self):
37
+ """This method is called when the object is pickled."""
37
38
  state = super().__getstate__()
38
39
  return state
39
40
 
40
41
 
41
42
  def __setstate__(self, state):
43
+ """This method is called when the object is unpickled."""
42
44
  super().__setstate__(state)
43
45
 
44
46
 
@@ -104,11 +104,13 @@ class AutonomousFn(SafeFn):
104
104
 
105
105
 
106
106
  def __getstate__(self):
107
+ """This method is called when the object is pickled."""
107
108
  state = super().__getstate__()
108
109
  state["_fixed_kwargs"] = self._fixed_kwargs
109
110
  return state
110
111
 
111
112
  def __setstate__(self, state):
113
+ """This method is called when the object is unpickled."""
112
114
  super().__setstate__(state)
113
115
  self._fixed_kwargs = state["_fixed_kwargs"]
114
116
 
@@ -66,6 +66,7 @@ class ProtectedFn(AutonomousFn):
66
66
 
67
67
 
68
68
  def __getstate__(self):
69
+ """This method is called when the object is pickled."""
69
70
  state = super().__getstate__()
70
71
  state["guards_addrs"] = self._guards_addrs
71
72
  state["validators_addrs"] = self._validators_addrs
@@ -73,6 +74,7 @@ class ProtectedFn(AutonomousFn):
73
74
 
74
75
 
75
76
  def __setstate__(self, state):
77
+ """This method is called when the object is unpickled."""
76
78
  self._invalidate_cache()
77
79
  super().__setstate__(state)
78
80
  self._guards_addrs = state["guards_addrs"]
@@ -271,6 +271,12 @@ class PureFnExecutionResultAddr(HashAddr):
271
271
 
272
272
 
273
273
  def _invalidate_cache(self):
274
+ """Invalidate the object's attribute cache.
275
+
276
+ If the object's attribute named ATTR is cached,
277
+ its cached value will be stored in an attribute named _ATTR_cache
278
+ This method should delete all such attributes.
279
+ """
274
280
  if hasattr(self, "_ready_cache"):
275
281
  del self._ready_cache
276
282
  if hasattr(self, "_result_cache"):
@@ -317,12 +323,14 @@ class PureFnExecutionResultAddr(HashAddr):
317
323
  return self._kwargs_cache
318
324
 
319
325
 
320
- def __setstate__(self, state): #*#*#
326
+ def __setstate__(self, state):
327
+ """This method is called when the object is unpickled."""
321
328
  self._invalidate_cache()
322
329
  self.strings = state["strings"]
323
330
 
324
331
 
325
- def __getstate__(self): #*#*#
332
+ def __getstate__(self):
333
+ """This method is called when the object is pickled."""
326
334
  state = dict(strings=self.strings)
327
335
  return state
328
336
 
@@ -14,7 +14,7 @@ from .._010_basic_portals import get_number_of_known_portals, get_all_known_port
14
14
  from .._010_basic_portals import BasicPortal
15
15
  from .system_utils import (
16
16
  get_current_process_id, process_is_active,
17
- get_process_start_time, get_current_process_start_time)
17
+ get_process_start_time, get_current_process_start_time, get_available_cpu_cores, get_available_ram_mb)
18
18
  from .._040_logging_code_portals.logging_portal_core_classes import build_execution_environment_summary
19
19
  from .._010_basic_portals.basic_portal_core_classes import _describe_runtime_characteristic
20
20
  from .._800_signatures_and_converters.random_signatures import get_random_signature
@@ -179,6 +179,8 @@ parameterizable.register_parameterizable_class(SwarmingPortal)
179
179
  def _launch_many_background_workers(**portal_init_params) -> None:
180
180
  """Launch many background worker processes."""
181
181
  n_workers_to_launch = portal_init_params["max_n_workers"]
182
+ n_workers_to_launch = min(n_workers_to_launch, get_available_cpu_cores())
183
+ n_workers_to_launch = min(n_workers_to_launch, get_available_ram_mb()/500)
182
184
  portal_init_params["max_n_workers"] = 0
183
185
  current_process_id = get_current_process_id()
184
186
  portal_init_params["parent_process_id"] = current_process_id
@@ -194,6 +196,7 @@ def _launch_many_background_workers(**portal_init_params) -> None:
194
196
 
195
197
  list_of_all_workers = []
196
198
 
199
+ n_workers_to_launch = int(n_workers_to_launch)
197
200
  with portal:
198
201
  for i in range(n_workers_to_launch):
199
202
  portal._randomly_delay_execution(p=1)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: pythagoras
3
- Version: 0.20.6
3
+ Version: 0.20.8
4
4
  Summary: Planet-scale distributed computing in Python.
5
5
  Keywords: cloud,ML,AI,serverless,distributed,parallel,machine-learning,deep-learning,pythagoras
6
6
  Author: Volodymyr (Vlad) Pavlov
@@ -1,17 +1,18 @@
1
- pythagoras/_010_basic_portals/__init__.py,sha256=07fc891782185c5be5e3d5d405de42bb4789bbfc49b06e4e7b3089af9592623a,1983
2
- pythagoras/_010_basic_portals/basic_portal_core_classes.py,sha256=86da2d29f7d6c43d17de6defe6cdc9a1cc5461cc0baf927a283b828f1b3d6068,18017
1
+ pythagoras/.DS_Store,sha256=40d35c2d6b7fca6728a8be89c610686ceb83b21e32d6fe0ddedcf1b3f58bfdaf,6148
2
+ pythagoras/_010_basic_portals/__init__.py,sha256=111d2457fb61ee16dae275d78e83fe8e837e72a9143281f889148280a50fef96,1703
3
+ pythagoras/_010_basic_portals/basic_portal_core_classes.py,sha256=1cc8fc86d8be58283a93b7707a63e27228b012f07601916f5eb851bc66d4db26,18995
3
4
  pythagoras/_010_basic_portals/exceptions.py,sha256=a15bab585769159db1bedf123bc92e920695f14faa3ad1f4ab0f863b7742b391,237
4
5
  pythagoras/_010_basic_portals/long_infoname.py,sha256=9742b2bf023389704c88628a8ed81d1c82ad6eb15a0a27c9cddadbfae1e9cf2d,650
5
- pythagoras/_010_basic_portals/not_picklable_class.py,sha256=6c9374e4e54c7b7532b6f9b95f47773a898064a73340839dc50ef3ddf7b1a0c7,222
6
+ pythagoras/_010_basic_portals/not_picklable_class.py,sha256=2f7405a32c530a68fae64c5e06731b69c44dea1fc0611e72284d5b13dffd77c0,352
6
7
  pythagoras/_010_basic_portals/portal_tester.py,sha256=cefdbf60bfc7697934ef8fd653303ac5295014884af780351baf84d859ec6de0,1817
7
8
  pythagoras/_010_basic_portals/post_init_metaclass.py,sha256=6dbe6856e4e46627bc0ae64d4220274b1147d4d2a74f6440f2454f8a93485c7e,583
8
9
  pythagoras/_020_ordinary_code_portals/__init__.py,sha256=a77912a9a4188f4c65864f41c29b45800d5449d4db7c790007943d307f80348d,1295
9
10
  pythagoras/_020_ordinary_code_portals/code_normalizer.py,sha256=2e2e4273add19a526273756ec8d8d0f33ea8aa0ef2cd15e0ce8c4c2b0d15f928,4869
10
11
  pythagoras/_020_ordinary_code_portals/function_processing.py,sha256=ae5841d1da5a533b6fe92336c2551d6947444dc576a55508098b9f56b40245c7,3623
11
- pythagoras/_020_ordinary_code_portals/ordinary_decorator.py,sha256=078c2e6e194ffab6bc8477b15b72198ccb34b57a935813ba2318ccf2d5c01a28,898
12
- pythagoras/_020_ordinary_code_portals/ordinary_portal_core_classes.py,sha256=bfe25e1911c9859c369a6dfeea8c8126c2b04058ad7b711994a99532e5c11b54,9182
12
+ pythagoras/_020_ordinary_code_portals/ordinary_decorator.py,sha256=278ab1d37344120a585af8380fc52400bd0f76d02ddac43230dd68a7a2cc16c0,1028
13
+ pythagoras/_020_ordinary_code_portals/ordinary_portal_core_classes.py,sha256=77b41aaeaa6f0de8bfa3fff503f426c632de3844cc16a6f6b5cc89e8e420fec5,9568
13
14
  pythagoras/_030_data_portals/__init__.py,sha256=8edbde910ded4fa56091492d7f8443d0980adedcab25f4b9308cfed05074c7f2,1237
14
- pythagoras/_030_data_portals/data_portal_core_classes.py,sha256=50bcbd71705be6287573ba7b2f12a1523bc97976608537d71da31befe27196aa,18492
15
+ pythagoras/_030_data_portals/data_portal_core_classes.py,sha256=4b156f7a3d958236e0d912799846b0afbb855891c64b1c0dffbf591e951330e1,19780
15
16
  pythagoras/_030_data_portals/ready_and_get.py,sha256=d87d16a6747239f4ed6c30c2be95eab9ae58bc5b2742f0fd924fd2a3ace71382,1571
16
17
  pythagoras/_030_data_portals/storable_decorator.py,sha256=97c5b71a156622c7208e80931aaded99d7a12860cb2159c56d333e196fb51b88,578
17
18
  pythagoras/_040_logging_code_portals/__init__.py,sha256=62ff68021e955c0a96ddd89168a3db25a8f20b3384d5e1ca10df7d9a24c96837,1251
@@ -19,16 +20,16 @@ pythagoras/_040_logging_code_portals/exception_processing_tracking.py,sha256=cb2
19
20
  pythagoras/_040_logging_code_portals/execution_environment_summary.py,sha256=853bd36da75802d8ac6781fcaeafa7fe1b0a3c24b7f23b12cfc970fc0d4322a6,2268
20
21
  pythagoras/_040_logging_code_portals/kw_args.py,sha256=096d299e99f33dea880e4c2b7ff9cef1081f0b9d3b4a6d649df6464a2a87db94,2435
21
22
  pythagoras/_040_logging_code_portals/logging_decorator.py,sha256=d3bf70dbf6791e15c52eb8325d07a48ee39bcbd09d5201541916d14f9969ba35,891
22
- pythagoras/_040_logging_code_portals/logging_portal_core_classes.py,sha256=6e637908139eb2b07e8bc88c27506bc1baf32a9cb043f1f034c605b3dbe41497,21585
23
+ pythagoras/_040_logging_code_portals/logging_portal_core_classes.py,sha256=2b87e89ee1f9ff1495dc81bedf9b7d9b9a85372c7e197a276fdc9a93a68ea11b,21965
23
24
  pythagoras/_040_logging_code_portals/notebook_checker.py,sha256=e654090c80f38512114a65f54fcf2701110c10ca030c57f438829cbd3a678739,541
24
25
  pythagoras/_040_logging_code_portals/output_capturer.py,sha256=a210a9eaaab12fb22e2467e716e088823e4e7381e60bc73bb99184feecd69199,4216
25
26
  pythagoras/_040_logging_code_portals/uncaught_exceptions.py,sha256=0776cfbd7e679c9271e098cb486765d715ce054da9aa8dc23fe32898901f5da1,3121
26
27
  pythagoras/_050_safe_code_portals/__init__.py,sha256=d514c7aa066848a9b99c32fa7f8d5d8e5aef6e826f2a5fb1ff8a0bf338753c98,69
27
28
  pythagoras/_050_safe_code_portals/safe_decorator.py,sha256=085bb05724d02a3ff4f51708802c2463c4184654b5d008724f7f2b2d0922519c,805
28
- pythagoras/_050_safe_code_portals/safe_portal_core_classes.py,sha256=517b5252c55433681433fceec6a2141486b0bc453797119bedf2fc37e408ae30,1678
29
+ pythagoras/_050_safe_code_portals/safe_portal_core_classes.py,sha256=bb37bd2515f7982829308265951e4576aed11517b1c8c76de75ef726f96c3ea4,1808
29
30
  pythagoras/_060_autonomous_code_portals/__init__.py,sha256=6c19fffb24a93aef9587f0abe9ef24732ef5971c94e9a0fa2c36f4d7a3664e35,2232
30
31
  pythagoras/_060_autonomous_code_portals/autonomous_decorators.py,sha256=c315b37cbc30e3929f18e29bda409aad39b6d5d840dcdb8b70c737817d346af6,3947
31
- pythagoras/_060_autonomous_code_portals/autonomous_portal_core_classes.py,sha256=f0ba893c6bbc9a5557ff9f5d204a5c8e88ff499d2e8edb51daab4302bae7ea14,4406
32
+ pythagoras/_060_autonomous_code_portals/autonomous_portal_core_classes.py,sha256=99544fb312a5be004e4bfea4f4c53b577d6659a7cff9e95a906609dedf0c6cfb,4536
32
33
  pythagoras/_060_autonomous_code_portals/names_usage_analyzer.py,sha256=1e0e84854ac630b204878fecde48a16739fd2663709fcee2ec0a8844f4fd4f13,7470
33
34
  pythagoras/_070_protected_code_portals/GPU_guards.py,sha256=75a11da44c802486bc6f65640aa48a730f0f684c5c07a42ba3cd1735eb3fb070,2
34
35
  pythagoras/_070_protected_code_portals/OK_const.py,sha256=17ea314496f44b422f9936585c3bb3749c960bc779b4f74962ec04e3fa4d2494,186
@@ -38,14 +39,14 @@ pythagoras/_070_protected_code_portals/fn_arg_names_checker.py,sha256=2000ba3cab
38
39
  pythagoras/_070_protected_code_portals/list_flattener.py,sha256=9a54b512ad9dc201db348e7908f5ca5f36d171ae3da433511d38023a6ba8d4b5,331
39
40
  pythagoras/_070_protected_code_portals/package_manager.py,sha256=30adf8d75f55b9b2c587dc95f4aa1c2154fa9354d668af6f0d586bb42f0d5b17,2008
40
41
  pythagoras/_070_protected_code_portals/protected_decorators.py,sha256=9cede3734b0235e15a9534fcf393454f9107073f90cf9bd5a532696d277e35da,1412
41
- pythagoras/_070_protected_code_portals/protected_portal_core_classes.py,sha256=58e66f14598a23d6d4bfa4dd3d510971ce272a11ea73a22d11b72487df112968,6616
42
+ pythagoras/_070_protected_code_portals/protected_portal_core_classes.py,sha256=9bd9edf3dfa45edb355f70332be77a4290d023b78d4b9804d67a0561d296a8ac,6746
42
43
  pythagoras/_070_protected_code_portals/python_packages_guards.py,sha256=a4ff97c250cbc94ad379edcab510fe10d4d590de9733bbff437bed71c025cecb,1025
43
44
  pythagoras/_080_pure_code_portals/__init__.py,sha256=8ed33435bb03d96675537fc6a696625332f971698d56c6b22b5ca1bc9d1b4274,1856
44
- pythagoras/_080_pure_code_portals/pure_core_classes.py,sha256=82617b2118b49c5fa95e7061396021b3851fdc47197aa00f43c98d05cb738145,18538
45
+ pythagoras/_080_pure_code_portals/pure_core_classes.py,sha256=73167f9aab4c91f5a07d6ac047e72ed81f81fe00ca3b96ca8b2085d4a0c40f9b,18906
45
46
  pythagoras/_080_pure_code_portals/pure_decorator.py,sha256=d908ad5eae50e59eeab32f738f784605708e00de71dd53dcbf25efecb83d110d,1168
46
47
  pythagoras/_090_swarming_portals/__init__.py,sha256=7041578f84ffa291f2752c7a2168007b9113f99482f0173f3729171b3bff551a,32
47
48
  pythagoras/_090_swarming_portals/output_suppressor.py,sha256=83e6cc9bcc62a226babb1165912ef5095ea948499ce5136a7516ac8b54522607,626
48
- pythagoras/_090_swarming_portals/swarming_portals.py,sha256=4616f309e169e7d0cc874f2659db085d3b4ca4acb9b788502cbda3fd6712782c,11378
49
+ pythagoras/_090_swarming_portals/swarming_portals.py,sha256=cb5240d0a070571a1b58d66fe3013c5d3123886ce4b266aeb35e95cc28d033fa,11633
49
50
  pythagoras/_090_swarming_portals/system_utils.py,sha256=d125a0b705b9f8477712179c49441daaf2cd2b0b0d4494b6ac977b2df87b50b5,1370
50
51
  pythagoras/_100_top_level_API/__init__.py,sha256=edac726311978503953fe4c37c475c7d616708236880f393b63894b9ab6251e5,95
51
52
  pythagoras/_100_top_level_API/default_local_portal.py,sha256=cfbe20499fed2f038b507b44fb58bb4cb6ea2fbe2fe93a3ab5ad7f3ac655005f,215
@@ -60,6 +61,6 @@ pythagoras/_900_project_stats_collector/__init__.py,sha256=e3b0c44298fc1c149afbf
60
61
  pythagoras/_900_project_stats_collector/project_analyzer.py,sha256=d06e9d7b516cb7424ef777e70abe9d5220e09b0b19476326b8974b4dc3917f89,3506
61
62
  pythagoras/__init__.py,sha256=94303c01a7bde4078fdbd90c0b142807a023fa352c473c3a544a2180b7254ae9,1062
62
63
  pythagoras/core/__init__.py,sha256=26640880921763e8801b74da28fccf1cd47ac0ba0d40ee8fcaf7d748410406bf,189
63
- pythagoras-0.20.6.dist-info/WHEEL,sha256=7de84e261f5edc1201bd668234a371ec3eb94db037d1f75c0139430c1319ab31,79
64
- pythagoras-0.20.6.dist-info/METADATA,sha256=21a2c9e6d12a3e19cd688aa123d03ffaa01942b485415e0a64149f33a25aaa43,4177
65
- pythagoras-0.20.6.dist-info/RECORD,,
64
+ pythagoras-0.20.8.dist-info/WHEEL,sha256=1f21b63a61110964f543b6041e8b7da078f20c09d500a32cb79eee5c3d655f54,79
65
+ pythagoras-0.20.8.dist-info/METADATA,sha256=da94d8c4b1a7c7173f25b935fb1f941baf58b6c9cbbc500add1e91854fba1b24,4177
66
+ pythagoras-0.20.8.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: uv 0.7.16
2
+ Generator: uv 0.7.17
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any