fred-oss 0.43.0__tar.gz → 0.44.0__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 (93) hide show
  1. {fred_oss-0.43.0/src/main/fred_oss.egg-info → fred_oss-0.44.0}/PKG-INFO +1 -1
  2. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/future/impl.py +74 -1
  3. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/future/result.py +33 -7
  4. fred_oss-0.44.0/src/main/fred/version +1 -0
  5. {fred_oss-0.43.0 → fred_oss-0.44.0/src/main/fred_oss.egg-info}/PKG-INFO +1 -1
  6. fred_oss-0.43.0/src/main/fred/version +0 -1
  7. {fred_oss-0.43.0 → fred_oss-0.44.0}/MANIFEST.in +0 -0
  8. {fred_oss-0.43.0 → fred_oss-0.44.0}/NOTICE.txt +0 -0
  9. {fred_oss-0.43.0 → fred_oss-0.44.0}/README.md +0 -0
  10. {fred_oss-0.43.0 → fred_oss-0.44.0}/requirements.txt +0 -0
  11. {fred_oss-0.43.0 → fred_oss-0.44.0}/setup.cfg +0 -0
  12. {fred_oss-0.43.0 → fred_oss-0.44.0}/setup.py +0 -0
  13. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/cli/__init__.py +0 -0
  14. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/cli/__main__.py +0 -0
  15. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/cli/interface.py +0 -0
  16. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/cli/main.py +0 -0
  17. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/dao/__init__.py +0 -0
  18. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/dao/comp/__init__.py +0 -0
  19. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/dao/comp/_keyval.py +0 -0
  20. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/dao/comp/_pubsub.py +0 -0
  21. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/dao/comp/_queue.py +0 -0
  22. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/dao/comp/catalog.py +0 -0
  23. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/dao/comp/interface.py +0 -0
  24. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/dao/service/__init__.py +0 -0
  25. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/dao/service/_redis.py +0 -0
  26. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/dao/service/_stdlib.py +0 -0
  27. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/dao/service/catalog.py +0 -0
  28. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/dao/service/interface.py +0 -0
  29. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/dao/service/utils.py +0 -0
  30. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/future/__init__.py +0 -0
  31. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/future/callback/__init__.py +0 -0
  32. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/future/callback/_function.py +0 -0
  33. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/future/callback/catalog.py +0 -0
  34. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/future/callback/interface.py +0 -0
  35. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/future/settings.py +0 -0
  36. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/future/utils.py +0 -0
  37. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/integrations/databricks/__init__.py +0 -0
  38. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/integrations/databricks/cli_ext.py +0 -0
  39. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/integrations/databricks/runtime.py +0 -0
  40. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/integrations/databricks/runtimes/__init__.py +0 -0
  41. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/integrations/databricks/runtimes/scanner.py +0 -0
  42. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/integrations/databricks/runtimes/sync.py +0 -0
  43. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/integrations/databricks/wrappers/__init__.py +0 -0
  44. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/integrations/databricks/wrappers/dbutils.py +0 -0
  45. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/integrations/runpod/__init__.py +0 -0
  46. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/integrations/runpod/cli_ext.py +0 -0
  47. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/integrations/runpod/helper.py +0 -0
  48. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/maturity.py +0 -0
  49. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/monad/__init__.py +0 -0
  50. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/monad/_either.py +0 -0
  51. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/monad/catalog.py +0 -0
  52. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/monad/interface.py +0 -0
  53. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/settings.py +0 -0
  54. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/utils/__init__.py +0 -0
  55. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/utils/dateops.py +0 -0
  56. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/utils/runtime.py +0 -0
  57. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/version.py +0 -0
  58. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/worker/__init__.py +0 -0
  59. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/worker/interface.py +0 -0
  60. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/worker/runner/__init__.py +0 -0
  61. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/worker/runner/backend.py +0 -0
  62. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/worker/runner/client.py +0 -0
  63. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/worker/runner/handler.py +0 -0
  64. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/worker/runner/model/__init__.py +0 -0
  65. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/worker/runner/model/_handler.py +0 -0
  66. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/worker/runner/model/_item.py +0 -0
  67. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/worker/runner/model/_request.py +0 -0
  68. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/worker/runner/model/_runner_spec.py +0 -0
  69. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/worker/runner/model/catalog.py +0 -0
  70. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/worker/runner/model/interface.py +0 -0
  71. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/worker/runner/plugins/__init__.py +0 -0
  72. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/worker/runner/plugins/_local.py +0 -0
  73. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/worker/runner/plugins/_runpod.py +0 -0
  74. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/worker/runner/plugins/catalog.py +0 -0
  75. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/worker/runner/plugins/interface.py +0 -0
  76. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/worker/runner/rest/__init__.py +0 -0
  77. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/worker/runner/rest/auth.py +0 -0
  78. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/worker/runner/rest/cli_ext.py +0 -0
  79. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/worker/runner/rest/routers/__init__.py +0 -0
  80. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/worker/runner/rest/routers/_runner.py +0 -0
  81. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/worker/runner/rest/routers/catalog.py +0 -0
  82. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/worker/runner/rest/routers/interface.py +0 -0
  83. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/worker/runner/rest/server.py +0 -0
  84. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/worker/runner/rest/settings.py +0 -0
  85. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/worker/runner/settings.py +0 -0
  86. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/worker/runner/signal.py +0 -0
  87. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/worker/runner/status.py +0 -0
  88. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred/worker/runner/utils.py +0 -0
  89. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred_oss.egg-info/SOURCES.txt +0 -0
  90. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred_oss.egg-info/dependency_links.txt +0 -0
  91. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred_oss.egg-info/entry_points.txt +0 -0
  92. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred_oss.egg-info/requires.txt +0 -0
  93. {fred_oss-0.43.0 → fred_oss-0.44.0}/src/main/fred_oss.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fred-oss
