pythagoras 0.23.18__py3-none-any.whl → 0.24.0__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/_010_basic_portals/basic_portal_core_classes.py +149 -24
- pythagoras/_010_basic_portals/exceptions.py +28 -0
- pythagoras/_010_basic_portals/long_infoname.py +17 -2
- pythagoras/_010_basic_portals/not_picklable_class.py +30 -2
- pythagoras/_010_basic_portals/portal_tester.py +41 -6
- pythagoras/_010_basic_portals/post_init_metaclass.py +21 -0
- pythagoras/_030_data_portals/data_portal_core_classes.py +2 -2
- pythagoras/_040_logging_code_portals/logging_portal_core_classes.py +6 -6
- pythagoras/_080_pure_code_portals/pure_core_classes.py +2 -2
- pythagoras/_090_swarming_portals/swarming_portals.py +2 -2
- {pythagoras-0.23.18.dist-info → pythagoras-0.24.0.dist-info}/METADATA +4 -4
- {pythagoras-0.23.18.dist-info → pythagoras-0.24.0.dist-info}/RECORD +13 -13
- {pythagoras-0.23.18.dist-info → pythagoras-0.24.0.dist-info}/WHEEL +1 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
"""This
|
|
2
|
-
|
|
1
|
+
"""This module provides foundational functionality for work with portals.
|
|
2
|
+
|
|
3
|
+
This module specifically handles portal stack management and provides access to the current
|
|
3
4
|
portal. It keeps track of all portals created in the system and manages
|
|
4
5
|
the stack of entered ('active') portals. It also provides a method to
|
|
5
6
|
clear all portals and their state.
|
|
@@ -7,7 +8,6 @@ clear all portals and their state.
|
|
|
7
8
|
|
|
8
9
|
from __future__ import annotations
|
|
9
10
|
|
|
10
|
-
import collections
|
|
11
11
|
import random
|
|
12
12
|
import sys
|
|
13
13
|
from abc import abstractmethod
|
|
@@ -27,6 +27,14 @@ def _describe_persistent_characteristic(name, value) -> pd.DataFrame:
|
|
|
27
27
|
A helper function used by the describe() method of BasicPortal
|
|
28
28
|
and its subclasses. It creates a DataFrame with a single row
|
|
29
29
|
containing the type, name, and value of the characteristic.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
name: The name of the characteristic.
|
|
33
|
+
value: The value of the characteristic.
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
A pandas DataFrame with columns 'type', 'name', and 'value',
|
|
37
|
+
containing a single row with type="Disk".
|
|
30
38
|
"""
|
|
31
39
|
d = dict(
|
|
32
40
|
type="Disk"
|
|
@@ -41,6 +49,14 @@ def _describe_runtime_characteristic(name, value) -> pd.DataFrame:
|
|
|
41
49
|
A helper function used by the describe() method of BasicPortal
|
|
42
50
|
and its subclasses. It creates a DataFrame with a single row
|
|
43
51
|
containing the type, name, and value of the characteristic.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
name: The name of the characteristic.
|
|
55
|
+
value: The value of the characteristic.
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
A pandas DataFrame with columns 'type', 'name', and 'value',
|
|
59
|
+
containing a single row with type="Runtime".
|
|
44
60
|
"""
|
|
45
61
|
d = dict(
|
|
46
62
|
type = "Runtime"
|
|
@@ -54,16 +70,16 @@ BACKEND_TYPE_TXT = "Backend type"
|
|
|
54
70
|
|
|
55
71
|
|
|
56
72
|
def _get_description_value_by_key(dataframe:pd.DataFrame, key:str) -> Any:
|
|
57
|
-
"""
|
|
58
|
-
Retrieves the value corresponding to a given key from a DataFrame.
|
|
73
|
+
"""Retrieve the value corresponding to a given key from a DataFrame.
|
|
59
74
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
75
|
+
Args:
|
|
76
|
+
dataframe: The input DataFrame with 4 columns.
|
|
77
|
+
It is supposed to be an output of portal.describe() method.
|
|
78
|
+
key: The key to search for in the third column of the DataFrame.
|
|
63
79
|
|
|
64
80
|
Returns:
|
|
65
|
-
|
|
66
|
-
|
|
81
|
+
The value from the fourth column corresponding to the key.
|
|
82
|
+
Returns None if the key is not found.
|
|
67
83
|
"""
|
|
68
84
|
# Check if the key exists in the 3rd column
|
|
69
85
|
result = dataframe.loc[dataframe.iloc[:, 1] == key]
|
|
@@ -80,31 +96,51 @@ _active_portals_counters_stack: list = [int]
|
|
|
80
96
|
_most_recently_created_portal: BasicPortal | None = None
|
|
81
97
|
|
|
82
98
|
def get_number_of_known_portals() -> int:
|
|
83
|
-
"""Get the number of portals currently in the system.
|
|
99
|
+
"""Get the number of portals currently in the system.
|
|
100
|
+
|
|
101
|
+
Returns:
|
|
102
|
+
The total count of all known portals in the system.
|
|
103
|
+
"""
|
|
84
104
|
global _all_known_portals
|
|
85
105
|
return len(_all_known_portals)
|
|
86
106
|
|
|
87
107
|
|
|
88
108
|
def get_all_known_portals() -> list[BasicPortal]:
|
|
89
|
-
"""Get a list of all known portals.
|
|
109
|
+
"""Get a list of all known portals.
|
|
110
|
+
|
|
111
|
+
Returns:
|
|
112
|
+
A list containing all portal instances currently known to the system.
|
|
113
|
+
"""
|
|
90
114
|
global _all_known_portals
|
|
91
115
|
return list(_all_known_portals.values())
|
|
92
116
|
|
|
93
117
|
|
|
94
118
|
def get_number_of_portals_in_active_stack() -> int:
|
|
95
|
-
"""Get the number of portals in
|
|
119
|
+
"""Get the number of unique portals in the active stack.
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
The count of unique portals currently in the active portal stack.
|
|
123
|
+
"""
|
|
96
124
|
global _active_portals_stack
|
|
97
125
|
return len(set(_active_portals_stack))
|
|
98
126
|
|
|
99
127
|
|
|
100
128
|
def get_depth_of_active_portal_stack() -> int:
|
|
101
|
-
"""Get the depth of the active portal stack.
|
|
129
|
+
"""Get the depth of the active portal stack.
|
|
130
|
+
|
|
131
|
+
Returns:
|
|
132
|
+
The total depth (sum of all counters) of the active portal stack.
|
|
133
|
+
"""
|
|
102
134
|
global _active_portals_counters_stack
|
|
103
135
|
return sum(_active_portals_counters_stack)
|
|
104
136
|
|
|
105
137
|
|
|
106
138
|
def get_most_recently_created_portal() -> BasicPortal | None:
|
|
107
|
-
"""Get the most recently created portal
|
|
139
|
+
"""Get the most recently created portal.
|
|
140
|
+
|
|
141
|
+
Returns:
|
|
142
|
+
The most recently created portal instance, or None if no portals exist.
|
|
143
|
+
"""
|
|
108
144
|
global _most_recently_created_portal
|
|
109
145
|
return _most_recently_created_portal
|
|
110
146
|
|
|
@@ -117,6 +153,9 @@ def get_active_portal() -> BasicPortal:
|
|
|
117
153
|
it finds the most recently created portal and makes it active.
|
|
118
154
|
If there are currently no portals exist in the system,
|
|
119
155
|
it creates and returns the default portal.
|
|
156
|
+
|
|
157
|
+
Returns:
|
|
158
|
+
The currently active portal instance.
|
|
120
159
|
"""
|
|
121
160
|
global _most_recently_created_portal, _active_portals_stack
|
|
122
161
|
|
|
@@ -132,7 +171,11 @@ def get_active_portal() -> BasicPortal:
|
|
|
132
171
|
|
|
133
172
|
|
|
134
173
|
def get_nonactive_portals() -> list[BasicPortal]:
|
|
135
|
-
"""Get a list of all portals that are not in the active stack.
|
|
174
|
+
"""Get a list of all portals that are not in the active stack.
|
|
175
|
+
|
|
176
|
+
Returns:
|
|
177
|
+
A list of portal instances that are not currently in the active portal stack.
|
|
178
|
+
"""
|
|
136
179
|
active_portal_str_id = get_active_portal()._str_id
|
|
137
180
|
found_portals = []
|
|
138
181
|
for portal_str_id, portal in _all_known_portals.items():
|
|
@@ -149,6 +192,9 @@ def get_default_portal_base_dir() -> str:
|
|
|
149
192
|
Pythagoras connects to the default local portal
|
|
150
193
|
when no other portal is specified in the
|
|
151
194
|
program which uses Pythagoras.
|
|
195
|
+
|
|
196
|
+
Returns:
|
|
197
|
+
The absolute path to the default portal's base directory as a string.
|
|
152
198
|
"""
|
|
153
199
|
home_directory = Path.home()
|
|
154
200
|
target_directory = home_directory / ".pythagoras" / ".default_portal"
|
|
@@ -168,7 +214,7 @@ class BasicPortal(NotPicklable,ParameterizableClass, metaclass = PostInitMeta):
|
|
|
168
214
|
across multiple runs of the application, and across multiple computers.
|
|
169
215
|
|
|
170
216
|
A Pythagoras-based application can have multiple portals,
|
|
171
|
-
and there is usually a
|
|
217
|
+
and there is usually a currently active one, accessible via
|
|
172
218
|
get_active_portal().
|
|
173
219
|
|
|
174
220
|
BasicPortal is a base class for all portal objects.
|
|
@@ -184,6 +230,17 @@ class BasicPortal(NotPicklable,ParameterizableClass, metaclass = PostInitMeta):
|
|
|
184
230
|
|
|
185
231
|
|
|
186
232
|
def __init__(self, root_dict:PersiDict|str|None = None):
|
|
233
|
+
"""Initialize a BasicPortal instance.
|
|
234
|
+
|
|
235
|
+
Sets up the portal with a root dictionary for persistent storage.
|
|
236
|
+
If no root_dict is provided, uses the default portal base directory
|
|
237
|
+
to create a FileDirDict.
|
|
238
|
+
|
|
239
|
+
Args:
|
|
240
|
+
root_dict: The root dictionary for persistent storage, a path string,
|
|
241
|
+
or None to use the default location. If a string is provided,
|
|
242
|
+
it will be converted to a FileDirDict using that path.
|
|
243
|
+
"""
|
|
187
244
|
ParameterizableClass.__init__(self)
|
|
188
245
|
if root_dict is None:
|
|
189
246
|
root_dict = get_default_portal_base_dir()
|
|
@@ -197,7 +254,12 @@ class BasicPortal(NotPicklable,ParameterizableClass, metaclass = PostInitMeta):
|
|
|
197
254
|
|
|
198
255
|
|
|
199
256
|
def _post_init_hook(self) -> None:
|
|
200
|
-
"""
|
|
257
|
+
"""Execute post-initialization tasks for the portal.
|
|
258
|
+
|
|
259
|
+
This method is automatically called after all __init__() methods
|
|
260
|
+
complete. It registers the portal in the global registry and updates
|
|
261
|
+
the most recently created portal reference.
|
|
262
|
+
"""
|
|
201
263
|
global _most_recently_created_portal, _all_known_portals
|
|
202
264
|
_all_known_portals[self._str_id] = self
|
|
203
265
|
_most_recently_created_portal = self
|
|
@@ -205,7 +267,16 @@ class BasicPortal(NotPicklable,ParameterizableClass, metaclass = PostInitMeta):
|
|
|
205
267
|
|
|
206
268
|
def _get_linked_objects_ids(self
|
|
207
269
|
, target_class: type | None = None) -> set[PObjectStrID]:
|
|
208
|
-
"""Get the set
|
|
270
|
+
"""Get the set of string IDs of objects linked to this portal.
|
|
271
|
+
|
|
272
|
+
Args:
|
|
273
|
+
target_class: The class type to filter for, or None to include
|
|
274
|
+
all linked objects regardless of type.
|
|
275
|
+
|
|
276
|
+
Returns:
|
|
277
|
+
A set of string IDs representing objects linked to this portal,
|
|
278
|
+
optionally filtered by the specified target class.
|
|
279
|
+
"""
|
|
209
280
|
global _all_links_from_objects_to_portals
|
|
210
281
|
result = set()
|
|
211
282
|
for obj_str_id, portal_str_id in _all_links_from_objects_to_portals.items():
|
|
@@ -220,7 +291,16 @@ class BasicPortal(NotPicklable,ParameterizableClass, metaclass = PostInitMeta):
|
|
|
220
291
|
|
|
221
292
|
|
|
222
293
|
def get_linked_objects(self, target_class: type | None = None) -> list[PortalAwareClass]:
|
|
223
|
-
"""Get the list of objects
|
|
294
|
+
"""Get the list of objects linked to this portal.
|
|
295
|
+
|
|
296
|
+
Args:
|
|
297
|
+
target_class: The class type to filter for, or None to include
|
|
298
|
+
all linked objects regardless of type.
|
|
299
|
+
|
|
300
|
+
Returns:
|
|
301
|
+
A list of PortalAwareClass instances that are linked to this portal,
|
|
302
|
+
optionally filtered by the specified target class.
|
|
303
|
+
"""
|
|
224
304
|
found_linked_objects_ids = self._get_linked_objects_ids(target_class)
|
|
225
305
|
found_linked_objects = []
|
|
226
306
|
for obj_str_id in found_linked_objects_ids:
|
|
@@ -230,12 +310,27 @@ class BasicPortal(NotPicklable,ParameterizableClass, metaclass = PostInitMeta):
|
|
|
230
310
|
|
|
231
311
|
|
|
232
312
|
def get_number_of_linked_objects(self, target_class: type | None = None) -> int:
|
|
233
|
-
"""Get the number of
|
|
234
|
-
|
|
313
|
+
"""Get the number of objects linked to this portal.
|
|
314
|
+
|
|
315
|
+
Args:
|
|
316
|
+
target_class: The class type to filter for, or None to include
|
|
317
|
+
all linked objects regardless of type.
|
|
318
|
+
|
|
319
|
+
Returns:
|
|
320
|
+
The count of portal-aware objects linked to this portal,
|
|
321
|
+
optionally filtered by the specified target class.
|
|
322
|
+
"""
|
|
323
|
+
return len(self._get_linked_objects_ids(target_class))
|
|
235
324
|
|
|
236
325
|
|
|
237
326
|
@property
|
|
238
327
|
def entropy_infuser(self) -> random.Random:
|
|
328
|
+
"""Get the shared random number generator for the portal system.
|
|
329
|
+
|
|
330
|
+
Returns:
|
|
331
|
+
A Random instance shared across all BasicPortal instances for
|
|
332
|
+
generating random values and entropy when needed.
|
|
333
|
+
"""
|
|
239
334
|
return BasicPortal._entropy_infuser
|
|
240
335
|
|
|
241
336
|
|
|
@@ -372,6 +467,16 @@ class PortalAwareClass(metaclass = PostInitMeta):
|
|
|
372
467
|
_visited_portals: set[str] | None
|
|
373
468
|
|
|
374
469
|
def __init__(self, portal:BasicPortal|None=None):
|
|
470
|
+
"""Initialize a PortalAwareClass instance.
|
|
471
|
+
|
|
472
|
+
Sets up the object with an optional linked portal. If a portal is provided,
|
|
473
|
+
the object will be linked to that portal and will use it for all operations.
|
|
474
|
+
If no portal is provided, the object will use the currently active portal.
|
|
475
|
+
|
|
476
|
+
Args:
|
|
477
|
+
portal: The portal to link this object to, or None to use the
|
|
478
|
+
currently active portal for operations.
|
|
479
|
+
"""
|
|
375
480
|
assert portal is None or isinstance(portal, BasicPortal)
|
|
376
481
|
self._linked_portal_at_init = portal
|
|
377
482
|
# self._hash_id_cache = None
|
|
@@ -379,7 +484,12 @@ class PortalAwareClass(metaclass = PostInitMeta):
|
|
|
379
484
|
|
|
380
485
|
|
|
381
486
|
def _post_init_hook(self):
|
|
382
|
-
"""
|
|
487
|
+
"""Execute post-initialization tasks for the portal-aware object.
|
|
488
|
+
|
|
489
|
+
This method is automatically called after all object's __init__() methods
|
|
490
|
+
complete. It registers the object with its linked portal if one was provided
|
|
491
|
+
during initialization, establishing the connection between the object and portal.
|
|
492
|
+
"""
|
|
383
493
|
global _all_links_from_objects_to_portals
|
|
384
494
|
global _all_activated_portal_aware_objects
|
|
385
495
|
if self._linked_portal_at_init is not None:
|
|
@@ -427,6 +537,14 @@ class PortalAwareClass(metaclass = PostInitMeta):
|
|
|
427
537
|
|
|
428
538
|
|
|
429
539
|
def _visit_portal(self, portal: BasicPortal) -> None:
|
|
540
|
+
"""Visit a portal and register the object if this is the first visit.
|
|
541
|
+
|
|
542
|
+
Checks if the object has previously visited the specified portal.
|
|
543
|
+
If this is the first visit, registers the object with that portal.
|
|
544
|
+
|
|
545
|
+
Args:
|
|
546
|
+
portal: The BasicPortal instance to visit.
|
|
547
|
+
"""
|
|
430
548
|
if portal._str_id not in self._visited_portals:
|
|
431
549
|
self._first_visit_to_portal(portal)
|
|
432
550
|
|
|
@@ -463,7 +581,14 @@ class PortalAwareClass(metaclass = PostInitMeta):
|
|
|
463
581
|
|
|
464
582
|
@abstractmethod
|
|
465
583
|
def __setstate__(self, state):
|
|
466
|
-
"""This method is called when the object is unpickled.
|
|
584
|
+
"""This method is called when the object is unpickled.
|
|
585
|
+
|
|
586
|
+
Restores the object's state from unpickling and resets portal-related
|
|
587
|
+
attributes to ensure proper initialization in the new environment.
|
|
588
|
+
|
|
589
|
+
Args:
|
|
590
|
+
state: The state dictionary used for unpickling the object.
|
|
591
|
+
"""
|
|
467
592
|
self._invalidate_cache()
|
|
468
593
|
self._visited_portals = set()
|
|
469
594
|
self._linked_portal_at_init = None
|
|
@@ -1,8 +1,36 @@
|
|
|
1
1
|
class PythagorasException(Exception):
|
|
2
|
+
"""Base exception class for all Pythagoras-related errors.
|
|
3
|
+
|
|
4
|
+
This is the base class for all custom exceptions raised by the Pythagoras
|
|
5
|
+
library. It provides a common interface and functionality for all
|
|
6
|
+
Pythagoras-specific error conditions.
|
|
7
|
+
|
|
8
|
+
Attributes:
|
|
9
|
+
message: A human-readable description of the error.
|
|
10
|
+
"""
|
|
11
|
+
|
|
2
12
|
def __init__(self, message):
|
|
13
|
+
"""Initialize the exception with an error message.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
message: A string describing the error that occurred.
|
|
17
|
+
"""
|
|
3
18
|
self.message = message
|
|
4
19
|
|
|
5
20
|
|
|
6
21
|
class NonCompliantFunction(PythagorasException):
|
|
22
|
+
"""Exception raised when a function does not comply with Pythagoras requirements.
|
|
23
|
+
|
|
24
|
+
This exception is raised when a function fails to meet the compliance
|
|
25
|
+
requirements for use within the Pythagoras framework. This typically
|
|
26
|
+
occurs when functions cannot be properly serialized, analyzed, or
|
|
27
|
+
executed within the portal environment.
|
|
28
|
+
"""
|
|
29
|
+
|
|
7
30
|
def __init__(self, message):
|
|
31
|
+
"""Initialize the exception with an error message.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
message: A string describing why the function is non-compliant.
|
|
35
|
+
"""
|
|
8
36
|
PythagorasException.__init__(self, message)
|
|
@@ -3,8 +3,23 @@ from typing import Any
|
|
|
3
3
|
from persidict import replace_unsafe_chars
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
def get_long_infoname(x:Any, drop_unsafe_chars:bool = True) -> str:
|
|
7
|
-
"""Build a string with extended information about an object and its type
|
|
6
|
+
def get_long_infoname(x: Any, drop_unsafe_chars: bool = True) -> str:
|
|
7
|
+
"""Build a string with extended information about an object and its type.
|
|
8
|
+
|
|
9
|
+
Creates a detailed identifier string that includes the module, class name,
|
|
10
|
+
and object name (if available) of the given object. This is useful for
|
|
11
|
+
creating unique identifiers for objects in the Pythagoras system.
|
|
12
|
+
|
|
13
|
+
Args:
|
|
14
|
+
x: The object for which to generate the information string.
|
|
15
|
+
drop_unsafe_chars: If True, replaces unsafe characters with underscores
|
|
16
|
+
using the persidict.replace_unsafe_chars function. Defaults to True.
|
|
17
|
+
|
|
18
|
+
Returns:
|
|
19
|
+
A string containing extended information about the object, including
|
|
20
|
+
its module, class name, and object name (when available). Unsafe
|
|
21
|
+
characters are replaced with underscores if drop_unsafe_chars is True.
|
|
22
|
+
"""
|
|
8
23
|
|
|
9
24
|
name = str(type(x).__module__)
|
|
10
25
|
|
|
@@ -1,8 +1,36 @@
|
|
|
1
1
|
class NotPicklable:
|
|
2
|
+
"""A mixin class that prevents objects from being pickled or unpickled.
|
|
3
|
+
|
|
4
|
+
This class provides a mechanism to explicitly prevent instances from being
|
|
5
|
+
serialized using Python's pickle module. Classes that inherit from this
|
|
6
|
+
mixin will raise TypeError exceptions when pickle attempts to serialize
|
|
7
|
+
or deserialize them.
|
|
8
|
+
|
|
9
|
+
This is useful for objects that contain non-serializable resources or
|
|
10
|
+
should not be persisted for security or architectural reasons.
|
|
11
|
+
"""
|
|
12
|
+
|
|
2
13
|
def __getstate__(self):
|
|
3
|
-
"""
|
|
14
|
+
"""Prevent object serialization by raising TypeError.
|
|
15
|
+
|
|
16
|
+
This method is called by pickle when attempting to serialize the object.
|
|
17
|
+
It unconditionally raises a TypeError to prevent pickling.
|
|
18
|
+
|
|
19
|
+
Raises:
|
|
20
|
+
TypeError: Always raised to prevent the object from being pickled.
|
|
21
|
+
"""
|
|
4
22
|
raise TypeError(f"{type(self).__name__} cannot be pickled")
|
|
5
23
|
|
|
6
24
|
def __setstate__(self, state):
|
|
7
|
-
"""
|
|
25
|
+
"""Prevent object deserialization by raising TypeError.
|
|
26
|
+
|
|
27
|
+
This method is called by pickle when attempting to deserialize the object.
|
|
28
|
+
It unconditionally raises a TypeError to prevent unpickling.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
state: The state dictionary that would be used for unpickling.
|
|
32
|
+
|
|
33
|
+
Raises:
|
|
34
|
+
TypeError: Always raised to prevent the object from being unpickled.
|
|
35
|
+
"""
|
|
8
36
|
raise TypeError(f"{type(self).__name__} cannot be unpickled")
|
|
@@ -14,8 +14,19 @@ class _PortalTester:
|
|
|
14
14
|
_current_instance:_PortalTester|None = None
|
|
15
15
|
_portal:BasicPortal|None = None
|
|
16
16
|
|
|
17
|
-
def __init__(self,portal_class:PortalType|None = None
|
|
18
|
-
|
|
17
|
+
def __init__(self, portal_class: PortalType | None = None, *args, **kwargs):
|
|
18
|
+
"""Initialize the portal tester.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
portal_class: The portal class to test, or None for no portal.
|
|
22
|
+
Must be a subclass of BasicPortal if provided.
|
|
23
|
+
*args: Positional arguments to pass to the portal constructor.
|
|
24
|
+
**kwargs: Keyword arguments to pass to the portal constructor.
|
|
25
|
+
|
|
26
|
+
Raises:
|
|
27
|
+
Exception: If another _PortalTester instance is already active.
|
|
28
|
+
AssertionError: If portal_class is not a subclass of BasicPortal.
|
|
29
|
+
"""
|
|
19
30
|
if _PortalTester._current_instance is not None:
|
|
20
31
|
raise Exception("_PortalTester can't be nested")
|
|
21
32
|
_PortalTester._current_instance = self
|
|
@@ -28,12 +39,27 @@ class _PortalTester:
|
|
|
28
39
|
|
|
29
40
|
|
|
30
41
|
@property
|
|
31
|
-
def portal(self) -> BasicPortal|None:
|
|
32
|
-
"""The portal object being tested.
|
|
33
|
-
return self._portal
|
|
42
|
+
def portal(self) -> BasicPortal | None:
|
|
43
|
+
"""The portal object being tested.
|
|
34
44
|
|
|
45
|
+
Returns:
|
|
46
|
+
The current portal instance if one exists, None otherwise.
|
|
47
|
+
"""
|
|
48
|
+
return self._portal
|
|
35
49
|
|
|
36
50
|
def __enter__(self):
|
|
51
|
+
"""Enter the portal testing context.
|
|
52
|
+
|
|
53
|
+
Clears all existing portals and creates a new portal instance if
|
|
54
|
+
a portal class was specified during initialization. The created
|
|
55
|
+
portal is automatically entered as a context manager.
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
The _PortalTester instance for use in context management.
|
|
59
|
+
|
|
60
|
+
Raises:
|
|
61
|
+
Exception: If another _PortalTester instance is already active.
|
|
62
|
+
"""
|
|
37
63
|
if (_PortalTester._current_instance is not None
|
|
38
64
|
and _PortalTester._current_instance is not self):
|
|
39
65
|
raise Exception("_PortalTester can't be nested")
|
|
@@ -42,12 +68,21 @@ class _PortalTester:
|
|
|
42
68
|
_clear_all_portals()
|
|
43
69
|
|
|
44
70
|
if self.portal_class is not None:
|
|
45
|
-
self._portal = self.portal_class(*self.args
|
|
71
|
+
self._portal = self.portal_class(*self.args, **self.kwargs)
|
|
46
72
|
self.portal.__enter__()
|
|
47
73
|
return self
|
|
48
74
|
|
|
49
75
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
76
|
+
"""Exit the portal testing context.
|
|
77
|
+
|
|
78
|
+
Properly exits the portal context if one was created, clears all
|
|
79
|
+
portals from the system, and resets the current instance tracker.
|
|
50
80
|
|
|
81
|
+
Args:
|
|
82
|
+
exc_type: Exception type if an exception occurred, None otherwise.
|
|
83
|
+
exc_val: Exception value if an exception occurred, None otherwise.
|
|
84
|
+
exc_tb: Exception traceback if an exception occurred, None otherwise.
|
|
85
|
+
"""
|
|
51
86
|
if self.portal_class is not None:
|
|
52
87
|
self.portal.__exit__(exc_type, exc_val, exc_tb)
|
|
53
88
|
self._portal = None
|
|
@@ -2,8 +2,29 @@ from abc import ABCMeta
|
|
|
2
2
|
|
|
3
3
|
class PostInitMeta(ABCMeta):
|
|
4
4
|
"""Ensures method ._post_init_hook() is always called after the constructor.
|
|
5
|
+
|
|
6
|
+
This metaclass automatically calls the `_post_init_hook()` method on instances
|
|
7
|
+
after their construction is complete. This ensures that any post-initialization
|
|
8
|
+
logic is consistently executed for all instances of classes using this metaclass.
|
|
5
9
|
"""
|
|
10
|
+
|
|
6
11
|
def __call__(self, *args, **kwargs):
|
|
12
|
+
"""Create and initialize an instance, then call its post-init hook.
|
|
13
|
+
|
|
14
|
+
This method overrides the default instance creation process to ensure
|
|
15
|
+
that `_post_init_hook()` is called after the regular constructor.
|
|
16
|
+
It also includes validation logic for portal-aware objects.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
*args: Positional arguments to pass to the class constructor.
|
|
20
|
+
**kwargs: Keyword arguments to pass to the class constructor.
|
|
21
|
+
|
|
22
|
+
Returns:
|
|
23
|
+
The fully initialized instance with post-init hook executed.
|
|
24
|
+
|
|
25
|
+
Raises:
|
|
26
|
+
AssertionError: If portal-aware object validation fails.
|
|
27
|
+
"""
|
|
7
28
|
instance = super().__call__(*args, **kwargs)
|
|
8
29
|
instance._post_init_hook()
|
|
9
30
|
#TODO: remove/improve this check at some point in the future
|
|
@@ -75,7 +75,7 @@ class DataPortal(OrdinaryCodePortal):
|
|
|
75
75
|
config_settings_prototype = self._root_dict.get_subdict("config_settings")
|
|
76
76
|
config_settings_params = config_settings_prototype.get_params()
|
|
77
77
|
config_settings_params.update(
|
|
78
|
-
digest_len=0,
|
|
78
|
+
digest_len=0, append_only=False, serialization_format="pkl")
|
|
79
79
|
config_settings = type(self._root_dict)(**config_settings_params)
|
|
80
80
|
self._config_settings = config_settings
|
|
81
81
|
|
|
@@ -90,7 +90,7 @@ class DataPortal(OrdinaryCodePortal):
|
|
|
90
90
|
value_store_prototype = self._root_dict.get_subdict("value_store")
|
|
91
91
|
value_store_params = value_store_prototype.get_params()
|
|
92
92
|
value_store_params.update(
|
|
93
|
-
digest_len=0,
|
|
93
|
+
digest_len=0, append_only=True, serialization_format = "pkl")
|
|
94
94
|
value_store = type(self._root_dict)(**value_store_params)
|
|
95
95
|
value_store = WriteOnceDict(value_store, 0)
|
|
96
96
|
self._value_store = value_store
|
|
@@ -551,13 +551,13 @@ class LoggingCodePortal(DataPortal):
|
|
|
551
551
|
crash_history_prototype = self._root_dict.get_subdict("crash_history")
|
|
552
552
|
crash_history_params = crash_history_prototype.get_params()
|
|
553
553
|
crash_history_params.update(
|
|
554
|
-
dict(
|
|
554
|
+
dict(serialization_format="json", append_only=True , digest_len=0))
|
|
555
555
|
self._crash_history = type(self._root_dict)(**crash_history_params)
|
|
556
556
|
|
|
557
557
|
event_history_prototype = self._root_dict.get_subdict("event_history")
|
|
558
558
|
event_history_params = event_history_prototype.get_params()
|
|
559
559
|
event_history_params.update(
|
|
560
|
-
dict(
|
|
560
|
+
dict(serialization_format="json", append_only=True, digest_len=0))
|
|
561
561
|
self._event_history = type(self._root_dict)(**event_history_params)
|
|
562
562
|
|
|
563
563
|
run_history_prototype = self._root_dict.get_subdict("run_history")
|
|
@@ -566,10 +566,10 @@ class LoggingCodePortal(DataPortal):
|
|
|
566
566
|
run_history = OverlappingMultiDict(
|
|
567
567
|
dict_type=dict_type
|
|
568
568
|
, shared_subdicts_params=run_history_shared_params
|
|
569
|
-
, json=dict(
|
|
570
|
-
, py=dict(base_class_for_values=str,
|
|
571
|
-
, txt=dict(base_class_for_values=str,
|
|
572
|
-
, pkl=dict(
|
|
569
|
+
, json=dict(append_only=True)
|
|
570
|
+
, py=dict(base_class_for_values=str, append_only=False) # Immutable items????
|
|
571
|
+
, txt=dict(base_class_for_values=str, append_only=True)
|
|
572
|
+
, pkl=dict(append_only=True)
|
|
573
573
|
)
|
|
574
574
|
self._run_history = run_history
|
|
575
575
|
|
|
@@ -68,7 +68,7 @@ class PureCodePortal(ProtectedCodePortal):
|
|
|
68
68
|
results_dict_prototype = self._root_dict.get_subdict(
|
|
69
69
|
"execution_results")
|
|
70
70
|
results_dict_params = results_dict_prototype.get_params()
|
|
71
|
-
results_dict_params.update(
|
|
71
|
+
results_dict_params.update(append_only=True, serialization_format = "pkl")
|
|
72
72
|
execution_results = type(self._root_dict)(**results_dict_params)
|
|
73
73
|
execution_results = WriteOnceDict(execution_results, 0)
|
|
74
74
|
self._execution_results = execution_results
|
|
@@ -76,7 +76,7 @@ class PureCodePortal(ProtectedCodePortal):
|
|
|
76
76
|
requests_dict_prototype = self._root_dict.get_subdict(
|
|
77
77
|
"execution_requests")
|
|
78
78
|
requests_dict_params = requests_dict_prototype.get_params()
|
|
79
|
-
requests_dict_params.update(
|
|
79
|
+
requests_dict_params.update(append_only=False, serialization_format="pkl")
|
|
80
80
|
execution_requests = type(self._root_dict)(**requests_dict_params)
|
|
81
81
|
self._execution_requests = execution_requests
|
|
82
82
|
|
|
@@ -77,8 +77,8 @@ class SwarmingPortal(PureCodePortal):
|
|
|
77
77
|
compute_nodes = OverlappingMultiDict(
|
|
78
78
|
dict_type=dict_type
|
|
79
79
|
, shared_subdicts_params=compute_nodes_shared_params
|
|
80
|
-
, json=dict(
|
|
81
|
-
, pkl=dict(
|
|
80
|
+
, json=dict(append_only=False)
|
|
81
|
+
, pkl=dict(append_only=False)
|
|
82
82
|
)
|
|
83
83
|
self._compute_nodes = compute_nodes
|
|
84
84
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: pythagoras
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.24.0
|
|
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
|
|
@@ -72,16 +72,16 @@ Pythagoras elevates two popular techniques — memoization and parallelization
|
|
|
72
72
|
to a global scale and then fuses them, unlocking performance and scalability
|
|
73
73
|
that were previously out of reach.
|
|
74
74
|
|
|
75
|
-
* [Pythagoras 101: Introduction to Memoization](https://
|
|
75
|
+
* [Pythagoras 101: Introduction to Memoization](https://pythagoras.link/tutorial-101)
|
|
76
76
|
|
|
77
|
-
* [Pythagoras 102: Parallelization Basics](https://
|
|
77
|
+
* [Pythagoras 102: Parallelization Basics](https://pythagoras.link/tutorial-102)
|
|
78
78
|
|
|
79
79
|
Drawing from many years of functional-programming practice,
|
|
80
80
|
Pythagoras extends these proven ideas to the next level.
|
|
81
81
|
In a Pythagoras environment, you can seamlessly employ your
|
|
82
82
|
preferred functional patterns, augmented by new capabilities.
|
|
83
83
|
|
|
84
|
-
* [Pythagoras 203: Work with Functions](https://
|
|
84
|
+
* [Pythagoras 203: Work with Functions](https://pythagoras.link/tutorial-203)
|
|
85
85
|
|
|
86
86
|
**!!! BOOKMARK THIS PAGE AND COME BACK LATER, WE WILL PUBLISH MORE TUTORIALS SOON !!!**
|
|
87
87
|
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
pythagoras/.DS_Store,sha256=QNNcLWt_ymcoqL6JxhBobOuDsh4y1v4N3tzxs_WL_a8,6148
|
|
2
2
|
pythagoras/_010_basic_portals/__init__.py,sha256=ER0kV_th7hba4nXXjoP-joN-cqkUMoH4iRSCgKUP75Y,1703
|
|
3
|
-
pythagoras/_010_basic_portals/basic_portal_core_classes.py,sha256=
|
|
4
|
-
pythagoras/_010_basic_portals/exceptions.py,sha256=
|
|
5
|
-
pythagoras/_010_basic_portals/long_infoname.py,sha256=
|
|
6
|
-
pythagoras/_010_basic_portals/not_picklable_class.py,sha256=
|
|
7
|
-
pythagoras/_010_basic_portals/portal_tester.py,sha256=
|
|
8
|
-
pythagoras/_010_basic_portals/post_init_metaclass.py,sha256=
|
|
3
|
+
pythagoras/_010_basic_portals/basic_portal_core_classes.py,sha256=2Hh9mEb0k0YKKl6sDiOINLk4EspBIbn2oR-1xGyUFFA,24568
|
|
4
|
+
pythagoras/_010_basic_portals/exceptions.py,sha256=CMtSyRb47YKZtANN-iCQBvvyYPKdKqomLygRE1fjSNk,1248
|
|
5
|
+
pythagoras/_010_basic_portals/long_infoname.py,sha256=KXOmHfQ_5hdZNqfB3Cif2CQiZ3XI3UAOEXKl3DLLYF4,1366
|
|
6
|
+
pythagoras/_010_basic_portals/not_picklable_class.py,sha256=hSU069pixbaRaV_TX0KDdy3kupB9GVqkzYwxU9DzEkE,1442
|
|
7
|
+
pythagoras/_010_basic_portals/portal_tester.py,sha256=x6HiJ3GW9XWplnsT6Ob7QCy2J_JPgGpdaJ8QRyFH-e8,3353
|
|
8
|
+
pythagoras/_010_basic_portals/post_init_metaclass.py,sha256=94FEVMCJBUReRb-fo2-LW8YWXUXw5lLLYlXMnlxHJuU,1495
|
|
9
9
|
pythagoras/_020_ordinary_code_portals/__init__.py,sha256=p3kSqaQYj0xlhk9BwptFgA1USdTbfHkAB5Q9MH-ANI0,1295
|
|
10
10
|
pythagoras/_020_ordinary_code_portals/code_normalizer.py,sha256=7L3P5AaZKS9IPqrIEv5UJc3HHDP48fw6EGQWXZcxQtM,5004
|
|
11
11
|
pythagoras/_020_ordinary_code_portals/function_processing.py,sha256=rlhB0dpaUztv6SM2wlUdaUdETcV2pVUICYufVrQCRcc,3623
|
|
12
12
|
pythagoras/_020_ordinary_code_portals/ordinary_decorator.py,sha256=J4qx03NEEgpYWvg4D8UkAL0PdtAt2sQyMN1op6LMFsA,1028
|
|
13
13
|
pythagoras/_020_ordinary_code_portals/ordinary_portal_core_classes.py,sha256=bY2Hzy19lTezv96dSc1l5SsaGxNkMTOq8IsfnkdV1wY,9743
|
|
14
14
|
pythagoras/_030_data_portals/__init__.py,sha256=f_F9DCmuVgPMgzwRjuNj6FI63S3oXu7lj3zU66Nw7Hc,1427
|
|
15
|
-
pythagoras/_030_data_portals/data_portal_core_classes.py,sha256=
|
|
15
|
+
pythagoras/_030_data_portals/data_portal_core_classes.py,sha256=_K7diNol7Wl0YLUI2VC3cn7djm0wFQ7rcvflpyIP84w,20420
|
|
16
16
|
pythagoras/_030_data_portals/ready_and_get.py,sha256=UKiQHkLhdAdvEwP5BTdoAnp4XEs7HDGx6026M2eMuc0,2842
|
|
17
17
|
pythagoras/_030_data_portals/storable_decorator.py,sha256=l8W3GhVmIscgjoCTGq3tmdehKGDLIVnFbTM-GW-1G4g,578
|
|
18
18
|
pythagoras/_040_logging_code_portals/__init__.py,sha256=q2hVyOVgE-9Ru3ycilK98YS9Rat8tSc6erd7AtGxpaA,996
|
|
@@ -20,7 +20,7 @@ pythagoras/_040_logging_code_portals/exception_processing_tracking.py,sha256=sU-
|
|
|
20
20
|
pythagoras/_040_logging_code_portals/execution_environment_summary.py,sha256=hTvTbadYAtisZ4H8rq-n_hsKPCS38jsSz8lw_A1DIqY,2268
|
|
21
21
|
pythagoras/_040_logging_code_portals/kw_args.py,sha256=4EYpPrr2xCgFAarmdFRKlEvGhIHC9kk9kc-VPW4S-XA,2717
|
|
22
22
|
pythagoras/_040_logging_code_portals/logging_decorator.py,sha256=079w2_Z5HhXFLrgyXQekjuOby9CdUgFUGRbRT5lpujU,891
|
|
23
|
-
pythagoras/_040_logging_code_portals/logging_portal_core_classes.py,sha256=
|
|
23
|
+
pythagoras/_040_logging_code_portals/logging_portal_core_classes.py,sha256=8jDjprlnpo7cXW9dhv-VdVF8eCMRRRTPZngol5xjiqw,21968
|
|
24
24
|
pythagoras/_040_logging_code_portals/notebook_checker.py,sha256=5lQJDIDzhRIRSmX1T88nAREMEMoDDFf0OIKcvTpnhzk,541
|
|
25
25
|
pythagoras/_040_logging_code_portals/output_capturer.py,sha256=ohCp6qqxL7IuJGfnFuCIgj5Oc4HmC8c7uZGE_uzWkZk,4216
|
|
26
26
|
pythagoras/_040_logging_code_portals/uncaught_exceptions.py,sha256=B3bPvX5nnJJx4JjLSGdl1xXOBU2pqo3CP-MomJAfXaE,3121
|
|
@@ -41,12 +41,12 @@ pythagoras/_070_protected_code_portals/protected_portal_core_classes.py,sha256=Z
|
|
|
41
41
|
pythagoras/_070_protected_code_portals/system_utils.py,sha256=h4oiEQFAyFKzqvd0ywwAI0WDYKSuIh_vif86XQIXYwE,2360
|
|
42
42
|
pythagoras/_070_protected_code_portals/validation_succesful_const.py,sha256=4NWGwN5Gu6kbbHTMkmJs8Ym0rFee_cIE2VlAPonmlJI,298
|
|
43
43
|
pythagoras/_080_pure_code_portals/__init__.py,sha256=OI7836lLHT51SYdFfmWp4GdGRgcAkpLiAj-Zj_g2Gxo,1052
|
|
44
|
-
pythagoras/_080_pure_code_portals/pure_core_classes.py,sha256=
|
|
44
|
+
pythagoras/_080_pure_code_portals/pure_core_classes.py,sha256=6AjtE9QdiG84e9WuJtsrvkuHTRC4MovC31xItGn2PD8,20455
|
|
45
45
|
pythagoras/_080_pure_code_portals/pure_decorator.py,sha256=WHZQzmyxgCpALHrqfeiOMrM6TDkZcv0Y2b756ez4Q2k,2279
|
|
46
46
|
pythagoras/_080_pure_code_portals/recursion_pre_validator.py,sha256=n03ooGISJvuwNWteBN9t7CFFSLYAu86AHHFJVcywPqg,1865
|
|
47
47
|
pythagoras/_090_swarming_portals/__init__.py,sha256=TuA17PftTBudptAblNtBlD46BqUiitksOtf3y01QKm0,514
|
|
48
48
|
pythagoras/_090_swarming_portals/output_suppressor.py,sha256=g-bMm8xioia6uxFlkS71CV6pSEmc5RNqdRasi1RSJgc,626
|
|
49
|
-
pythagoras/_090_swarming_portals/swarming_portals.py,sha256=
|
|
49
|
+
pythagoras/_090_swarming_portals/swarming_portals.py,sha256=YKs7JDUp7ifqZSSkkAVkJ73IdyxCwkvArQW9C-cCIfk,12286
|
|
50
50
|
pythagoras/_100_top_level_API/__init__.py,sha256=s5LtwskY2nwkRPFKzP0PrCzQ1c9oScZO0kM9_bWLi3U,64
|
|
51
51
|
pythagoras/_100_top_level_API/default_local_portal.py,sha256=SnykTpTXg1KuT1qwDnrAZ63lYshMy-0nNiUgoOVMxCs,339
|
|
52
52
|
pythagoras/_100_top_level_API/top_level_API.py,sha256=S2NXW4bfL98o6Txn6NM0EeBb1nzwFtPSl-yWNevAQIE,906
|
|
@@ -60,6 +60,6 @@ pythagoras/_900_project_stats_collector/__init__.py,sha256=Eagt-BhPPtBGgpMywx2lk
|
|
|
60
60
|
pythagoras/_900_project_stats_collector/project_analyzer.py,sha256=uhycFKjUIXEpYcZYnak3yn4JFhchl-oZ7wt6spFxhoY,3574
|
|
61
61
|
pythagoras/__init__.py,sha256=TMPtJdSi_WShCpJnsVVdO48Wcvs78GMbUi5gHc1eMLw,1233
|
|
62
62
|
pythagoras/core/__init__.py,sha256=cXtQ-Vbm8TqzazvkFws5cV3AEEYbEKzNXYeuHeLGFK0,328
|
|
63
|
-
pythagoras-0.
|
|
64
|
-
pythagoras-0.
|
|
65
|
-
pythagoras-0.
|
|
63
|
+
pythagoras-0.24.0.dist-info/WHEEL,sha256=-neZj6nU9KAMg2CnCY6T3w8J53nx1kFGw_9HfoSzM60,79
|
|
64
|
+
pythagoras-0.24.0.dist-info/METADATA,sha256=oCp51VLtdtZ93XR25Nv-FQIu-VG0rU770nm6WWug_vU,7449
|
|
65
|
+
pythagoras-0.24.0.dist-info/RECORD,,
|