pythagoras 0.8.6__tar.gz → 0.8.8__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 (71) hide show
  1. {pythagoras-0.8.6/pythagoras.egg-info → pythagoras-0.8.8}/PKG-INFO +7 -1
  2. {pythagoras-0.8.6 → pythagoras-0.8.8}/README.md +3 -0
  3. {pythagoras-0.8.6 → pythagoras-0.8.8}/pythagoras/_01_foundational_objects/hash_addresses.py +2 -1
  4. {pythagoras-0.8.6 → pythagoras-0.8.8}/pythagoras/_01_foundational_objects/value_addresses.py +5 -4
  5. {pythagoras-0.8.6 → pythagoras-0.8.8}/pythagoras/_03_autonomous_functions/autonomous_decorators.py +1 -1
  6. {pythagoras-0.8.6 → pythagoras-0.8.8}/pythagoras/_03_autonomous_functions/autonomous_funcs.py +13 -10
  7. {pythagoras-0.8.6 → pythagoras-0.8.8}/pythagoras/_04_idempotent_functions/__init__.py +1 -1
  8. {pythagoras-0.8.6 → pythagoras-0.8.8}/pythagoras/_04_idempotent_functions/idempotent_decorator.py +1 -1
  9. {pythagoras-0.8.6 → pythagoras-0.8.8}/pythagoras/_04_idempotent_functions/idempotent_func_and_address.py +106 -10
  10. {pythagoras-0.8.6 → pythagoras-0.8.8}/pythagoras/_04_idempotent_functions/kw_args.py +1 -1
  11. pythagoras-0.8.8/pythagoras/_05_events_and_exceptions/__init__.py +2 -0
  12. {pythagoras-0.8.6/pythagoras/_99_misc_utils → pythagoras-0.8.8/pythagoras/_05_events_and_exceptions}/context_builder.py +4 -4
  13. {pythagoras-0.8.6/pythagoras/_05_mission_control → pythagoras-0.8.8/pythagoras/_05_events_and_exceptions}/events_and_exceptions_core.py +4 -4
  14. {pythagoras-0.8.6/pythagoras/_99_misc_utils → pythagoras-0.8.8/pythagoras/_05_events_and_exceptions}/find_in_callstack.py +13 -10
  15. pythagoras-0.8.8/pythagoras/_05_events_and_exceptions/type_retrievers.py +30 -0
  16. {pythagoras-0.8.6/pythagoras/_05_mission_control → pythagoras-0.8.8/pythagoras/_06_mission_control}/__events_and_exceptions_OLD__.py +1 -1
  17. {pythagoras-0.8.6/pythagoras/_05_mission_control → pythagoras-0.8.8/pythagoras/_06_mission_control}/__init__.py +2 -2
  18. {pythagoras-0.8.6/pythagoras/_05_mission_control → pythagoras-0.8.8/pythagoras/_06_mission_control}/global_state_management.py +48 -19
  19. {pythagoras-0.8.6/pythagoras/_05_mission_control → pythagoras-0.8.8/pythagoras/_06_mission_control}/summary.py +2 -2
  20. {pythagoras-0.8.6 → pythagoras-0.8.8}/pythagoras/_99_misc_utils/__init__.py +0 -3
  21. {pythagoras-0.8.6 → pythagoras-0.8.8}/pythagoras/________OLD________/NEW_hash_address.py +1 -1
  22. {pythagoras-0.8.6 → pythagoras-0.8.8}/pythagoras/__init__.py +11 -4
  23. {pythagoras-0.8.6 → pythagoras-0.8.8}/pythagoras/__main__.py +1 -1
  24. {pythagoras-0.8.6 → pythagoras-0.8.8/pythagoras.egg-info}/PKG-INFO +7 -1
  25. {pythagoras-0.8.6 → pythagoras-0.8.8}/pythagoras.egg-info/SOURCES.txt +11 -10
  26. {pythagoras-0.8.6 → pythagoras-0.8.8}/pythagoras.egg-info/requires.txt +3 -0
  27. {pythagoras-0.8.6 → pythagoras-0.8.8}/setup.py +7 -3
  28. pythagoras-0.8.6/pythagoras/_99_misc_utils/isinstance_txt.py +0 -34
  29. {pythagoras-0.8.6 → pythagoras-0.8.8}/LICENSE +0 -0
  30. {pythagoras-0.8.6 → pythagoras-0.8.8}/pyproject.toml +0 -0
  31. {pythagoras-0.8.6 → pythagoras-0.8.8}/pythagoras/NEW_exception_logging.py +0 -0
  32. {pythagoras-0.8.6 → pythagoras-0.8.8}/pythagoras/OLD__main__.py +0 -0
  33. {pythagoras-0.8.6 → pythagoras-0.8.8}/pythagoras/OLD_p_cloud.py +0 -0
  34. {pythagoras-0.8.6 → pythagoras-0.8.8}/pythagoras/OLD_utils.py +0 -0
  35. {pythagoras-0.8.6 → pythagoras-0.8.8}/pythagoras/_01_foundational_objects/__init__.py +0 -0
  36. {pythagoras-0.8.6 → pythagoras-0.8.8}/pythagoras/_01_foundational_objects/hash_signature.py +0 -0
  37. {pythagoras-0.8.6 → pythagoras-0.8.8}/pythagoras/_02_ordinary_functions/__init__.py +0 -0
  38. {pythagoras-0.8.6 → pythagoras-0.8.8}/pythagoras/_02_ordinary_functions/assert_ordinarity.py +0 -0
  39. {pythagoras-0.8.6 → pythagoras-0.8.8}/pythagoras/_02_ordinary_functions/check_n_positional_args.py +0 -0
  40. {pythagoras-0.8.6 → pythagoras-0.8.8}/pythagoras/_02_ordinary_functions/code_normalizer.py +0 -0
  41. {pythagoras-0.8.6 → pythagoras-0.8.8}/pythagoras/_02_ordinary_functions/code_normalizer_implementation.py +0 -0
  42. {pythagoras-0.8.6 → pythagoras-0.8.8}/pythagoras/_02_ordinary_functions/ordinary_decorator.py +0 -0
  43. {pythagoras-0.8.6 → pythagoras-0.8.8}/pythagoras/_02_ordinary_functions/ordinary_funcs.py +0 -0
  44. {pythagoras-0.8.6 → pythagoras-0.8.8}/pythagoras/_03_autonomous_functions/__init__.py +0 -0
  45. {pythagoras-0.8.6 → pythagoras-0.8.8}/pythagoras/_03_autonomous_functions/autonomicity_checks.py +0 -0
  46. {pythagoras-0.8.6 → pythagoras-0.8.8}/pythagoras/_03_autonomous_functions/call_graph_explorer.py +0 -0
  47. {pythagoras-0.8.6 → pythagoras-0.8.8}/pythagoras/_03_autonomous_functions/default_island_singleton.py +0 -0
  48. {pythagoras-0.8.6 → pythagoras-0.8.8}/pythagoras/_03_autonomous_functions/names_usage_analyzer.py +0 -0
  49. {pythagoras-0.8.6 → pythagoras-0.8.8}/pythagoras/_04_idempotent_functions/astkeywords_dict_convertors.py +0 -0
  50. {pythagoras-0.8.6 → pythagoras-0.8.8}/pythagoras/_04_idempotent_functions/idempotency_checks.py +0 -0
  51. {pythagoras-0.8.6 → pythagoras-0.8.8}/pythagoras/_04_idempotent_functions/process_augmented_func_src.py +0 -0
  52. {pythagoras-0.8.6/pythagoras/_99_misc_utils → pythagoras-0.8.8/pythagoras/_05_events_and_exceptions}/current_date_gmt_str.py +0 -0
  53. {pythagoras-0.8.6/pythagoras/_99_misc_utils → pythagoras-0.8.8/pythagoras/_05_events_and_exceptions}/notebook_checker.py +0 -0
  54. {pythagoras-0.8.6 → pythagoras-0.8.8}/pythagoras/_99_misc_utils/base_16_32_convertors.py +0 -0
  55. {pythagoras-0.8.6 → pythagoras-0.8.8}/pythagoras/_99_misc_utils/function_name.py +0 -0
  56. {pythagoras-0.8.6 → pythagoras-0.8.8}/pythagoras/_99_misc_utils/id_examiner.py +0 -0
  57. {pythagoras-0.8.6 → pythagoras-0.8.8}/pythagoras/_99_misc_utils/long_infoname.py +0 -0
  58. {pythagoras-0.8.6 → pythagoras-0.8.8}/pythagoras/_99_misc_utils/output_capturer.py +0 -0
  59. {pythagoras-0.8.6 → pythagoras-0.8.8}/pythagoras/_99_misc_utils/package_manager.py +0 -0
  60. {pythagoras-0.8.6 → pythagoras-0.8.8}/pythagoras/_99_misc_utils/random_safe_str_creator.py +0 -0
  61. {pythagoras-0.8.6 → pythagoras-0.8.8}/pythagoras/________OLD________/OLD_dependency_discovery.py +0 -0
  62. {pythagoras-0.8.6 → pythagoras-0.8.8}/pythagoras/________OLD________/OLD_environmental.py +0 -0
  63. {pythagoras-0.8.6 → pythagoras-0.8.8}/pythagoras/________OLD________/OLD_package_dependencies.py +0 -0
  64. {pythagoras-0.8.6 → pythagoras-0.8.8}/pythagoras/________OLD________/OLD_persidicts.py +0 -0
  65. {pythagoras-0.8.6 → pythagoras-0.8.8}/pythagoras/________OLD________/OLD_persiout.py +0 -0
  66. {pythagoras-0.8.6 → pythagoras-0.8.8}/pythagoras/________OLD________/OLD_persistent_dicts.py +0 -0
  67. {pythagoras-0.8.6 → pythagoras-0.8.8}/pythagoras/________OLD________/__init__.py +0 -0
  68. {pythagoras-0.8.6 → pythagoras-0.8.8}/pythagoras/________OLD________/test_package_dependencies.py +0 -0
  69. {pythagoras-0.8.6 → pythagoras-0.8.8}/pythagoras.egg-info/dependency_links.txt +0 -0
  70. {pythagoras-0.8.6 → pythagoras-0.8.8}/pythagoras.egg-info/top_level.txt +0 -0
  71. {pythagoras-0.8.6 → pythagoras-0.8.8}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pythagoras