3
- Version: 0.43.0
3
+ Version: 0.44.0
4
4
  Summary: FREDOSS
5
5
  Home-page: https://fred.fahera.mx
6
6
  Author: Fahera Research, Education, and Development
@@ -63,6 +63,7 @@ class Future(MonadInterface[A]):
63
63
  on_start: Optional[CallbackInterface] = None,
64
64
  on_complete: Optional[CallbackInterface] = None,
65
65
  parent_id: Optional[str] = None,
66
+ broadcast: bool = False,
66
67
  **kwargs
67
68
  ):
68
69
  """Initializes a Future with the provided function to be executed asynchronously.
@@ -77,7 +78,11 @@ class Future(MonadInterface[A]):
77
78
  All keyword arguments except 'future_id' are forwarded to the target function.
78
79
  """
79
80
  # Create a new available future
80
- future = FutureUndefinedPending.auto(parent_id=parent_id, future_id=kwargs.pop("future_id", None))
81
+ future = FutureUndefinedPending.auto(
82
+ parent_id=parent_id,
83
+ broadcast=broadcast,
84
+ future_id=kwargs.pop("future_id", None),
85
+ )
81
86
  # Register the Future-ID and define the available future via the provided function.
82
87
  # Note: The 'apply' method is blocking by itself; thus, we run it in a separate thread.
83
88
  # Note: The thread is a daemon to ensure it does not block program exit.
@@ -276,6 +281,10 @@ class Future(MonadInterface[A]):
276
281
  Returns:
277
282
  Future[A]: A Future instance representing the pulled future.
