pythagoras 0.8.5__tar.gz → 0.8.9__tar.gz

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.
Files changed (73) hide show
  1. {pythagoras-0.8.5/pythagoras.egg-info → pythagoras-0.8.9}/PKG-INFO +7 -1
  2. {pythagoras-0.8.5 → pythagoras-0.8.9}/README.md +3 -0
  3. {pythagoras-0.8.5 → pythagoras-0.8.9}/pythagoras/_01_foundational_objects/hash_addresses.py +2 -1
  4. {pythagoras-0.8.5 → pythagoras-0.8.9}/pythagoras/_01_foundational_objects/value_addresses.py +5 -4
  5. {pythagoras-0.8.5 → pythagoras-0.8.9}/pythagoras/_02_ordinary_functions/ordinary_funcs.py +5 -1
  6. {pythagoras-0.8.5 → pythagoras-0.8.9}/pythagoras/_03_autonomous_functions/autonomous_decorators.py +1 -1
  7. {pythagoras-0.8.5 → pythagoras-0.8.9}/pythagoras/_03_autonomous_functions/autonomous_funcs.py +28 -25
  8. {pythagoras-0.8.5 → pythagoras-0.8.9}/pythagoras/_04_idempotent_functions/__init__.py +1 -1
  9. {pythagoras-0.8.5 → pythagoras-0.8.9}/pythagoras/_04_idempotent_functions/idempotent_decorator.py +1 -1
  10. {pythagoras-0.8.5 → pythagoras-0.8.9}/pythagoras/_04_idempotent_functions/idempotent_func_and_address.py +111 -10
  11. {pythagoras-0.8.5 → pythagoras-0.8.9}/pythagoras/_04_idempotent_functions/kw_args.py +1 -1
  12. pythagoras-0.8.9/pythagoras/_05_events_and_exceptions/__init__.py +2 -0
  13. pythagoras-0.8.5/pythagoras/_99_misc_utils/context_builder.py → pythagoras-0.8.9/pythagoras/_05_events_and_exceptions/context_utils.py +13 -5
  14. pythagoras-0.8.9/pythagoras/_05_events_and_exceptions/event_logger.py +88 -0
  15. {pythagoras-0.8.5/pythagoras/_99_misc_utils → pythagoras-0.8.9/pythagoras/_05_events_and_exceptions}/find_in_callstack.py +13 -10
  16. pythagoras-0.8.9/pythagoras/_05_events_and_exceptions/type_retrievers.py +30 -0
  17. pythagoras-0.8.9/pythagoras/_05_events_and_exceptions/uncaught_exception_handlers.py +62 -0
  18. {pythagoras-0.8.5/pythagoras/_05_mission_control → pythagoras-0.8.9/pythagoras/_06_mission_control}/__events_and_exceptions_OLD__.py +1 -1
  19. {pythagoras-0.8.5/pythagoras/_05_mission_control → pythagoras-0.8.9/pythagoras/_06_mission_control}/__init__.py +2 -2
  20. {pythagoras-0.8.5/pythagoras/_05_mission_control → pythagoras-0.8.9/pythagoras/_06_mission_control}/global_state_management.py +59 -34
  21. {pythagoras-0.8.5/pythagoras/_05_mission_control → pythagoras-0.8.9/pythagoras/_06_mission_control}/summary.py +2 -2
  22. {pythagoras-0.8.5 → pythagoras-0.8.9}/pythagoras/_99_misc_utils/__init__.py +0 -3
  23. {pythagoras-0.8.5 → pythagoras-0.8.9}/pythagoras/________OLD________/NEW_hash_address.py +1 -1
  24. {pythagoras-0.8.5 → pythagoras-0.8.9}/pythagoras/__init__.py +11 -4
  25. {pythagoras-0.8.5 → pythagoras-0.8.9}/pythagoras/__main__.py +1 -1
  26. {pythagoras-0.8.5 → pythagoras-0.8.9/pythagoras.egg-info}/PKG-INFO +7 -1
  27. {pythagoras-0.8.5 → pythagoras-0.8.9}/pythagoras.egg-info/SOURCES.txt +12 -10
  28. {pythagoras-0.8.5 → pythagoras-0.8.9}/pythagoras.egg-info/requires.txt +3 -0
  29. {pythagoras-0.8.5 → pythagoras-0.8.9}/setup.py +7 -3
  30. pythagoras-0.8.5/pythagoras/_05_mission_control/events_and_exceptions_core.py +0 -89
  31. pythagoras-0.8.5/pythagoras/_99_misc_utils/isinstance_txt.py +0 -34
  32. {pythagoras-0.8.5 → pythagoras-0.8.9}/LICENSE +0 -0
  33. {pythagoras-0.8.5 → pythagoras-0.8.9}/pyproject.toml +0 -0
  34. {pythagoras-0.8.5 → pythagoras-0.8.9}/pythagoras/NEW_exception_logging.py +0 -0
  35. {pythagoras-0.8.5 → pythagoras-0.8.9}/pythagoras/OLD__main__.py +0 -0
  36. {pythagoras-0.8.5 → pythagoras-0.8.9}/pythagoras/OLD_p_cloud.py +0 -0
  37. {pythagoras-0.8.5 → pythagoras-0.8.9}/pythagoras/OLD_utils.py +0 -0
  38. {pythagoras-0.8.5 → pythagoras-0.8.9}/pythagoras/_01_foundational_objects/__init__.py +0 -0
  39. {pythagoras-0.8.5 → pythagoras-0.8.9}/pythagoras/_01_foundational_objects/hash_signature.py +0 -0
  40. {pythagoras-0.8.5 → pythagoras-0.8.9}/pythagoras/_02_ordinary_functions/__init__.py +0 -0
  41. {pythagoras-0.8.5 → pythagoras-0.8.9}/pythagoras/_02_ordinary_functions/assert_ordinarity.py +0 -0
  42. {pythagoras-0.8.5 → pythagoras-0.8.9}/pythagoras/_02_ordinary_functions/check_n_positional_args.py +0 -0
  43. {pythagoras-0.8.5 → pythagoras-0.8.9}/pythagoras/_02_ordinary_functions/code_normalizer.py +0 -0
  44. {pythagoras-0.8.5 → pythagoras-0.8.9}/pythagoras/_02_ordinary_functions/code_normalizer_implementation.py +0 -0
  45. {pythagoras-0.8.5 → pythagoras-0.8.9}/pythagoras/_02_ordinary_functions/ordinary_decorator.py +0 -0
  46. {pythagoras-0.8.5 → pythagoras-0.8.9}/pythagoras/_03_autonomous_functions/__init__.py +0 -0
  47. {pythagoras-0.8.5 → pythagoras-0.8.9}/pythagoras/_03_autonomous_functions/autonomicity_checks.py +0 -0
  48. {pythagoras-0.8.5 → pythagoras-0.8.9}/pythagoras/_03_autonomous_functions/call_graph_explorer.py +0 -0
  49. {pythagoras-0.8.5 → pythagoras-0.8.9}/pythagoras/_03_autonomous_functions/default_island_singleton.py +0 -0
  50. {pythagoras-0.8.5 → pythagoras-0.8.9}/pythagoras/_03_autonomous_functions/names_usage_analyzer.py +0 -0
  51. {pythagoras-0.8.5 → pythagoras-0.8.9}/pythagoras/_04_idempotent_functions/astkeywords_dict_convertors.py +0 -0
  52. {pythagoras-0.8.5 → pythagoras-0.8.9}/pythagoras/_04_idempotent_functions/idempotency_checks.py +0 -0
  53. {pythagoras-0.8.5 → pythagoras-0.8.9}/pythagoras/_04_idempotent_functions/process_augmented_func_src.py +0 -0
  54. {pythagoras-0.8.5/pythagoras/_99_misc_utils → pythagoras-0.8.9/pythagoras/_05_events_and_exceptions}/current_date_gmt_str.py +0 -0
  55. {pythagoras-0.8.5/pythagoras/_99_misc_utils → pythagoras-0.8.9/pythagoras/_05_events_and_exceptions}/notebook_checker.py +0 -0
  56. {pythagoras-0.8.5 → pythagoras-0.8.9}/pythagoras/_99_misc_utils/base_16_32_convertors.py +0 -0
  57. {pythagoras-0.8.5 → pythagoras-0.8.9}/pythagoras/_99_misc_utils/function_name.py +0 -0
  58. {pythagoras-0.8.5 → pythagoras-0.8.9}/pythagoras/_99_misc_utils/id_examiner.py +0 -0
  59. {pythagoras-0.8.5 → pythagoras-0.8.9}/pythagoras/_99_misc_utils/long_infoname.py +0 -0
  60. {pythagoras-0.8.5 → pythagoras-0.8.9}/pythagoras/_99_misc_utils/output_capturer.py +0 -0
  61. {pythagoras-0.8.5 → pythagoras-0.8.9}/pythagoras/_99_misc_utils/package_manager.py +0 -0
  62. {pythagoras-0.8.5 → pythagoras-0.8.9}/pythagoras/_99_misc_utils/random_safe_str_creator.py +0 -0
  63. {pythagoras-0.8.5 → pythagoras-0.8.9}/pythagoras/________OLD________/OLD_dependency_discovery.py +0 -0
  64. {pythagoras-0.8.5 → pythagoras-0.8.9}/pythagoras/________OLD________/OLD_environmental.py +0 -0
  65. {pythagoras-0.8.5 → pythagoras-0.8.9}/pythagoras/________OLD________/OLD_package_dependencies.py +0 -0
  66. {pythagoras-0.8.5 → pythagoras-0.8.9}/pythagoras/________OLD________/OLD_persidicts.py +0 -0
  67. {pythagoras-0.8.5 → pythagoras-0.8.9}/pythagoras/________OLD________/OLD_persiout.py +0 -0
  68. {pythagoras-0.8.5 → pythagoras-0.8.9}/pythagoras/________OLD________/OLD_persistent_dicts.py +0 -0
  69. {pythagoras-0.8.5 → pythagoras-0.8.9}/pythagoras/________OLD________/__init__.py +0 -0
  70. {pythagoras-0.8.5 → pythagoras-0.8.9}/pythagoras/________OLD________/test_package_dependencies.py +0 -0
  71. {pythagoras-0.8.5 → pythagoras-0.8.9}/pythagoras.egg-info/dependency_links.txt +0 -0
  72. {pythagoras-0.8.5 → pythagoras-0.8.9}/pythagoras.egg-info/top_level.txt +0 -0
  73. {pythagoras-0.8.5 → pythagoras-0.8.9}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pythagoras