3
- Version: 0.8.6
3
+ Version: 0.8.8
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]
@@ -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,12 @@ 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 \
20
+ from pythagoras._05_events_and_exceptions.events_and_exceptions_core import \
21
21
  EventLogger
22
22
 
23
- from pythagoras._05_mission_control.global_state_management import (
23
+ from pythagoras._06_mission_control.global_state_management import (
24
24
  is_correctly_initialized)
25
- from pythagoras._99_misc_utils.find_in_callstack import find_in_callstack
25
+ from pythagoras._05_events_and_exceptions.find_in_callstack import find_local_var_in_callstack
26
26
 
27
27
 
28
28
  class AutonomousFunction(OrdinaryFunction):
@@ -139,7 +139,7 @@ class AutonomousFunction(OrdinaryFunction):
139
139
  return True
140
140
 
141
141
 
142
- def __call__(self, **kwargs) -> Any:
142
+ def execute(self, **kwargs) -> Any:
143
143
  try:
144
144
  assert self.runtime_checks()
145
145
  names_dict = dict()
@@ -163,6 +163,9 @@ class AutonomousFunction(OrdinaryFunction):
163
163
  log_exception()
164
164
  raise e
165
165
 
166
+ def __call__(self, **kwargs) -> Any:
167
+ return self.execute(**kwargs)
168
+
166
169
  def __getstate__(self):
167
170
  draft_state = dict(name = self.name
168
171
  , naked_source_code = self.naked_source_code
@@ -227,15 +230,15 @@ def register_autonomous_function(f: AutonomousFunction) -> None:
227
230
  assert not hasattr(f, "_dependencies")
228
231
 
229
232
  def log_exception():
230
- callers = find_in_callstack(name_to_find="self"
231
- , class_to_find=AutonomousFunction)
233
+ callers = find_local_var_in_callstack(name_to_find="self"
234
+ , class_to_find=AutonomousFunction)
232
235
  caller_name = ""
233
236
  if len(callers) > 0:
234
237
  caller_name = callers[0].name + "_"
235
238
  (exc_type, exc_value, trace_back) = sys.exc_info()
236
239
  exception_description = traceback.format_exception(
237
240
  exc_type, exc_value, trace_back)
238
- logger = EventLogger(event_log = pth.crash_history
241
+ logger = EventLogger(event_log = pth.global_crash_history
239
242
  , prefix = caller_name + exc_type.__name__
240
243
  , save_context = True)
241
244
  logger.log_event(exception=exc_value
@@ -260,8 +263,8 @@ class EventPoster:
260
263
  self.silent = silent
261
264
 
262
265
  def __call__(self, **event_args)-> None:
263
- callers = find_in_callstack(name_to_find="self"
264
- , class_to_find=AutonomousFunction)
266
+ callers = find_local_var_in_callstack(name_to_find="self"
267
+ , class_to_find=AutonomousFunction)
265
268
  caller_name = ""
266
269
  if len(callers) > 0:
267
270
  caller_name = callers[0].name
@@ -272,7 +275,7 @@ class EventPoster:
272
275
  caller_prefix = caller_name
273
276
  caller_type = "AutonomousFunction"
274
277
  prefix = caller_prefix + self.label
275
- logger = EventLogger(event_log=pth.event_log
278
+ logger = EventLogger(event_log=pth.global_event_log
276
279
  , prefix=prefix, save_context=False)
277
280
 
278
281
  if not self.silent:
@@ -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_builder import build_context
26
+
24
27
 
25
28
  class IdempotentFunction(AutonomousFunction):
26
29
  augmented_code_checked: bool
@@ -105,16 +108,44 @@ 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
+ if output_address.ready:
112
116
  return output_address.get()
117
+ output_address.request_execution()
118
+ registration_addr = (output_address[0]
119
+ , output_address[1], get_random_safe_str())
120
+ pth.function_execution_attempts[registration_addr] = build_context()
113
121
  unpacked_kwargs = UnpackedKwArgs(**packed_kwargs)
114
- result = super().__call__(**unpacked_kwargs)
122
+ result = super().execute(**unpacked_kwargs)
115
123
  pth.function_output_store[output_address] = ValueAddress(result)
124
+ pth.function_execution_requests.delete_if_exists(output_address)
116
125
  return result
117
126
 
127
+ def list_execute(self, list_of_kwargs:list[dict]) -> Any:
128
+ assert isinstance(list_of_kwargs, (list, tuple))
129
+ for kwargs in list_of_kwargs:
130
+ assert isinstance(kwargs, dict)
131
+ addrs = []
132
+ for kwargs in list_of_kwargs:
133
+ new_addr = FuncOutputAddress(self, kwargs)
134
+ new_addr.request_execution()
135
+ addrs.append(new_addr)
136
+ addrs_indexed = list(zip(range(len(addrs)), addrs))
137
+ pth.entropy_infuser.shuffle(addrs_indexed)
138
+ results_dict = dict()
139
+ for n, an_addr in addrs_indexed:
140
+ results_dict[n] = an_addr.function.execute(**an_addr.arguments)
141
+ results_list = [results_dict[n] for n in range(len(addrs))]
142
+ return results_list
143
+
144
+ def execution_attempts(self, **kwargs) -> list:
145
+ packed_kwargs = PackedKwArgs(**kwargs)
146
+ output_address = FuncOutputAddress(self, packed_kwargs)
147
+ return output_address.execution_attempts
148
+
118
149
 
119
150
  def register_idempotent_function(f: IdempotentFunction) -> None:
120
151
  """Register an idempotent function in the Pythagoras system."""
@@ -143,8 +174,18 @@ class FuncOutputAddress(HashAddress):
143
174
  tmp = ValueAddress(signature)
144
175
  super().__init__(tmp.prefix, tmp.hash_value)
145
176
 
177
+ @property
146
178
  def ready(self):
147
- return self in pth.function_output_store
179
+ result = self in pth.function_output_store
180
+ return result
181
+
182
+ def request_execution(self):
183
+ if self in pth.function_output_store:
184
+ if self in pth.function_execution_requests:
185
+ del pth.function_execution_requests[self]
186
+ else:
187
+ if self not in pth.function_execution_requests:
188
+ pth.function_execution_requests[self] = True
148
189
 
149
190
  def get(self, timeout: int = None):
150
191
  """Retrieve value, referenced by the address.
@@ -152,14 +193,20 @@ class FuncOutputAddress(HashAddress):
152
193
  If the value is not immediately available, backoff exponentially
153
194
  till timeout is exceeded. If timeout is None, keep trying forever.
154
195
  """
196
+ if self.ready:
197
+ return pth.global_value_store[pth.function_output_store[self]]
198
+ self.request_execution()
199
+
155
200
  start_time, backoff_period = time.time(), 1.0
156
201
  stop_time = (start_time + timeout) if timeout else None
157
202
  # start_time, stop_time and backoff_period are in seconds
203
+
158
204
  while True:
159
- try:
160
- address = pth.function_output_store[self]
161
- return pth.value_store[address]
162
- except:
205
+ if self.ready:
206
+ result = pth.global_value_store[pth.function_output_store[self]]
207
+ pth.function_execution_requests.delete_if_exists(self)
208
+ return result
209
+ else:
163
210
  time.sleep(backoff_period)
164
211
  backoff_period *= 2.0
165
212
  backoff_period += pth.entropy_infuser.uniform(-0.5, 0.5)
@@ -190,4 +237,53 @@ class FuncOutputAddress(HashAddress):
190
237
  signature_addr = ValueAddress.from_strings(
191
238
  prefix=self.prefix, hash_value=self.hash_value)
192
239
  signature = signature_addr.get()
193
- return signature.args_addr.get()
240
+ return signature.args_addr.get()
241
+
242
+ @property
243
+ def can_be_executed(self) -> bool:
244
+ """Indicates if the function can be executed in the current session.
245
+
246
+ Currently, it's just a placeholder that always returns True.
247
+
248
+ The function should fe refactored once we start supporting
249
+ VALIDATORS, CORRECTORS and SEQUENCERS
250
+ """
251
+ return True
252
+
253
+ @property
254
+ def needs_execution(self) -> bool:
255
+ """Indicates if the function is a good candidate for execution.
256
+
257
+ Returns False if the result is already available, or if some other
258
+ process is currently working on it. Otherwise, returns True.
259
+ """
260
+ DEFAULT_EXECUTION_TIME = 10
261
+ MAX_EXECUTION_ATTEMPTS = 5
262
+ # TODO: these should not be constants
263
+ if self.ready:
264
+ return False
265
+ past_attempts = pth.function_execution_attempts.get_subdict(self)
266
+ n_past_attempts = len(past_attempts)
267
+ if n_past_attempts == 0:
268
+ return True
269
+ if n_past_attempts > MAX_EXECUTION_ATTEMPTS:
270
+ #TODO: log this event. Should we have DLQ?
271
+ return False
272
+ most_recent_timestamp = max(
273
+ past_attempts.mtimestamp(a) for a in past_attempts)
274
+ current_timestamp = time.time()
275
+ if (current_timestamp - most_recent_timestamp
276
+ > DEFAULT_EXECUTION_TIME*(2**n_past_attempts)):
277
+ return True
278
+ return False
279
+
280
+ @property
281
+ def execution_attempts(self) -> list:
282
+ attempts = pth.function_execution_attempts.get_subdict(self)
283
+ attemps_timed = {-attempts.mtimestamp(a):attempts[a] for a in attempts}
284
+ times = sorted(attemps_timed)
285
+ result = []
286
+ for t in times:
287
+ result.append(attemps_timed[t])
288
+ return result
289
+
@@ -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,12 @@ 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
37
  return context
@@ -1,12 +1,12 @@
1
1
  import sys
2
2
  import traceback
3
3
 
4
- from pythagoras._99_misc_utils.current_date_gmt_str import (
4
+ from pythagoras._05_events_and_exceptions.current_date_gmt_str import (
5
5
  current_date_gmt_string)
6
- from pythagoras._99_misc_utils.notebook_checker import is_executed_in_notebook
6
+ from pythagoras._05_events_and_exceptions.notebook_checker import is_executed_in_notebook
7
7
  from pythagoras._99_misc_utils.random_safe_str_creator import (
8
8
  get_random_safe_str)
9
- from pythagoras._99_misc_utils.context_builder import build_context
9
+ from pythagoras._05_events_and_exceptions.context_builder import build_context
10
10
 
11
11
  import pythagoras as pth
12
12
 
@@ -39,7 +39,7 @@ class EventLogger:
39
39
  self.event_log[name_part_1, name_part_2] = kwargs
40
40
 
41
41
  def log_uncaught_exception(exception: Exception, **kwargs):
42
- logger = EventLogger(event_log = pth.crash_history
42
+ logger = EventLogger(event_log = pth.global_crash_history
43
43
  , prefix = "__none__"+ exception.__class__.__name__
44
44
  , save_context = True)
45
45
  logger.log_event(exception=exception, **kwargs)
@@ -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(obj):
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(obj):
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(obj):
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
+
@@ -158,7 +158,7 @@ def get_current_date_gmt():
158
158
  def post_crash_report(exception:Any, output:str=None): #TODO: refactor
159
159
  crash_report_name = 'crash_' + get_random_safe_str()
160
160
  event_key = get_current_date_gmt() + [crash_report_name]
161
- pth.crash_history[event_key] = ExceptionLogEntry(exception,output)
161
+ pth.global_crash_history[event_key] = ExceptionLogEntry(exception, output)
162
162
 
163
163
 
164
164
 
@@ -1,4 +1,4 @@
1
- from pythagoras._05_mission_control.global_state_management import (
1
+ from pythagoras._06_mission_control.global_state_management import (
2
2
  initialize
3
3
  , is_global_state_correct
4
4
  , is_correctly_initialized
@@ -9,4 +9,4 @@ from pythagoras._05_mission_control.global_state_management import (
9
9
  , get_autonomous_function
10
10
  )
11
11
 
12
- from pythagoras._05_mission_control.summary import summary
12
+ from pythagoras._06_mission_control.summary import summary
@@ -6,9 +6,9 @@ from persidict import FileDirDict, PersiDict
6
6
  import pythagoras as pth
7
7
  from pythagoras._03_autonomous_functions.default_island_singleton import (
8
8
  DefaultIslandType, DefaultIsland)
9
- from pythagoras._05_mission_control.events_and_exceptions_core import (
9
+ from pythagoras._05_events_and_exceptions.events_and_exceptions_core import (
10
10
  register_exception_handlers, unregister_exception_handlers, pth_excepthook)
11
- from pythagoras._99_misc_utils.notebook_checker import is_executed_in_notebook
11
+ from pythagoras._05_events_and_exceptions.notebook_checker import is_executed_in_notebook
12
12
 
13
13
 
14
14
  def initialize(base_dir:str
@@ -34,8 +34,8 @@ def initialize(base_dir:str
34
34
  os.mkdir(base_dir)
35
35
  assert os.path.isdir(base_dir)
36
36
 
37
- value_store_dir = os.path.join(base_dir, "value_store")
38
- pth.value_store = dict_type(value_store_dir, digest_len=0)
37
+ value_store_dir = os.path.join(base_dir, "global_value_store")
38
+ pth.global_value_store = dict_type(value_store_dir, digest_len=0)
39
39
 
40
40
  func_garage_dir = os.path.join(base_dir, "func_garage")
41
41
  pth.function_garage = dict_type(func_garage_dir, digest_len=0)
@@ -43,18 +43,31 @@ def initialize(base_dir:str
43
43
  func_garage_dir, digest_len=0, file_type="py"
44
44
  , base_class_for_values=str)
45
45
 
46
- crash_history_dir = os.path.join(base_dir, "crash_history")
47
- pth.crash_history = dict_type(crash_history_dir
46
+ crash_history_dir = os.path.join(base_dir, "global_crash_history")
47
+ pth.global_crash_history = dict_type(crash_history_dir
48
48
  , digest_len=0, file_type="json")
49
49
 
50
- event_log_dir = os.path.join(base_dir, "event_log")
51
- pth.event_log = dict_type(event_log_dir
50
+ event_log_dir = os.path.join(base_dir, "global_event_log")
51
+ pth.global_event_log = dict_type(event_log_dir
52
52
  , digest_len=0, file_type="json")
53
53
 
54
54
  func_output_store_dir = os.path.join(base_dir, "func_output_store")
55
55
  pth.function_output_store = dict_type(func_output_store_dir, digest_len=0)
56
- swarming_requests_dir = os.path.join(base_dir, "swarming_requests")
57
- pth.swarming_requests = dict_type(swarming_requests_dir, digest_len=0)
56
+ execution_requests_dir = os.path.join(base_dir, "function_execution_requests")
57
+ pth.function_execution_requests = dict_type(execution_requests_dir, digest_len=0)
58
+ execution_attempts_dir = os.path.join(base_dir, "function_execution_attempts")
59
+ pth.function_execution_attempts = dict_type(execution_attempts_dir, digest_len=0)
60
+
61
+ function_crash_history_dir = os.path.join(base_dir, "function_crash_history")
62
+ pth.function_crash_history = dict_type(function_crash_history_dir
63
+ , digest_len=0, file_type="json")
64
+
65
+ function_event_log_dir = os.path.join(base_dir, "function_event_log")
66
+ pth.function_event_log = dict_type(function_event_log_dir
67
+ , digest_len=0, file_type="json")
68
+
69
+
70
+
58
71
  pth.default_island_name = default_island_name
59
72
  pth.all_autonomous_functions = dict()
60
73
  pth.all_autonomous_functions[default_island_name] = dict()
@@ -72,12 +85,16 @@ def initialize(base_dir:str
72
85
  def is_fully_unitialized():
73
86
  """ Check if Pythagoras is uninitialized."""
74
87
  result = True
75
- result &= pth.value_store is None
88
+ result &= pth.global_value_store is None
76
89
  result &= pth.function_garage is None
77
90
  result &= pth.function_source_repository is None
78
91
  result &= pth.function_output_store is None
79
- result &= pth.crash_history is None
80
- result &= pth.event_log is None
92
+ result &= pth.function_execution_requests is None
93
+ result &= pth.function_execution_attempts is None
94
+ result &= pth.function_crash_history is None
95
+ result &= pth.function_event_log is None
96
+ result &= pth.global_crash_history is None
97
+ result &= pth.global_event_log is None
81
98
  result &= pth.default_island_name is None
82
99
  result &= pth.all_autonomous_functions is None
83
100
  result &= pth.initialization_parameters is None
@@ -86,7 +103,7 @@ def is_fully_unitialized():
86
103
 
87
104
  def is_correctly_initialized():
88
105
  """ Check if Pythagoras is correctly initialized."""
89
- if not isinstance(pth.value_store, PersiDict):
106
+ if not isinstance(pth.global_value_store, PersiDict):
90
107
  return False
91
108
  if not isinstance(pth.function_garage, PersiDict):
92
109
  return False
@@ -94,9 +111,17 @@ def is_correctly_initialized():
94
111
  return False
95
112
  if not isinstance(pth.function_output_store, PersiDict):
96
113
  return False
97
- if not isinstance(pth.crash_history, PersiDict):
114
+ if not isinstance(pth.function_execution_requests, PersiDict):
115
+ return False
116
+ if not isinstance(pth.function_execution_attempts, PersiDict):
117
+ return False
118
+ if not isinstance(pth.function_crash_history, PersiDict):
119
+ return False
120
+ if not isinstance(pth.function_event_log, PersiDict):
121
+ return False
122
+ if not isinstance(pth.global_crash_history, PersiDict):
98
123
  return False
99
- if not isinstance(pth.event_log, PersiDict):
124
+ if not isinstance(pth.global_event_log, PersiDict):
100
125
  return False
101
126
  if not isinstance(pth.default_island_name, str):
102
127
  return False
@@ -136,12 +161,16 @@ def is_global_state_correct():
136
161
  return result
137
162
 
138
163
  def _clean_global_state():
139
- pth.value_store = None
164
+ pth.global_value_store = None
140
165
  pth.function_garage = None #???
141
166
  pth.function_source_repository = None
142
167
  pth.function_output_store = None
143
- pth.crash_history = None
144
- pth.event_log = None
168
+ pth.function_execution_requests = None
169
+ pth.function_execution_attempts = None
170
+ pth.function_crash_history = None
171
+ pth.function_event_log = None
172
+ pth.global_crash_history = None
173
+ pth.global_event_log = None
145
174
  pth.all_autonomous_functions = None
146
175
  pth.default_island_name = None
147
176
  pth.initialization_parameters = None
@@ -1,4 +1,4 @@
1
- from pythagoras._05_mission_control.global_state_management import is_fully_unitialized, is_correctly_initialized
1
+ from pythagoras._06_mission_control.global_state_management import is_fully_unitialized, is_correctly_initialized
2
2
  import pythagoras as pth
3
3
 
4
4
  def summary():
@@ -12,7 +12,7 @@ def summary():
12
12
  divider = 60 * "~" + "\n"
13
13
  result = "\n\n"+divider
14
14
  result += "PERSISTENT STATE: \n"
15
- result += f"{len(pth.value_store)=} \n"
15
+ result += f"{len(pth.global_value_store)=} \n"
16
16
  result += f"{len(pth.function_output_store)=} \n"
17
17
 
18
18
  result += divider
@@ -13,9 +13,6 @@ from pythagoras._99_misc_utils.package_manager import (
13
13
  from pythagoras._99_misc_utils.output_capturer import (
14
14
  OutputCapturer)
15
15
 
16
- from pythagoras._99_misc_utils.isinstance_txt import (
17
- isinstance_txt)
18
-
19
16
  from pythagoras._99_misc_utils.function_name import (
20
17
  get_function_name_from_source)
21
18
 
@@ -12,7 +12,7 @@ from pythagoras.python_utils import get_long_infoname
12
12
 
13
13
  T = TypeVar("T")
14
14
 
15
- # value_store:Optional[PersiDict] = None
15
+ # global_value_store:Optional[PersiDict] = None
16
16
 
17
17
  def set_value_store(store:PersiDict) -> None:
18
18
  """Set a global value store."""
@@ -14,13 +14,20 @@ from pythagoras._01_foundational_objects import *
14
14
  from pythagoras._02_ordinary_functions import *
15
15
  from pythagoras._03_autonomous_functions import *
16
16
  from pythagoras._04_idempotent_functions import *
17
- from pythagoras._05_mission_control import *
17
+ from pythagoras._05_events_and_exceptions import *
18
+ from pythagoras._06_mission_control import *
18
19
 
19
20
 
20
- value_store:Optional[PersiDict] = None
21
+ global_value_store:Optional[PersiDict] = None
22
+ global_crash_history: Optional[PersiDict] = None
23
+ global_event_log: Optional[PersiDict] = None
24
+
21
25
  function_output_store:Optional[PersiDict] = None
22
- crash_history: Optional[PersiDict] = None
23
- event_log: Optional[PersiDict] = None
26
+ function_execution_requests:Optional[PersiDict] = None
27
+ function_execution_attempts:Optional[PersiDict] = None
28
+ function_crash_history:Optional[PersiDict] = None
29
+ function_event_log:Optional[PersiDict] = None
30
+
24
31
 
25
32
  # ??????????????????????????????????????
26
33
  function_garage:Optional[PersiDict] = None # ????
@@ -1,4 +1,4 @@
1
- from pythagoras._05_mission_control.global_state_management import _clean_global_state
1
+ from pythagoras._06_mission_control.global_state_management import _clean_global_state
2
2
  import pythagoras as pth
3
3
 
4
4
  _clean_global_state()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pythagoras
3
- Version: 0.8.6
3
+ Version: 0.8.8
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
 
@@ -38,21 +38,22 @@ pythagoras/_04_idempotent_functions/idempotent_decorator.py
38
38
  pythagoras/_04_idempotent_functions/idempotent_func_and_address.py
39
39
  pythagoras/_04_idempotent_functions/kw_args.py
40
40
  pythagoras/_04_idempotent_functions/process_augmented_func_src.py
41
- pythagoras/_05_mission_control/__events_and_exceptions_OLD__.py
42
- pythagoras/_05_mission_control/__init__.py
43
- pythagoras/_05_mission_control/events_and_exceptions_core.py
44
- pythagoras/_05_mission_control/global_state_management.py
45
- pythagoras/_05_mission_control/summary.py
41
+ pythagoras/_05_events_and_exceptions/__init__.py
42
+ pythagoras/_05_events_and_exceptions/context_builder.py
43
+ pythagoras/_05_events_and_exceptions/current_date_gmt_str.py
44
+ pythagoras/_05_events_and_exceptions/events_and_exceptions_core.py
45
+ pythagoras/_05_events_and_exceptions/find_in_callstack.py
46
+ pythagoras/_05_events_and_exceptions/notebook_checker.py
47
+ pythagoras/_05_events_and_exceptions/type_retrievers.py
48
+ pythagoras/_06_mission_control/__events_and_exceptions_OLD__.py
49
+ pythagoras/_06_mission_control/__init__.py
50
+ pythagoras/_06_mission_control/global_state_management.py
51
+ pythagoras/_06_mission_control/summary.py
46
52
  pythagoras/_99_misc_utils/__init__.py
47
53
  pythagoras/_99_misc_utils/base_16_32_convertors.py
48
- pythagoras/_99_misc_utils/context_builder.py
49
- pythagoras/_99_misc_utils/current_date_gmt_str.py
50
- pythagoras/_99_misc_utils/find_in_callstack.py
51
54
  pythagoras/_99_misc_utils/function_name.py
52
55
  pythagoras/_99_misc_utils/id_examiner.py
53
- pythagoras/_99_misc_utils/isinstance_txt.py
54
56
  pythagoras/_99_misc_utils/long_infoname.py
55
- pythagoras/_99_misc_utils/notebook_checker.py
56
57
  pythagoras/_99_misc_utils/output_capturer.py
57
58
  pythagoras/_99_misc_utils/package_manager.py
58
59
  pythagoras/_99_misc_utils/random_safe_str_creator.py
@@ -12,3 +12,6 @@ boto3
12
12
  moto
13
13
  pytest
14
14
  autopep8
15
+ torch
16
+ keras
17
+ tensorflow
@@ -5,7 +5,7 @@ with open("README.md", "r") as f:
5
5
 
6
6
  setuptools.setup(
7
7
  name="pythagoras"
8
- ,version="0.8.6"
8
+ ,version="0.8.8"
9
9
  ,author="Volodymyr (Vlad) Pavlov"
10
10
  ,author_email="vlpavlov@ieee.org"
11
11
  ,description= "Simple framework for planet-scale "
@@ -14,11 +14,12 @@ setuptools.setup(
14
14
  ,long_description_content_type="text/markdown"
15
15
  ,url="https://github.com/vladlpavlov/pythagoras"
16
16
  ,packages=["pythagoras"
17
- ,"pythagoras._01_foundational_objects"
17
+ , "pythagoras._01_foundational_objects"
18
18
  , "pythagoras._02_ordinary_functions"
19
19
  , "pythagoras._03_autonomous_functions"
20
20
  , "pythagoras._04_idempotent_functions"
21
- , "pythagoras._05_mission_control"
21
+ , "pythagoras._05_events_and_exceptions"
22
+ , "pythagoras._06_mission_control"
22
23
  , "pythagoras._99_misc_utils"
23
24
  ]
24
25
  ,classifiers=[
@@ -52,6 +53,9 @@ setuptools.setup(
52
53
  , 'moto'
53
54
  , 'pytest'
54
55
  , 'autopep8'
56
+ , 'torch'
57
+ , 'keras'
58
+ , 'tensorflow'
55
59
  ]
56
60
 
57
61
  )
@@ -1,34 +0,0 @@
1
- from typing import Any
2
-
3
- from pythagoras._99_misc_utils.find_in_callstack import find_in_callstack
4
-
5
- def isinstance_txt(obj:Any, type_name:str)->bool:
6
- """Check if an object is an instance of a type specified by a string name.
7
-
8
- This function extends the functionality of the built-in isinstance function
9
- by allowing the type to be specified as a string.
10
-
11
- It first attempts to evaluate the string to a type using the eval function.
12
- If this raises a exception (indicating that the type is not directly
13
- accessible in the current scope), it then searches the call stack
14
- for an object that matches the type name. If a single matching type
15
- is found in the stack, it checks if `obj` is an instance of this type.
16
-
17
- Parameters:
18
- obj (Any): The object to be checked.
19
- type_name (str): The name of the type as a string.
20
-
21
- Returns:
22
- bool: True if `obj` is an instance of the type specified by `type_name`,
23
- False otherwise
24
- """
25
-
26
- assert isinstance(type_name, str)
27
- try:
28
- if isinstance(obj, eval(type_name)):
29
- return True
30
- except NameError:
31
- candidates = find_in_callstack(type_name)
32
- assert len(candidates) == 1
33
- return isinstance(obj, candidates[0])
34
- return False
File without changes
File without changes
File without changes