Pyramid-Temporal integration library.
This package provides automatic transaction management for Temporal activities using pyramid_tm, similar to how it works for web requests.
Main components: - Worker: Pyramid-aware Temporal Worker with automatic context binding - PyramidEnvironment: Wrapper for Pyramid bootstrap environment - activity: Decorator module for defining pyramid-temporal activities - ActivityContext: Context object providing real Pyramid requests to activities
Activities receive real Pyramid Request objects (via Pyramid's request factory), so all request methods configured via add_request_method work automatically (dbsession, tm, etc.).
Each activity execution owns its request and its transaction, so activities can
run concurrently. Activities may be written as async def or as plain def;
sync ones run in Temporal's activity executor, which keeps a blocking body off
the worker's event loop.
Example
from pyramid.paster import bootstrap from pyramid_temporal import Worker, activity, ActivityContext, PyramidEnvironment
@activity.defn async def enrich_user(context: ActivityContext, user_id: int) -> bool: # Real Pyramid request with all configured methods session = context.request.dbsession user = session.query(User).get(user_id) user.enriched = True return True
In worker setup:¶
env = PyramidEnvironment.from_bootstrap(bootstrap('development.ini')) worker = Worker( client, env, task_queue="my-queue", activities=[enrich_user], workflows=[MyWorkflow], ) await worker.run()
ActivityContext
¶
Context object providing Pyramid integration for one activity execution.
A context belongs to a single execution. The bound activity creates one per invocation, so concurrent executions never share a request, and therefore never share a dbsession or a transaction.
The request has all the same properties and methods as a web request, including any configured via add_request_method (like dbsession, tm, etc.).
Example
@activity.defn async def my_activity(context: ActivityContext, user_id: int) -> bool: # Real Pyramid request with all configured methods session = context.request.dbsession user = session.query(User).get(user_id) return user is not None
Source code in pyramid_temporal/context.py
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 | |
env
property
¶
Get the Pyramid environment.
registry
property
¶
Get the Pyramid registry.
request
property
¶
Get this execution's request.
This is a real Pyramid Request object with all configured request methods (dbsession, tm, etc.) available.
| Raises: |
|
|---|
settings
property
¶
Get application settings (shortcut to registry.settings).
__init__(env)
¶
Initialize the activity context.
| Parameters: |
|
|---|
Source code in pyramid_temporal/context.py
65 66 67 68 69 70 71 72 73 | |
close_request()
¶
Close this execution's request and clean up resources.
Processes finished callbacks and tears down the threadlocal scope.
Source code in pyramid_temporal/context.py
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 | |
create_request(*, threadlocal_request=True)
¶
Create the Pyramid Request for this activity execution.
Uses Pyramid's request factory to create a real request, applies request extensions (add_request_method), and opens a threadlocal scope.
| Parameters: |
|
|---|
| Returns: |
|
|---|
| Raises: |
|
|---|
Source code in pyramid_temporal/context.py
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 | |
PyramidActivity
¶
Wrapper for pyramid-temporal activities.
This class wraps an activity function and provides the ability to bind it to a Pyramid environment for execution.
The wrapper is what workflow code references, so it carries a Temporal
activity definition and is callable: workflow.execute_activity(my_activity,
...) reads the registered name from that definition. What actually runs is
the bound wrapper bind gives the Worker, and both carry the same name.
Source code in pyramid_temporal/activity.py
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 | |
fn
property
¶
Get the original function.
is_async
property
¶
Whether the activity body is a coroutine function.
name
property
¶
Get the activity name.
__call__(context, *args, **kwargs)
¶
Run the activity body inside an execution that already exists.
This is how an activity body is exercised as a plain function, given a
context from activity_execution. Async bodies come back as their
coroutine, for the caller to await.
| Raises: |
|
|---|
Source code in pyramid_temporal/activity.py
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 | |
__init__(fn, name=None, no_thread_cancel_exception=False)
¶
Initialize the pyramid activity wrapper.
| Parameters: |
|
|---|
Source code in pyramid_temporal/activity.py
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 | |
bind(env)
¶
Bind this activity to a Pyramid environment for Temporal registration.
Every call of the returned activity is one execution, and owns its own ActivityContext, Pyramid request, and transaction. Nothing is shared between executions, so they are safe to run concurrently.
An async activity binds to a coroutine function, which Temporal runs on the worker's event loop. A sync activity binds to a plain function, which Temporal runs in its activity executor, so a blocking body leaves the event loop free.
| Parameters: |
|
|---|
| Returns: |
|
|---|
Source code in pyramid_temporal/activity.py
259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 | |
PyramidEnvironment
¶
Wrapper for Pyramid bootstrap environment.
This class wraps the output of pyramid.paster.bootstrap, providing structured access to the Pyramid application components.
| Attributes: |
|
|---|
Example
from pyramid.paster import bootstrap from pyramid_temporal import PyramidEnvironment
Create from bootstrap output¶
env_dict = bootstrap('development.ini') env = PyramidEnvironment.from_bootstrap(env_dict)
Access components¶
settings = env.settings registry = env.registry
Clean up when done¶
env.close()
Source code in pyramid_temporal/environment.py
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | |
app
property
¶
Get the WSGI application.
registry
property
¶
Get the Pyramid registry.
request
property
¶
Get the base request object from bootstrap.
root
property
¶
Get the root object for traversal-based applications.
settings
property
¶
Get application settings (shortcut to registry.settings).
__init__(registry, app=None, request=None, root=None, closer=None)
¶
Initialize the Pyramid environment.
| Parameters: |
|
|---|
Source code in pyramid_temporal/environment.py
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | |
__repr__()
¶
Return string representation.
Source code in pyramid_temporal/environment.py
131 132 133 | |
close()
¶
Clean up resources.
This calls the closer function from bootstrap to properly clean up the Pyramid application.
Source code in pyramid_temporal/environment.py
121 122 123 124 125 126 127 128 129 | |
from_bootstrap(env)
classmethod
¶
Create a PyramidEnvironment from bootstrap output.
This is the preferred way to create a PyramidEnvironment when using pyramid.paster.bootstrap.
| Parameters: |
|
|---|
| Returns: |
|
|---|
Example
from pyramid.paster import bootstrap from pyramid_temporal import PyramidEnvironment
env = PyramidEnvironment.from_bootstrap(bootstrap('development.ini'))
Source code in pyramid_temporal/environment.py
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | |
Worker
¶
Pyramid-aware Temporal Worker.
This worker wraps the standard Temporal Worker and binds pyramid-temporal
activities to the Pyramid environment. Each activity execution gets its own
Pyramid request and its own transaction, so max_concurrent_activities
greater than 1 is safe.
Activities written as plain def functions run in Temporal's activity
executor. The worker creates a thread pool for them unless the caller passes
its own activity_executor.
Example
from pyramid.paster import bootstrap from pyramid_temporal import Worker, activity, PyramidEnvironment
@activity.defn async def my_activity(context: ActivityContext, user_id: int) -> bool: session = context.request.dbsession # ... do work ... return True
Create environment from bootstrap¶
env = PyramidEnvironment.from_bootstrap(bootstrap('development.ini'))
worker = Worker( client, env, task_queue="my-queue", activities=[my_activity], workflows=[MyWorkflow], max_concurrent_activities=10, )
Run the worker¶
await worker.run()
Source code in pyramid_temporal/worker.py
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 | |
activity_executor
property
¶
Get the activity executor the worker created for sync activities.
env
property
¶
Get the Pyramid environment.
task_queue
property
¶
Get the task queue name.
__aenter__()
async
¶
Async context manager entry.
Source code in pyramid_temporal/worker.py
226 227 228 229 | |
__aexit__(*args)
async
¶
Async context manager exit.
Source code in pyramid_temporal/worker.py
231 232 233 234 235 236 | |
__init__(client, env, *, task_queue, activities=(), workflows=(), interceptors=(), **kwargs)
¶
Initialize the Pyramid-aware worker.
| Parameters: |
|
|---|
Source code in pyramid_temporal/worker.py
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | |
run()
async
¶
Run the worker until shutdown is requested.
This is the main entry point for running the worker. It will poll the task queue and execute activities/workflows.
Source code in pyramid_temporal/worker.py
214 215 216 217 218 219 220 221 222 223 224 | |
activity_execution(env, *, threadlocal_request)
¶
Give one activity execution its own request and its own transaction.
The transaction commits when the body returns and aborts when it raises, and the request is closed either way. Nothing is shared with any other execution, so activities are safe to run concurrently.
| Parameters: |
|
|---|
| Yields: |
|
|---|
Source code in pyramid_temporal/execution.py
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | |
defn(fn=None, *, name=None, no_thread_cancel_exception=False)
¶
Decorator to define a pyramid-temporal activity.
This decorator marks a function as a pyramid-temporal activity that will receive an ActivityContext as its first argument. The context is automatically injected when the activity is executed via the pyramid-temporal Worker.
The decorated function should have ActivityContext as its first parameter:
@activity.defn
async def my_activity(context: ActivityContext, user_id: int) -> bool:
session = context.request.dbsession
# ... do work ...
return True
A plain def works too, and is the better choice whenever the body
blocks. Temporal runs sync activities in its activity executor, so a
blocking call never occupies the worker's event loop:
@activity.defn
def my_blocking_activity(context: ActivityContext, user_id: int) -> bool:
session = context.request.dbsession
# ... blocking HTTP, gRPC, or database work ...
return True
Workflow code references the decorated activity itself, and Temporal reads the registered name from it:
await workflow.execute_activity(
my_activity, user_id, start_to_close_timeout=timedelta(seconds=30)
)
| Parameters: |
|
|---|
| Returns: |
|
|---|
Example
@activity.defn async def process_order(context: ActivityContext, order_id: int) -> bool: session = context.request.dbsession order = session.query(Order).get(order_id) # Process the order... return True
Or with custom name:¶
@activity.defn(name="custom-activity-name") async def my_activity(context: ActivityContext, data: str) -> str: return data.upper()
Source code in pyramid_temporal/activity.py
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | |
includeme(config)
¶
Pyramid configuration include function.
This function can be called via config.include('pyramid_temporal') to register pyramid-temporal with a Pyramid application.
Configuration settings: - pyramid_temporal.temporal_host: Temporal server host (default: localhost:7233) - pyramid_temporal.temporal_namespace: Temporal namespace (default: default). The alias pyramid_temporal.namespace is also accepted and takes precedence. - pyramid_temporal.task_queue: Default task queue for started workflows (default: default) - pyramid_temporal.log_level: Logging level (default: INFO) - pyramid_temporal.auto_connect: Auto-connect to Temporal on startup (default: True)
Request methods registered: - request.temporal_client: The connected async Temporal client (or None). - request.temporal_start_workflow(workflow_run, arg, , id, task_queue=None) -> run_id - request.temporal_signal_workflow(workflow_id, run_id, signal, args) -> None
| Parameters: |
|
|---|
Example
from pyramid.config import Configurator
def main():
config = Configurator()
config.include('pyramid_temporal')
# Optional: Configure Temporal connection
config.registry.settings['pyramid_temporal.temporal_host'] = 'localhost:7233'
# ... rest of configuration
Source code in pyramid_temporal/__init__.py
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 | |
is_pyramid_activity(obj)
¶
Check if an object is a pyramid-temporal activity.
| Parameters: |
|
|---|
| Returns: |
|
|---|
Source code in pyramid_temporal/activity.py
302 303 304 305 306 307 308 309 310 311 | |
signal_workflow(*, temporal_host, namespace, workflow_id, run_id, signal, args=())
¶
Connect to Temporal and signal a running workflow.
Source code in pyramid_temporal/client.py
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 | |
start_workflow(*, temporal_host, namespace, task_queue, workflow_run, arg, id, wait=False)
¶
start_workflow(*, temporal_host: str, namespace: str, task_queue: str, workflow_run: Any, arg: Any, id: str, wait: Literal[False] = False) -> str
start_workflow(*, temporal_host: str, namespace: str, task_queue: str, workflow_run: Any, arg: Any, id: str, wait: Literal[True]) -> Any
Connect to Temporal and start a workflow.
By default this returns the run_id of the started workflow without waiting.
Pass wait=True to block until the workflow completes and return its result
instead (equivalent to Temporal's execute_workflow).
Source code in pyramid_temporal/client.py
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | |
temporal_signal_workflow(request, workflow_id, run_id, signal, *args)
¶
Signal a running Temporal workflow using registry connection settings.
Source code in pyramid_temporal/__init__.py
263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 | |
temporal_start_workflow(request, workflow_run, arg, *, id, task_queue=None)
¶
Start a Temporal workflow using connection settings from the registry.
The task queue defaults to the pyramid_temporal.task_queue setting, so callers
can omit it. Pass task_queue to override the configured queue for a single call.
Returns the started workflow run_id.
Source code in pyramid_temporal/__init__.py
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 | |