Skip to content

Commit 729e3e7

Browse files
authored
Implement workflow & activity registry (#14)
1 parent bd6f175 commit 729e3e7

File tree

6 files changed

+392
-4
lines changed

6 files changed

+392
-4
lines changed

cadence/__init__.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
"""
2+
Cadence Python Client
3+
4+
A Python framework for authoring workflows and activities for Cadence.
5+
"""
6+
7+
# Import main client functionality
8+
from .client import Client
9+
10+
__version__ = "0.1.0"
11+
12+
__all__ = [
13+
"Client",
14+
]

cadence/worker/__init__.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,16 @@
55
WorkerOptions
66
)
77

8+
from ._registry import (
9+
Registry,
10+
RegisterWorkflowOptions,
11+
RegisterActivityOptions,
12+
)
13+
814
__all__ = [
915
"Worker",
10-
"WorkerOptions"
11-
]
16+
"WorkerOptions",
17+
'Registry',
18+
'RegisterWorkflowOptions',
19+
'RegisterActivityOptions',
20+
]

cadence/worker/_registry.py

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Workflow and Activity Registry for Cadence Python Client.
4+
5+
This module provides a registry system for managing workflows and activities,
6+
similar to the Go client's registry.go implementation.
7+
"""
8+
9+
import logging
10+
from typing import Callable, Dict, Optional, Unpack, TypedDict
11+
12+
13+
logger = logging.getLogger(__name__)
14+
15+
16+
class RegisterWorkflowOptions(TypedDict, total=False):
17+
"""Options for registering a workflow."""
18+
name: Optional[str]
19+
alias: Optional[str]
20+
21+
22+
class RegisterActivityOptions(TypedDict, total=False):
23+
"""Options for registering an activity."""
24+
name: Optional[str]
25+
alias: Optional[str]
26+
27+
28+
class Registry:
29+
"""
30+
Registry for managing workflows and activities.
31+
32+
This class provides functionality to register, retrieve, and manage
33+
workflows and activities in a Cadence application.
34+
"""
35+
36+
def __init__(self):
37+
"""Initialize the registry."""
38+
self._workflows: Dict[str, Callable] = {}
39+
self._activities: Dict[str, Callable] = {}
40+
self._workflow_aliases: Dict[str, str] = {} # alias -> name mapping
41+
self._activity_aliases: Dict[str, str] = {} # alias -> name mapping
42+
43+
def workflow(
44+
self,
45+
func: Optional[Callable] = None,
46+
**kwargs: Unpack[RegisterWorkflowOptions]
47+
) -> Callable:
48+
"""
49+
Register a workflow function.
50+
51+
This method can be used as a decorator or called directly.
52+
53+
Args:
54+
func: The workflow function to register
55+
**kwargs: Options for registration (name, alias)
56+
57+
Returns:
58+
The decorated function or the function itself
59+
60+
Raises:
61+
KeyError: If workflow name already exists
62+
"""
63+
options = RegisterWorkflowOptions(**kwargs)
64+
65+
def decorator(f: Callable) -> Callable:
66+
workflow_name = options.get('name') or f.__name__
67+
68+
if workflow_name in self._workflows:
69+
raise KeyError(f"Workflow '{workflow_name}' is already registered")
70+
71+
self._workflows[workflow_name] = f
72+
73+
# Register alias if provided
74+
alias = options.get('alias')
75+
if alias:
76+
if alias in self._workflow_aliases:
77+
raise KeyError(f"Workflow alias '{alias}' is already registered")
78+
self._workflow_aliases[alias] = workflow_name
79+
80+
logger.info(f"Registered workflow '{workflow_name}'")
81+
return f
82+
83+
if func is None:
84+
return decorator
85+
return decorator(func)
86+
87+
def activity(
88+
self,
89+
func: Optional[Callable] = None,
90+
**kwargs: Unpack[RegisterActivityOptions]
91+
) -> Callable:
92+
"""
93+
Register an activity function.
94+
95+
This method can be used as a decorator or called directly.
96+
97+
Args:
98+
func: The activity function to register
99+
**kwargs: Options for registration (name, alias)
100+
101+
Returns:
102+
The decorated function or the function itself
103+
104+
Raises:
105+
KeyError: If activity name already exists
106+
"""
107+
options = RegisterActivityOptions(**kwargs)
108+
109+
def decorator(f: Callable) -> Callable:
110+
activity_name = options.get('name') or f.__name__
111+
112+
if activity_name in self._activities:
113+
raise KeyError(f"Activity '{activity_name}' is already registered")
114+
115+
self._activities[activity_name] = f
116+
117+
# Register alias if provided
118+
alias = options.get('alias')
119+
if alias:
120+
if alias in self._activity_aliases:
121+
raise KeyError(f"Activity alias '{alias}' is already registered")
122+
self._activity_aliases[alias] = activity_name
123+
124+
logger.info(f"Registered activity '{activity_name}'")
125+
return f
126+
127+
if func is None:
128+
return decorator
129+
return decorator(func)
130+
131+
def get_workflow(self, name: str) -> Callable:
132+
"""
133+
Get a registered workflow by name.
134+
135+
Args:
136+
name: Name or alias of the workflow
137+
138+
Returns:
139+
The workflow function
140+
141+
Raises:
142+
KeyError: If workflow is not found
143+
"""
144+
# Check if it's an alias
145+
actual_name = self._workflow_aliases.get(name, name)
146+
147+
if actual_name not in self._workflows:
148+
raise KeyError(f"Workflow '{name}' not found in registry")
149+
150+
return self._workflows[actual_name]
151+
152+
def get_activity(self, name: str) -> Callable:
153+
"""
154+
Get a registered activity by name.
155+
156+
Args:
157+
name: Name or alias of the activity
158+
159+
Returns:
160+
The activity function
161+
162+
Raises:
163+
KeyError: If activity is not found
164+
"""
165+
# Check if it's an alias
166+
actual_name = self._activity_aliases.get(name, name)
167+
168+
if actual_name not in self._activities:
169+
raise KeyError(f"Activity '{name}' not found in registry")
170+
171+
return self._activities[actual_name]
172+
173+
174+

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ dependencies = [
3535

3636
[project.optional-dependencies]
3737
dev = [
38-
"pytest>=7.0.0",
38+
"pytest>=8.4.1",
3939
"pytest-cov>=4.0.0",
4040
"pytest-asyncio>=0.21.0",
4141
"black>=23.0.0",

0 commit comments

Comments
 (0)