278
283
  """
284
+ logger.warning(
285
+ "The 'pullsync' method is scheduled for deprecation and will be removed in future versions. "
286
+ "Please use the 'subscribe' method instead that uses a pubsub mechanism."
287
+ )
279
288
  from fred.future.utils import pull_future_result
280
289
 
281
290
  return cls(
@@ -289,6 +298,70 @@ class Future(MonadInterface[A]):
289
298
  on_complete=on_complete,
290
299
  **kwargs
291
300
  )
301
+
302
+ @classmethod
303
+ def subscribe(
304
+ cls,
305
+ future_id: str,
306
+ on_start: Optional[CallbackInterface] = None,
307
+ on_complete: Optional[CallbackInterface] = None,
308
+ ) -> 'Future[A]':
309
+ """Subscribes to updates for an existing future using a publish-subscribe mechanism.
310
+ This method allows for receiving real-time updates about the future's state
311
+ and result without blocking.
312
+ Args:
313
+ future_id (str): The unique identifier of the future to subscribe to.
314
+ on_start (Optional[CallbackInterface]): An optional callback to be executed
315
+ when the subscription starts.
316
+ on_complete (Optional[CallbackInterface]): An optional callback to be executed
317
+ when the future completes.
318
+ Returns:
319
+ Future[A]: A Future instance that will execute the subscription logic.
320
+ """
321
+ # Define a closure that will handle incoming messages from the pub-sub channel
322
+ def closure():
323
+ for payload in FutureResult._get_bcast_channel(future_id=future_id).subscribe():
324
+ if payload.get("type") != "message":
325
+ continue
326
+ message = payload.get("data")
327
+ if not message:
328
+ continue
329
+ match FutureResult.from_string(message):
330
+ case FutureDefined(value=value):
331
+ return value.resolve()
332
+ case FutureUndefinedPending():
333
+ continue
334
+ case FutureUndefinedInProgress():
335
+ continue
336
+ case _:
337
+ raise TypeError("Unknown FutureResult type")
338
+ shared_params = {
339
+ "parent_id": future_id,
340
+ "broadcast": False,
341
+ "on_start": on_start,
342
+ "on_complete": on_complete,
343
+ }
344
+ # Depending on the current state of the future, either return the resolved value
345
+ # or subscribe to the broadcast channel to wait for updates (via closure).
346
+ match FutureResult.from_backend(future_id=future_id):
347
+ case FutureDefined(value=value):
348
+ return cls(
349
+ function=lambda: value.resolve(),
350
+ **shared_params
351
+ )
352
+ case instance:
353
+ # The future-result can be None if the future_id does not exist
354
+ if not instance:
355
+ raise ValueError(f"Future with ID '{future_id}' does not exist.")
356
+ # If the future exists, but is not configured for broadcast, raise an error...
357
+ if not instance.broadcast:
358
+ raise ValueError("Future is not configured for broadcast; cannot subscribe.")
359
+ # If the future exists and is configured for broadcast, subscribe to updates...
360
+ return cls(
361
+ function=closure,
362
+ **shared_params
363
+ )
364
+
292
365
  def lineage(self) -> list[str]:
293
366
  """Retrieves the lineage of the future, tracing back through its parent futures.
294
367
  This method is useful for debugging and understanding the sequence of computations
@@ -20,7 +20,7 @@ from fred.future.settings import (
20
20
  from fred.future.callback.interface import CallbackInterface
21
21
  from fred.dao.service.catalog import ServiceCatalog
22
22
  from fred.utils.dateops import datetime_utcnow
23
- from fred.dao.comp.catalog import FredKeyVal
23
+ from fred.dao.comp.catalog import FredKeyVal, FredQueue, FredPubSub
24
24
  from fred.monad.catalog import EitherMonad
25
25
 
26
26
  logger = logger_manager.get_logger(__name__)
@@ -30,6 +30,8 @@ A = TypeVar("A")
30
30
 
31
31
  class FutureBackend:
32
32
  keyval: type[FredKeyVal]
33
+ queue: type[FredQueue]
34
+ pubsub: type[FredPubSub]
33
35
 
34
36
  @classmethod
35
37
  def with_backend(cls, service: ServiceCatalog, **kwargs) -> type['FutureBackend']:
@@ -47,7 +49,9 @@ class FutureBackend:
47
49
  f"{service.name.title()}{cls.__name__}",
48
50
  (cls,),
49
51
  {
50
- "keyval": components.KEYVAL.value
52
+ "keyval": components.KEYVAL.value,
53
+ "queue": components.QUEUE.value,
54
+ "pubsub": components.PUBSUB.value,
51
55
  },
52
56
  )
53
57
 
