moose-lib 0.6.18__py3-none-any.whl → 0.6.20__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.

Potentially problematic release.


This version of moose-lib might be problematic. Click here for more details.

moose_lib/__init__.py CHANGED
@@ -4,8 +4,6 @@ from .blocks import *
4
4
 
5
5
  from .commons import *
6
6
 
7
- from .tasks import *
8
-
9
7
  from .data_models import *
10
8
 
11
9
  from .dmv2 import *
@@ -65,6 +65,7 @@ from .materialized_view import (
65
65
  )
66
66
 
67
67
  from .workflow import (
68
+ TaskContext,
68
69
  TaskConfig,
69
70
  Task,
70
71
  WorkflowConfig,
@@ -140,6 +141,7 @@ __all__ = [
140
141
  'MaterializedView',
141
142
 
142
143
  # Workflow
144
+ 'TaskContext',
143
145
  'TaskConfig',
144
146
  'Task',
145
147
  'WorkflowConfig',
@@ -11,16 +11,17 @@ from pydantic import BaseModel
11
11
  from .types import TypedMooseResource, T_none, U_none
12
12
  from ._registry import _workflows
13
13
 
14
- type TaskRunFunc[T_none, U_none] = Union[
15
- # Case 1: No input, no output
16
- Callable[[], None],
17
- # Case 2: No input, with output
18
- Callable[[], Union[U_none, Awaitable[U_none]]],
19
- # Case 3: With input, no output
20
- Callable[[T_none], None],
21
- # Case 4: With input, with output
22
- Callable[[T_none], Union[U_none, Awaitable[U_none]]]
23
- ]
14
+ @dataclasses.dataclass
15
+ class TaskContext(Generic[T_none]):
16
+ """Context object passed to task handlers.
17
+
18
+ - When a task declares an input model `T`, `input` is of type `T` (not Optional).
19
+ - For no-input tasks (`T` is `None`), `input` is exactly `None`.
20
+ """
21
+ state: Dict[str, Any]
22
+ input: T_none
23
+
24
+ type TaskRunFunc[T_none, U_none] = Callable[[TaskContext[T_none]], Union[U_none, Awaitable[U_none]]]
24
25
 
25
26
  @dataclasses.dataclass
26
27
  class TaskConfig(Generic[T_none, U_none]):
@@ -28,12 +29,16 @@ class TaskConfig(Generic[T_none, U_none]):
28
29
 
29
30
  Attributes:
30
31
  run: The handler function that executes the task logic.
32
+ Signature: run(context: TaskContext[T]) -> U
31
33
  on_complete: Optional list of tasks to run after this task completes.
32
- timeout: Optional timeout string (e.g. "5m", "1h").
34
+ on_cancel: Optional function to call when the task is cancelled.
35
+ Signature: on_cancel(context: TaskContext[T]) -> None
36
+ timeout: Optional timeout string (e.g. "5m", "1h", "never").
33
37
  retries: Optional number of retry attempts.
34
38
  """
35
39
  run: TaskRunFunc[T_none, U_none]
36
40
  on_complete: Optional[list["Task[U_none, Any]"]] = None
41
+ on_cancel: Optional[Callable[[TaskContext[T_none]], Union[None, Awaitable[None]]]] = None
37
42
  timeout: Optional[str] = None
38
43
  retries: Optional[int] = None
39
44
 
moose_lib/main.py CHANGED
@@ -220,7 +220,7 @@ class QueryClient:
220
220
 
221
221
 
222
222
  class WorkflowClient:
223
- """Client for interacting with Temporal workflows.
223
+ """Client for interacting with Temporal DMv2 workflows.
224
224
 
225
225
  Args:
226
226
  temporal_client: An instance of the Temporal client.
@@ -228,8 +228,6 @@ class WorkflowClient:
228
228
 
229
229
  def __init__(self, temporal_client: TemporalClient):
230
230
  self.temporal_client = temporal_client
231
- self.configs = self.load_consolidated_configs()
232
- print(f"WorkflowClient - configs: {self.configs}")
233
231
 
234
232
  # Test workflow executor in rust if this changes significantly
235
233
  def execute(self, name: str, input_data: Any) -> Dict[str, Any]:
@@ -278,42 +276,43 @@ class WorkflowClient:
278
276
  run_timeout = self.parse_timeout_to_timedelta(config['timeout_str'])
279
277
 
280
278
  print(
281
- f"WorkflowClient - starting {'DMv2 ' if config['is_dmv2'] else ''}workflow: {name} with retry policy: {retry_policy} and timeout: {run_timeout}")
279
+ f"WorkflowClient - starting DMv2 workflow: {name} with retry policy: {retry_policy} and timeout: {run_timeout}")
282
280
 
283
281
  # Start workflow with appropriate args
284
- workflow_args = self._build_workflow_args(name, processed_input, config['is_dmv2'])
282
+ workflow_args = self._build_workflow_args(name, processed_input)
283
+
284
+ # Handle "never" timeout by omitting run_timeout parameter
285
+ workflow_kwargs = {
286
+ "args": workflow_args,
287
+ "id": workflow_id,
288
+ "task_queue": "python-script-queue",
289
+ "id_conflict_policy": WorkflowIDConflictPolicy.FAIL,
290
+ "id_reuse_policy": WorkflowIDReusePolicy.ALLOW_DUPLICATE,
291
+ "retry_policy": retry_policy,
292
+ }
293
+
294
+ if run_timeout is not None:
295
+ workflow_kwargs["run_timeout"] = run_timeout
285
296
 
286
297
  workflow_handle = await self.temporal_client.start_workflow(
287
298
  "ScriptWorkflow",
288
- args=workflow_args,
289
- id=workflow_id,
290
- task_queue="python-script-queue",
291
- id_conflict_policy=WorkflowIDConflictPolicy.FAIL,
292
- id_reuse_policy=WorkflowIDReusePolicy.ALLOW_DUPLICATE,
293
- retry_policy=retry_policy,
294
- run_timeout=run_timeout
299
+ **workflow_kwargs
295
300
  )
296
301
 
297
302
  return workflow_id, workflow_handle.result_run_id
298
303
 
299
304
  def _get_workflow_config(self, name: str) -> Dict[str, Any]:
300
- """Extract workflow configuration from DMv2 or legacy config."""
305
+ """Extract workflow configuration from DMv2 workflow."""
301
306
  from moose_lib.dmv2 import get_workflow
302
307
 
303
308
  dmv2_workflow = get_workflow(name)
304
- if dmv2_workflow is not None:
305
- return {
306
- 'retry_count': dmv2_workflow.config.retries or 3,
307
- 'timeout_str': dmv2_workflow.config.timeout or "1h",
308
- 'is_dmv2': True
309
- }
310
- else:
311
- config = self.configs.get(name, {})
312
- return {
313
- 'retry_count': config.get('retries', 3),
314
- 'timeout_str': config.get('timeout', "1h"),
315
- 'is_dmv2': False
316
- }
309
+ if dmv2_workflow is None:
310
+ raise ValueError(f"DMv2 workflow '{name}' not found")
311
+
312
+ return {
313
+ 'retry_count': dmv2_workflow.config.retries or 3,
314
+ 'timeout_str': dmv2_workflow.config.timeout or "1h",
315
+ }
317
316
 
318
317
  def _process_input_data(self, name: str, input_data: Any) -> tuple[Any, str]:
319
318
  """Process input data and generate workflow ID."""
@@ -339,26 +338,16 @@ class WorkflowClient:
339
338
 
340
339
  return input_data, workflow_id
341
340
 
342
- def _build_workflow_args(self, name: str, input_data: Any, is_dmv2: bool) -> list:
343
- """Build workflow arguments based on workflow type."""
344
- if is_dmv2:
345
- return [f"{name}", input_data]
346
- else:
347
- return [f"{os.getcwd()}/app/scripts/{name}", input_data]
341
+ def _build_workflow_args(self, name: str, input_data: Any) -> list:
342
+ """Build workflow arguments for DMv2 workflow."""
343
+ return [{"workflow_name": name, "execution_mode": "start"}, input_data]
344
+
348
345
 
349
- # TODO: Remove when workflows dmv1 is removed
350
- def load_consolidated_configs(self):
351
- try:
352
- file_path = os.path.join(os.getcwd(), ".moose", "workflow_configs.json")
353
- with open(file_path, 'r') as file:
354
- data = json.load(file)
355
- config_map = {config['name']: config for config in data}
356
- return config_map
357
- except Exception as e:
358
- print(f"Could not load configs for workflows v1: {e}")
359
346
 
360
- def parse_timeout_to_timedelta(self, timeout_str: str) -> timedelta:
361
- if timeout_str.endswith('h'):
347
+ def parse_timeout_to_timedelta(self, timeout_str: str) -> Optional[timedelta]:
348
+ if timeout_str == "never":
349
+ return None # Unlimited execution timeout
350
+ elif timeout_str.endswith('h'):
362
351
  return timedelta(hours=int(timeout_str[:-1]))
363
352
  elif timeout_str.endswith('m'):
364
353
  return timedelta(minutes=int(timeout_str[:-1]))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: moose_lib
3
- Version: 0.6.18
3
+ Version: 0.6.20
4
4
  Home-page: https://www.fiveonefour.com/moose
5
5
  Author: Fiveonefour Labs Inc.
6
6
  Author-email: support@fiveonefour.com
@@ -1,18 +1,17 @@
1
- moose_lib/__init__.py,sha256=_XXfHrzRl-M5M1e3CwBQGGblVQYKnU5QJkipghe4bXU,184
1
+ moose_lib/__init__.py,sha256=LiUdVqmtASPqbMF53dXVZzCdU1jtFVx62_tiSbW65a0,162
2
2
  moose_lib/blocks.py,sha256=_wdvC2NC_Y3MMEnB71WTgWbeQ--zPNHk19xjToJW0C0,3185
3
3
  moose_lib/commons.py,sha256=FUpRv8D3-LeGcjhcqtDyiimz5izwpCq53h50ydxC_uA,3711
4
4
  moose_lib/data_models.py,sha256=PgoFXTzf4C66FlKUowMT4VOrwSRR9_bk36P43ub4LzU,10274
5
5
  moose_lib/dmv2-serializer.py,sha256=CL_Pvvg8tJOT8Qk6hywDNzY8MYGhMVdTOw8arZi3jng,49
6
6
  moose_lib/internal.py,sha256=UYF42oFujLH1g9GDo_HdP-AYipKTf_lReyVi6qs945A,14397
7
- moose_lib/main.py,sha256=WAxrM39rp5leAtHxZQZlC8-a8pA8Ksy1TjVQnq12YeA,17354
7
+ moose_lib/main.py,sha256=XcVX_sTnt5QbrPXKNLCKZGCvpFpE8oiqSG2S1So9ztI,16713
8
8
  moose_lib/query_param.py,sha256=kxcR09BMIsEg4o2qetjKrVu1YFRaLfMEzwzyGsKUpvA,6474
9
- moose_lib/tasks.py,sha256=6MXA0j7nhvQILAJVTQHCAsquwrSOi2zAevghAc_7kXs,1554
10
9
  moose_lib/clients/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
10
  moose_lib/clients/redis_client.py,sha256=UBCdxwgZpIOIOy2EnPyxJIAYjw_qmNwGsJQCQ66SxUI,8117
12
11
  moose_lib/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
12
  moose_lib/config/config_file.py,sha256=NyjY6YFraBel7vBk18lLkpGaPR1viKMAEv4ZldyfLIA,2585
14
13
  moose_lib/config/runtime.py,sha256=gF1wrUCbp0MxRU92k0vyItvWX2x6P1FuBRzM61nJE_U,2776
15
- moose_lib/dmv2/__init__.py,sha256=MXVSaMw8V0h06JlA_uomSjl6MXPerjC9tVo2KvIvLgE,2732
14
+ moose_lib/dmv2/__init__.py,sha256=3DVAtNMZUoP94CMJBFhuXfYEQXDbQUNKSgg9XnKqae0,2768
16
15
  moose_lib/dmv2/_registry.py,sha256=RweMQWHSvhxgSceZ5mfd0NGnJ-49VZORbaFmw-O0bFY,611
17
16
  moose_lib/dmv2/consumption.py,sha256=KJV9MKgrnVLIRhlFVZMDhTsXvOaZoMiHk4hnRePdPWA,8745
18
17
  moose_lib/dmv2/ingest_api.py,sha256=Snek9NGwaJl_BuImSWGtQq91m9D3AJ4qBoGiKZ-9yTQ,2323
@@ -25,14 +24,14 @@ moose_lib/dmv2/sql_resource.py,sha256=kUZoGqxhZMHMthtBZGYJBxTFjXkspXiWLXhJRYXgGU
25
24
  moose_lib/dmv2/stream.py,sha256=jiUWBsjFalLLP63mikOxyHRdieiDAlzf9lXfLye-Wjc,10761
26
25
  moose_lib/dmv2/types.py,sha256=5FsB0HLHFkYB-8cjJ0rtRUjqahVA-ToLr2JXT1lFiss,3276
27
26
  moose_lib/dmv2/view.py,sha256=fVbfbJgc2lvhjpGvpfKcFUqZqxKuLD4X59jdupxIe94,1350
28
- moose_lib/dmv2/workflow.py,sha256=ZNEMaYWGCkOw_1qropfl2m553aq5YG16Y0rOJjo8eak,5916
27
+ moose_lib/dmv2/workflow.py,sha256=_FY4-VRo7uWxRtoipxGSo04qzBb4pbP30iQei1W0Ios,6287
29
28
  moose_lib/streaming/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
29
  moose_lib/streaming/streaming_function_runner.py,sha256=toide6mpUIuHWv75Iv8PmKL6HlsPnBB2XRq2aIjOWBY,23421
31
30
  tests/__init__.py,sha256=0Gh4yzPkkC3TzBGKhenpMIxJcRhyrrCfxLSfpTZnPMQ,53
32
31
  tests/conftest.py,sha256=ZVJNbnr4DwbcqkTmePW6U01zAzE6QD0kNAEZjPG1f4s,169
33
32
  tests/test_moose.py,sha256=mBsx_OYWmL8ppDzL_7Bd7xR6qf_i3-pCIO3wm2iQNaA,2136
34
33
  tests/test_redis_client.py,sha256=d9_MLYsJ4ecVil_jPB2gW3Q5aWnavxmmjZg2uYI3LVo,3256
35
- moose_lib-0.6.18.dist-info/METADATA,sha256=ewael_1KEnFWIZtC7fMG8sY1dTNkn_B1PjgfiSaDcdo,730
36
- moose_lib-0.6.18.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
37
- moose_lib-0.6.18.dist-info/top_level.txt,sha256=XEns2-4aCmGp2XjJAeEH9TAUcGONLnSLy6ycT9FSJh8,16
38
- moose_lib-0.6.18.dist-info/RECORD,,
34
+ moose_lib-0.6.20.dist-info/METADATA,sha256=5oRl34L_r8CXqY2lKc__Tx2CX3M4pcQcbQCuojDbAS4,730
35
+ moose_lib-0.6.20.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
36
+ moose_lib-0.6.20.dist-info/top_level.txt,sha256=XEns2-4aCmGp2XjJAeEH9TAUcGONLnSLy6ycT9FSJh8,16
37
+ moose_lib-0.6.20.dist-info/RECORD,,
moose_lib/tasks.py DELETED
@@ -1,45 +0,0 @@
1
- from functools import wraps
2
- from typing import TypeVar, Callable, Any
3
- from .commons import Logger
4
- import asyncio
5
-
6
- T = TypeVar('T')
7
-
8
- def task(func: Callable[..., T] = None, *, retries: int = 3) -> Callable[..., T]:
9
- """Decorator to mark a function as a Moose task
10
-
11
- Args:
12
- func: The function to decorate
13
- retries: Number of times to retry the task if it fails
14
- """
15
- def validate_result(result: Any) -> None:
16
- """Ensure proper return format"""
17
- if not isinstance(result, dict):
18
- raise ValueError("Task must return a dictionary with 'task' and 'data' keys")
19
- if "task" not in result or "data" not in result:
20
- raise ValueError("Task result must contain 'task' and 'data' keys")
21
-
22
- def decorator(f: Callable[..., T]) -> Callable[..., T]:
23
- if asyncio.iscoroutinefunction(f):
24
- @wraps(f)
25
- async def wrapper(*args, **kwargs) -> T:
26
- result = await f(*args, **kwargs)
27
- validate_result(result)
28
- return result
29
- else:
30
- @wraps(f)
31
- def wrapper(*args, **kwargs) -> T:
32
- result = f(*args, **kwargs)
33
- validate_result(result)
34
- return result
35
-
36
- # Add the markers to the wrapper
37
- wrapper._is_moose_task = True
38
- wrapper._retries = retries
39
- wrapper.logger = Logger(is_moose_task=True)
40
- return wrapper
41
-
42
- # Handle both @task and @task() syntax
43
- if func is None:
44
- return decorator
45
- return decorator(func)