3
- Version: 0.8.5
3
+ Version: 0.8.9
4
4
  Summary: Simple framework for planet-scale idempotent computations in Python.
5
5
  Home-page: https://github.com/vladlpavlov/pythagoras
6
6
  Author: Volodymyr (Vlad) Pavlov
@@ -34,6 +34,9 @@ Requires-Dist: boto3
34
34
  Requires-Dist: moto
35
35
  Requires-Dist: pytest
36
36
  Requires-Dist: autopep8
37
+ Requires-Dist: torch
38
+ Requires-Dist: keras
39
+ Requires-Dist: tensorflow
37
40
 
38
41
  # Pythagoras
39
42
 
@@ -74,6 +77,9 @@ at the Python package index at: https://pypi.org/project/pythagoras
74
77
  * [jsonpickle](https://jsonpickle.github.io)
75
78
  * [scikit-learn](https://scikit-learn.org)
76
79
  * [autopep8](https://pypi.org/project/autopep8)
80
+ * [pytorch](https://pytorch.org)
81
+ * [tensorflow](https://www.tensorflow.org)
82
+ * [keras](https://keras.io)
77
83
 
78
84
  ## Key Contacts
79
85
 
@@ -37,6 +37,9 @@ at the Python package index at: https://pypi.org/project/pythagoras
37
37
  * [jsonpickle](https://jsonpickle.github.io)
38
38
  * [scikit-learn](https://scikit-learn.org)
39
39
  * [autopep8](https://pypi.org/project/autopep8)
40
+ * [pytorch](https://pytorch.org)
41
+ * [tensorflow](https://www.tensorflow.org)
42
+ * [keras](https://keras.io)
40
43
 
41
44
  ## Key Contacts
42
45
 
@@ -79,10 +79,11 @@ class HashAddress(SafeStrTuple, ABC):
79
79
  address = cls.__new__(cls)
80
80
  super(cls, address).__init__(prefix, hash_value)
81
81
  if assert_readiness:
82
- assert address.ready()
82
+ assert address.ready
83
83
  return address
84
84
 
85
85
 
86
+ @property
86
87
  @abstractmethod
87
88
  def ready(self) -> bool:
88
89
  """Check if address points to a value that is ready to be retrieved."""
@@ -38,17 +38,18 @@ class ValueAddress(HashAddress):
38
38
 
39
39
  super().__init__(prefix, hash_value)
40
40
 
41
- if push_to_cloud and not (self in pth.value_store):
42
- pth.value_store[self] = data
41
+ if push_to_cloud and not (self in pth.global_value_store):
42
+ pth.global_value_store[self] = data
43
43
 
44
+ @property
44
45
  def ready(self):
45
46
  """Check if address points to a value that is ready to be retrieved."""
46
- return self in pth.value_store
47
+ return self in pth.global_value_store
47
48
 
48
49
 
49
50
  def get(self, timeout:Optional[int] = None) -> Any:
50
51
  """Retrieve value, referenced by the address"""
51
- return pth.value_store[self]
52
+ return pth.global_value_store[self]
52
53
 
53
54
  def get_typed(self
54
55
  ,expected_type:Type[T]
@@ -38,4 +38,8 @@ class OrdinaryFunction:
38
38
 
39
39
  @property
40
40
  def decorator(self):
41
- return "@pth.ordinary()"
41
+ return "@pth.ordinary()"
42
+
43
+ @property
44
+ def f_name(self):
45
+ return self.name
@@ -35,7 +35,7 @@ from pythagoras._03_autonomous_functions.default_island_singleton import (
35
35
  DefaultIslandType, DefaultIsland)
36
36
  from pythagoras._03_autonomous_functions.autonomous_funcs import (
37
37
  AutonomousFunction)
38
- from pythagoras._05_mission_control.global_state_management import (
38
+ from pythagoras._06_mission_control.global_state_management import (
39
39
  is_fully_unitialized)
40
40
 
41
41
 
@@ -17,12 +17,16 @@ from pythagoras._03_autonomous_functions.call_graph_explorer import (
17
17
 
18
18
  from pythagoras._03_autonomous_functions.names_usage_analyzer import (
19
19
  analyze_names_in_function)
20
- from pythagoras._05_mission_control.events_and_exceptions_core import \
21
- EventLogger
22
20
 
23
- from pythagoras._05_mission_control.global_state_management import (
21
+ from pythagoras._05_events_and_exceptions.find_in_callstack import (
22
+ find_local_var_in_callstack)
23
+
24
+ from pythagoras._05_events_and_exceptions.event_logger import (
25
+ log_exception)
26
+
27
+ from pythagoras._06_mission_control.global_state_management import (
24
28
  is_correctly_initialized)
25
- from pythagoras._99_misc_utils.find_in_callstack import find_in_callstack
29
+
26
30
 
27
31
 
28
32
  class AutonomousFunction(OrdinaryFunction):
@@ -139,7 +143,7 @@ class AutonomousFunction(OrdinaryFunction):
139
143
  return True
140
144
 
141
145
 
142
- def __call__(self, **kwargs) -> Any:
146
+ def execute(self, **kwargs) -> Any:
143
147
  try:
144
148
  assert self.runtime_checks()
145
149
  names_dict = dict()
@@ -163,6 +167,9 @@ class AutonomousFunction(OrdinaryFunction):
163
167
  log_exception()
164
168
  raise e
165
169
 
170
+ def __call__(self, **kwargs) -> Any:
171
+ return self.execute(**kwargs)
172
+
166
173
  def __getstate__(self):
167
174
  draft_state = dict(name = self.name
168
175
  , naked_source_code = self.naked_source_code
@@ -226,21 +233,6 @@ def register_autonomous_function(f: AutonomousFunction) -> None:
226
233
  assert not hasattr(f, "_runtime_checks_passed")
227
234
  assert not hasattr(f, "_dependencies")
228
235
 
229
- def log_exception():
230
- callers = find_in_callstack(name_to_find="self"
231
- , class_to_find=AutonomousFunction)
232
- caller_name = ""
233
- if len(callers) > 0:
234
- caller_name = callers[0].name + "_"
235
- (exc_type, exc_value, trace_back) = sys.exc_info()
236
- exception_description = traceback.format_exception(
237
- exc_type, exc_value, trace_back)
238
- logger = EventLogger(event_log = pth.crash_history
239
- , prefix = caller_name + exc_type.__name__
240
- , save_context = True)
241
- logger.log_event(exception=exc_value
242
- , exception_description=exception_description)
243
-
244
236
 
245
237
  class EventPosterFactory:
246
238
  def __init__(self, silent:bool = False):
@@ -260,8 +252,8 @@ class EventPoster:
260
252
  self.silent = silent
261
253
 
262
254
  def __call__(self, **event_args)-> None:
263
- callers = find_in_callstack(name_to_find="self"
264
- , class_to_find=AutonomousFunction)
255
+ callers = find_local_var_in_callstack(name_to_find="self"
256
+ , class_to_find=AutonomousFunction)
265
257
  caller_name = ""
266
258
  if len(callers) > 0:
267
259
  caller_name = callers[0].name
@@ -272,11 +264,10 @@ class EventPoster:
272
264
  caller_prefix = caller_name
273
265
  caller_type = "AutonomousFunction"
274
266
  prefix = caller_prefix + self.label
275
- logger = EventLogger(event_log=pth.event_log
267
+ logger = EventLogger(event_log=pth.global_event_log
276
268
  , prefix=prefix, save_context=False)
277
- logger.log_event(**event_args)
278
- if not self.silent:
279
269
 
270
+ if not self.silent:
280
271
  print(30*"~")
281
272
  print(f"Event '{self.label}' "
282
273
  + f"inside an {caller_type} '{caller_name}':")
@@ -284,5 +275,17 @@ class EventPoster:
284
275
  print(f" {key} = {value}")
285
276
  print()
286
277
 
278
+ label_arg_name = "event_label"
279
+ while label_arg_name in event_args:
280
+ label_arg_name += "_"
281
+ event_args[label_arg_name] = self.label
282
+
283
+ caller_arg_name = "caller_name"
284
+ while caller_arg_name in event_args:
285
+ caller_arg_name += "_"
286
+ event_args[caller_arg_name] = caller_name
287
+
288
+ logger.log_event(**event_args)
289
+
287
290
  post_event: EventPosterFactory = EventPosterFactory(silent=True)
288
291
  print_event: EventPosterFactory = EventPosterFactory(silent=False)
@@ -2,7 +2,7 @@ from pythagoras._04_idempotent_functions.kw_args import (
2
2
  SortedKwArgs, PackedKwArgs, UnpackedKwArgs)
3
3
 
4
4
  from pythagoras._04_idempotent_functions.idempotent_func_and_address import (
5
- IdempotentFunction)
5
+ IdempotentFunction, FuncOutputAddress)
6
6
 
7
7
  from pythagoras._04_idempotent_functions.idempotent_decorator import (
8
8
  idempotent)
@@ -8,7 +8,7 @@ from pythagoras._03_autonomous_functions.default_island_singleton import (
8
8
  from pythagoras._04_idempotent_functions.idempotent_func_and_address import (
9
9
  IdempotentFunction)
10
10
 
11
- from pythagoras._05_mission_control.global_state_management import (
11
+ from pythagoras._06_mission_control.global_state_management import (
12
12
  is_fully_unitialized)
13
13
 
14
14
 
@@ -4,12 +4,13 @@ import time
4
4
  from typing import Callable, Any
5
5
 
6
6
  import pythagoras as pth
7
+ from pythagoras import get_random_safe_str
7
8
 
8
9
  from pythagoras._01_foundational_objects.hash_addresses import HashAddress
9
10
  from pythagoras._01_foundational_objects.value_addresses import ValueAddress
10
11
 
11
12
  from pythagoras._03_autonomous_functions.autonomous_funcs import (
12
- AutonomousFunction, register_autonomous_function, log_exception)
13
+ AutonomousFunction, register_autonomous_function)
13
14
 
14
15
  from pythagoras._02_ordinary_functions.ordinary_funcs import (
15
16
  OrdinaryFunction)
@@ -21,6 +22,8 @@ from pythagoras._04_idempotent_functions.kw_args import (
21
22
  UnpackedKwArgs, PackedKwArgs, SortedKwArgs)
22
23
  from pythagoras._04_idempotent_functions.process_augmented_func_src import (
23
24
  process_augmented_func_src)
25
+ from pythagoras._05_events_and_exceptions.context_utils import build_context
26
+
24
27
 
25
28
  class IdempotentFunction(AutonomousFunction):
26
29
  augmented_code_checked: bool
@@ -105,16 +108,45 @@ class IdempotentFunction(AutonomousFunction):
105
108
 
106
109
  self.augmented_code_checked = True
107
110
 
108
- def __call__(self, **kwargs) -> Any:
111
+
112
+ def execute(self, **kwargs) -> Any:
109
113
  packed_kwargs = PackedKwArgs(**kwargs)
110
114
  output_address = FuncOutputAddress(self, packed_kwargs)
111
- if output_address.ready():
115
+ _pth_f_addr_ = output_address
116
+ if output_address.ready:
112
117
  return output_address.get()
118
+ output_address.request_execution()
119
+ registration_addr = (output_address[0]
120
+ , output_address[1], get_random_safe_str())
121
+ pth.function_execution_attempts[registration_addr] = build_context()
113
122
  unpacked_kwargs = UnpackedKwArgs(**packed_kwargs)
114
- result = super().__call__(**unpacked_kwargs)
123
+ result = super().execute(**unpacked_kwargs)
115
124
  pth.function_output_store[output_address] = ValueAddress(result)
125
+ pth.function_execution_requests.delete_if_exists(output_address)
116
126
  return result
117
127
 
128
+ def list_execute(self, list_of_kwargs:list[dict]) -> Any:
129
+ assert isinstance(list_of_kwargs, (list, tuple))
130
+ for kwargs in list_of_kwargs:
131
+ assert isinstance(kwargs, dict)
132
+ addrs = []
133
+ for kwargs in list_of_kwargs:
134
+ new_addr = FuncOutputAddress(self, kwargs)
135
+ new_addr.request_execution()
136
+ addrs.append(new_addr)
137
+ addrs_indexed = list(zip(range(len(addrs)), addrs))
138
+ pth.entropy_infuser.shuffle(addrs_indexed)
139
+ results_dict = dict()
140
+ for n, an_addr in addrs_indexed:
141
+ results_dict[n] = an_addr.function.execute(**an_addr.arguments)
142
+ results_list = [results_dict[n] for n in range(len(addrs))]
143
+ return results_list
144
+
145
+ def execution_attempts(self, **kwargs) -> list:
146
+ packed_kwargs = PackedKwArgs(**kwargs)
147
+ output_address = FuncOutputAddress(self, packed_kwargs)
148
+ return output_address.execution_attempts
149
+
118
150
 
119
151
  def register_idempotent_function(f: IdempotentFunction) -> None:
120
152
  """Register an idempotent function in the Pythagoras system."""
@@ -143,8 +175,18 @@ class FuncOutputAddress(HashAddress):
143
175
  tmp = ValueAddress(signature)
144
176
  super().__init__(tmp.prefix, tmp.hash_value)
145
177
 
178
+ @property
146
179
  def ready(self):
147
- return self in pth.function_output_store
180
+ result = self in pth.function_output_store
181
+ return result
182
+
183
+ def request_execution(self):
184
+ if self in pth.function_output_store:
185
+ if self in pth.function_execution_requests:
186
+ del pth.function_execution_requests[self]
187
+ else:
188
+ if self not in pth.function_execution_requests:
189
+ pth.function_execution_requests[self] = True
148
190
 
149
191
  def get(self, timeout: int = None):
150
192
  """Retrieve value, referenced by the address.
@@ -152,14 +194,20 @@ class FuncOutputAddress(HashAddress):
152
194
  If the value is not immediately available, backoff exponentially
153
195
  till timeout is exceeded. If timeout is None, keep trying forever.
154
196
  """
197
+ if self.ready:
198
+ return pth.global_value_store[pth.function_output_store[self]]
199
+ self.request_execution()
200
+
155
201
  start_time, backoff_period = time.time(), 1.0
156
202
  stop_time = (start_time + timeout) if timeout else None
157
203
  # start_time, stop_time and backoff_period are in seconds
204
+
158
205
  while True:
159
- try:
160
- address = pth.function_output_store[self]
161
- return pth.value_store[address]
162
- except:
206
+ if self.ready:
207
+ result = pth.global_value_store[pth.function_output_store[self]]
208
+ pth.function_execution_requests.delete_if_exists(self)
209
+ return result
210
+ else:
163
211
  time.sleep(backoff_period)
164
212
  backoff_period *= 2.0
165
213
  backoff_period += pth.entropy_infuser.uniform(-0.5, 0.5)
@@ -185,9 +233,62 @@ class FuncOutputAddress(HashAddress):
185
233
  signature = signature_addr.get()
186
234
  return signature.f_name
187
235
 
236
+ @property
237
+ def island_name(self) -> str:
238
+ return self.function.island_name
239
+
188
240
  @property
189
241
  def arguments(self) -> SortedKwArgs:
190
242
  signature_addr = ValueAddress.from_strings(
191
243
  prefix=self.prefix, hash_value=self.hash_value)
192
244
  signature = signature_addr.get()
193
- return signature.args_addr.get()
245
+ return signature.args_addr.get()
246
+
247
+ @property
248
+ def can_be_executed(self) -> bool:
249
+ """Indicates if the function can be executed in the current session.
250
+
251
+ Currently, it's just a placeholder that always returns True.
252
+
253
+ The function should fe refactored once we start supporting
254
+ VALIDATORS, CORRECTORS and SEQUENCERS
255
+ """
256
+ return True
257
+
258
+ @property
259
+ def needs_execution(self) -> bool:
260
+ """Indicates if the function is a good candidate for execution.
261
+
262
+ Returns False if the result is already available, or if some other
263
+ process is currently working on it. Otherwise, returns True.
264
+ """
265
+ DEFAULT_EXECUTION_TIME = 10
266
+ MAX_EXECUTION_ATTEMPTS = 5
267
+ # TODO: these should not be constants
268
+ if self.ready:
269
+ return False
270
+ past_attempts = pth.function_execution_attempts.get_subdict(self)
271
+ n_past_attempts = len(past_attempts)
272
+ if n_past_attempts == 0:
273
+ return True
274
+ if n_past_attempts > MAX_EXECUTION_ATTEMPTS:
275
+ #TODO: log this event. Should we have DLQ?
276
+ return False
277
+ most_recent_timestamp = max(
278
+ past_attempts.mtimestamp(a) for a in past_attempts)
279
+ current_timestamp = time.time()
280
+ if (current_timestamp - most_recent_timestamp
281
+ > DEFAULT_EXECUTION_TIME*(2**n_past_attempts)):
282
+ return True
283
+ return False
284
+
285
+ @property
286
+ def execution_attempts(self) -> list:
287
+ attempts = pth.function_execution_attempts.get_subdict(self)
288
+ attemps_timed = {-attempts.mtimestamp(a):attempts[a] for a in attempts}
289
+ times = sorted(attemps_timed)
290
+ result = []
291
+ for t in times:
292
+ result.append(attemps_timed[t])
293
+ return result
294
+
@@ -25,7 +25,7 @@ class SortedKwArgs(dict):
25
25
  unpacked_copy = dict()
26
26
  for k,v in self.items():
27
27
  if isinstance(v, ValueAddress):
28
- unpacked_copy[k] = pth.value_store[v]
28
+ unpacked_copy[k] = pth.global_value_store[v]
29
29
  else:
30
30
  unpacked_copy[k] = v
31
31
  return unpacked_copy
@@ -0,0 +1,2 @@
1
+ from pythagoras._05_events_and_exceptions.notebook_checker import (
2
+ is_executed_in_notebook)
@@ -5,7 +5,8 @@ import socket
5
5
  from typing import Dict
6
6
  from getpass import getuser
7
7
  from datetime import datetime
8
- from pythagoras._99_misc_utils.notebook_checker import is_executed_in_notebook
8
+ import torch
9
+ from pythagoras._05_events_and_exceptions.notebook_checker import is_executed_in_notebook
9
10
 
10
11
  def build_context()-> Dict:
11
12
  """Capture core information about execution environment.
@@ -14,7 +15,6 @@ def build_context()-> Dict:
14
15
  to help debug (distributed) applications.
15
16
  """
16
17
  cwd = os.getcwd()
17
- is_in_notebook = is_executed_in_notebook()
18
18
 
19
19
  context = dict(
20
20
  hostname = socket.gethostname()
@@ -26,12 +26,20 @@ def build_context()-> Dict:
26
26
  ,processor = platform.processor()
27
27
  ,cpu_count = psutil.cpu_count()
28
28
  ,cpu_load_avg = psutil.getloadavg()
29
+ ,cuda_gpu_count=torch.cuda.device_count()
29
30
  ,disk_usage = psutil.disk_usage(cwd)
30
31
  ,virtual_memory = psutil.virtual_memory()
31
- ,cwd = os.getcwd()
32
32
  ,working_directory = cwd
33
33
  ,local_timezone = datetime.now().astimezone().tzname()
34
- ,is_in_notebook = is_in_notebook
34
+ ,is_in_notebook = is_executed_in_notebook()
35
35
  )
36
36
 
37
- return context
37
+ return context
38
+
39
+
40
+ def add_context(**kwargs):
41
+ context_param_name = "context"
42
+ while context_param_name in kwargs:
43
+ context_param_name += "_"
44
+ kwargs[context_param_name] = build_context()
45
+ return kwargs
@@ -0,0 +1,88 @@
1
+ import sys
2
+ import traceback
3
+ from copy import copy
4
+
5
+ from pythagoras._05_events_and_exceptions.context_utils import add_context
6
+ from pythagoras._05_events_and_exceptions.current_date_gmt_str import (
7
+ current_date_gmt_string)
8
+ from pythagoras._05_events_and_exceptions.find_in_callstack import (
9
+ find_local_var_in_callstack)
10
+ from pythagoras._05_events_and_exceptions.type_retrievers import (
11
+ retrieve_AutonomousFunction_class, retrieve_FuncOutputAddress_class)
12
+ from pythagoras._99_misc_utils.random_safe_str_creator import (
13
+ get_random_safe_str)
14
+
15
+ from persidict import PersiDict
16
+ import pythagoras as pth
17
+
18
+
19
+ def log_one_entry(event_log_dict:PersiDict
20
+ , path:list[str] | None
21
+ , prefixes: list[str] | None
22
+ , name:str
23
+ , event:any):
24
+ if prefixes is not None and len(prefixes) > 0:
25
+ name = "_".join(prefixes + [name])
26
+ if path is None or len(path) == 0:
27
+ address = [name]
28
+ else:
29
+ address = path + [name]
30
+ event_log_dict[address] = event
31
+
32
+ def log_two_mirrored_entries(
33
+ global_event_log_dict:PersiDict | None
34
+ , local_event_log_dict:PersiDict | None
35
+ , prefix: str | None
36
+ , event: any):
37
+ func_addrs = find_local_var_in_callstack(name_to_find="_pth_f_addr_"
38
+ , class_to_find = retrieve_FuncOutputAddress_class())
39
+ if len(func_addrs) > 0:
40
+ func_pointer = func_addrs[0]
41
+ local_path = list(func_pointer)
42
+ else:
43
+ funcs = find_local_var_in_callstack(name_to_find="self"
44
+ , class_to_find=retrieve_AutonomousFunction_class())
45
+ if len(funcs) > 0:
46
+ func_pointer = funcs[0]
47
+ local_path = None
48
+ else:
49
+ assert False, "No function pointer found in callstack"
50
+
51
+ island_name = func_pointer.island_name
52
+ function_name = func_pointer.f_name
53
+ event_name = get_random_safe_str()
54
+
55
+ if prefix is not None:
56
+ prefixes = [prefix]
57
+ else:
58
+ prefixes = []
59
+
60
+ if global_event_log_dict is not None:
61
+ if island_name is not None:
62
+ global_prefixes = [island_name] + prefixes
63
+ else:
64
+ global_prefixes = copy(prefixes)
65
+ global_prefixes = [function_name] + global_prefixes
66
+ global_path = [current_date_gmt_string()]
67
+ log_one_entry(global_event_log_dict
68
+ , global_path, global_prefixes, event_name, event)
69
+
70
+ if local_event_log_dict is not None and local_path is not None:
71
+ local_prefixes = copy(prefixes)
72
+ log_one_entry(local_event_log_dict
73
+ , local_path, local_prefixes, event_name, event)
74
+
75
+
76
+ def log_exception():
77
+ (exc_type, exc_value, trace_back) = sys.exc_info()
78
+ description = traceback.format_exception(
79
+ exc_type, exc_value, trace_back)
80
+ event = add_context(
81
+ exception_description=description
82
+ ,exception=exc_value
83
+ ,trace_back= trace_back)
84
+ log_two_mirrored_entries(
85
+ global_event_log_dict=pth.global_crash_history
86
+ , local_event_log_dict=pth.function_crash_history
87
+ , prefix=exc_type.__name__
88
+ , event=event)
@@ -1,11 +1,11 @@
1
1
  import inspect
2
2
  from typing import List, Any
3
3
 
4
- def find_in_callstack(
4
+ def find_local_var_in_callstack(
5
5
  name_to_find: str
6
- , class_to_find: None | type = None
6
+ , class_to_find: None | type | str = None
7
7
  ) -> List[Any]:
8
- """ Search the entire call stack for objects with the specified name/type.
8
+ """ Search the entire call stack for local objects with the specified name/type.
9
9
 
10
10
  If the callstack contains objects
11
11
  with name name_to_find and type class_to_find,
@@ -19,16 +19,18 @@ def find_in_callstack(
19
19
 
20
20
  Parameters:
21
21
  name_to_find (str): The name of the object to search for.
22
- class_to_find (None | type): The type of the object to search for.
22
+ class_to_find (None | type | str): The type of the object to search for.
23
23
 
24
24
  Returns:
25
- List[Any]: A list of objects found in the stack
25
+ List[Any]: A list of local objects found in the stack
26
26
  with the specified name/type.
27
27
  The list contains no duplicates.
28
28
  """
29
29
 
30
30
  assert isinstance(name_to_find, str) and len(name_to_find)
31
- assert class_to_find is None or inspect.isclass(class_to_find)
31
+ assert (class_to_find is None
32
+ or inspect.isclass(class_to_find)
33
+ or isinstance(class_to_find, str))
32
34
 
33
35
  found_objects = []
34
36
 
@@ -36,11 +38,12 @@ def find_in_callstack(
36
38
  frame = frame_info.frame
37
39
  if name_to_find in frame.f_locals:
38
40
  candidate = frame.f_locals[name_to_find]
39
- if class_to_find is None or isinstance(candidate, class_to_find):
41
+ if class_to_find is None:
40
42
  found_objects.append(candidate)
41
- if name_to_find in frame.f_globals:
42
- candidate = frame.f_globals[name_to_find]
43
- if class_to_find is None or isinstance(candidate, class_to_find):
43
+ elif isinstance(class_to_find, str):
44
+ if candidate.__class__.__name__ == class_to_find:
45
+ found_objects.append(candidate)
46
+ elif isinstance(candidate, class_to_find):
44
47
  found_objects.append(candidate)
45
48
 
46
49
  dedup_dict = dict()
@@ -0,0 +1,30 @@
1
+ import sys
2
+
3
+ def retrieve_IdempotentFunction_class() -> type:
4
+ """Return the IdempotentFunction class.
5
+
6
+ This is for-internal-use-only function, created to
7
+ avoid circular import dependencies.
8
+ """
9
+ return sys.modules['pythagoras'].IdempotentFunction
10
+
11
+
12
+ def retrieve_AutonomousFunction_class() -> type:
13
+ """Return the AutonomousFunction class.
14
+
15
+ This is for-internal-use-only function, created to
16
+ avoid circular import dependencies.
17
+ """
18
+ return sys.modules['pythagoras'].AutonomousFunction
19
+
20
+
21
+ def retrieve_FuncOutputAddress_class() -> type:
22
+ """Return the FuncOutputAddress class.
23
+
24
+ This is for-internal-use-only function, created to
25
+ avoid circular import dependencies.
26
+ """
27
+ return sys.modules['pythagoras'].FuncOutputAddress
28
+
29
+
30
+