@@ -80,6 +84,7 @@ class FutureResult(Generic[A], FutureBackend.infer_backend()):
80
84
  retrieval and management of asynchronous tasks."""
81
85
  future_id: str
82
86
  parent_id: Optional[str]
87
+ broadcast: bool
83
88
 
84
89
  @staticmethod
85
90
  def _get_future_keyname(future_id: str) -> str:
@@ -89,6 +94,21 @@ class FutureResult(Generic[A], FutureBackend.infer_backend()):
89
94
  def future_keyname(self) -> str:
90
95
  return self._get_future_keyname(future_id=self.future_id)
91
96
 
97
+ @classmethod
98
+ def _get_bcast_channel(cls, future_id: str) -> FredPubSub:
99
+ return cls.pubsub(name=":".join([cls._get_future_keyname(future_id=future_id), "bcast"]))
100
+
101
+ @property
102
+ def bcast_channel(self) -> FredPubSub:
103
+ return self._get_bcast_channel(future_id=self.future_id)
104
+
105
+ def bcast_now(self, item: Optional[str] = None, ignore_flag: bool = False) -> bool:
106
+ if ignore_flag or self.broadcast:
107
+ self.bcast_channel.publish(item=item or self.stringify())
108
+ return True
109
+ return False
110
+
111
+
92
112
  @classmethod
93
113
  def _get_status_key(cls, future_id: str) -> FredKeyVal:
94
114
  return cls.keyval(key=":".join([cls._get_future_keyname(future_id=future_id), "status"]))
@@ -132,7 +152,7 @@ class FutureResult(Generic[A], FutureBackend.infer_backend()):
132
152
 
133
153
  @classmethod
134
154
  def from_backend(cls, future_id: str) -> Optional['FutureResult[A]']:
135
- return FutureResult(future_id=future_id, parent_id=None)._from_backend()
155
+ return FutureResult(future_id=future_id, parent_id=None, broadcast=False)._from_backend()
136
156
 
137
157
  @property
138
158
  def _pre(self) -> Optional['FutureResult']:
@@ -169,7 +189,8 @@ class FutureUndefinedPending(FutureResult[A]):
169
189
  import uuid
170
190
  parent_id = kwargs.pop("parent_id", None)
171
191
  future_id = kwargs.pop("future_id", None) or str(uuid.uuid4())
172
- return FutureUndefinedPending[A](future_id=future_id, parent_id=parent_id, **kwargs)
192
+ broadcast = kwargs.pop("broadcast", False)
193
+ return FutureUndefinedPending[A](future_id=future_id, parent_id=parent_id, broadcast=broadcast, **kwargs)
173
194
 
174
195
  def __post_init__(self):
175
196
  logger.debug(f"Future[{self.future_id}] initialized and pending execution")
@@ -178,9 +199,10 @@ class FutureUndefinedPending(FutureResult[A]):
178
199
  expire=FRD_FUTURE_DEFAULT_EXPIRATION
179
200
  )
180
201
  self.obj.set(
181
- value=self.stringify(),
202
+ value=(obj_payload := self.stringify()),
182
203
  expire=FRD_FUTURE_DEFAULT_EXPIRATION
183
204
  )
205
+ self.bcast_now(item=obj_payload, ignore_flag=False)
184
206
 
185
207
  def apply(
186
208
  self,
@@ -203,6 +225,7 @@ class FutureUndefinedPending(FutureResult[A]):
203
225
  fip = FutureUndefinedInProgress[A](
204
226
  future_id=self.future_id,
205
227
  parent_id=self.parent_id,
228
+ broadcast=self.broadcast,
206
229
  started_at=perf_counter(),
207
230
  function_name=function.__name__,
208
231
  )
@@ -227,9 +250,10 @@ class FutureUndefinedInProgress(FutureResult[A]):
227
250
  expire=FRD_FUTURE_DEFAULT_EXPIRATION
228
251
  )
229
252
  self.obj.set(
230
- value=self.stringify(),
253
+ value=(obj_payload := self.stringify()),
231
254
  expire=FRD_FUTURE_DEFAULT_EXPIRATION
232
255
  )
256
+ self.bcast_now(item=obj_payload, ignore_flag=False)
233
257
 
234
258
  def exec(
235
259
  self,
@@ -278,6 +302,7 @@ class FutureUndefinedInProgress(FutureResult[A]):
278
302
  future_defined = FutureDefined(
279
303
  future_id=self.future_id,
280
304
  parent_id=self.parent_id,
305
+ broadcast=self.broadcast,
281
306
  value=value,
282
307
  ok=ok,
283
308
  )
@@ -308,9 +333,10 @@ class FutureDefined(FutureResult[A]):
308
333
  expire=FRD_FUTURE_DEFAULT_EXPIRATION
309
334
  )
310
335
  self.obj.set(
311
- value=self.stringify(),
336
+ value=(obj_payload := self.stringify()),
312
337
  expire=FRD_FUTURE_DEFAULT_EXPIRATION
313
338
  )
339
+ self.bcast_now(item=obj_payload, ignore_flag=False)
314
340
  match self.value:
315
341
  case EitherMonad.Right(value=value):
316
342
  # TODO: Consider using a more robust serialization method...
@@ -0,0 +1 @@
1
+ 0.44.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fred-oss
3
- Version: 0.43.0
3
+ Version: 0.44.0
4
4
  Summary: FREDOSS
5
5
  Home-page: https://fred.fahera.mx
6
6
  Author: Fahera Research, Education, and Development
@@ -1 +0,0 @@
1
- 0.43.0
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes