@@ -94,6 +94,7 @@ informal introduction to the features and their implementation.
94
94
- [ Heartbeating and Cancellation] ( #heartbeating-and-cancellation )
95
95
- [ Worker Shutdown] ( #worker-shutdown )
96
96
- [ Testing] ( #testing-1 )
97
+ - [ Interceptors] ( #interceptors )
97
98
- [ Nexus] ( #nexus )
98
99
- [ Workflow Replay] ( #workflow-replay )
99
100
- [ Observability] ( #observability )
@@ -1310,6 +1311,70 @@ affect calls activity code might make to functions on the `temporalio.activity`
1310
1311
* ` worker_shutdown() ` can be invoked to simulate a worker shutdown during execution of the activity
1311
1312
1312
1313
1314
+ ### Interceptors
1315
+
1316
+ The behavior of the SDK can be customized in many useful ways by modifying inbound and outbound calls using
1317
+ interceptors. This is similar to the use of middleware in other frameworks.
1318
+
1319
+ There are five categories of inbound and outbound calls that you can modify in this way:
1320
+
1321
+ 1 . Outbound client calls, such as ` start_workflow() ` , ` signal_workflow() ` , ` list_workflows() ` , ` update_schedule() ` , etc.
1322
+
1323
+ 2 . Inbound workflow calls: ` execute_workflow() ` , ` handle_signal() ` , ` handle_update_handler() ` , etc
1324
+
1325
+ 3 . Outbound workflow calls: ` start_activity() ` , ` start_child_workflow() ` , ` start_nexus_operation() ` , etc
1326
+
1327
+ 4 . Inbound call to execute an activity: ` execute_activity() `
1328
+
1329
+ 5 . Outbound activity calls: ` info() ` and ` heartbeat() `
1330
+
1331
+
1332
+ To modify outbound client calls, define a class inheriting from
1333
+ [ ` client.Interceptor ` ] ( https://python.temporal.io/temporalio.client.Interceptor.html ) , and implement the method
1334
+ ` intercept_client() ` to return an instance of
1335
+ [ ` OutboundInterceptor ` ] ( https://python.temporal.io/temporalio.client.OutboundInterceptor.html ) that implements the
1336
+ subset of outbound client calls that you wish to modify.
1337
+
1338
+ Then, pass a list containing an instance of your ` client.Interceptor ` class as the
1339
+ ` interceptors ` argument of [ ` Client.connect() ` ] ( https://python.temporal.io/temporalio.client.Client.html#connect ) .
1340
+
1341
+ The purpose of the interceptor framework is that the methods you implement on your interceptor classes can perform
1342
+ arbitrary side effects and/or arbitrary modifications to the data, before it is received by the SDK's "real"
1343
+ implementation. The ` interceptors ` list can contain multiple interceptors. In this case they form a chain: a method
1344
+ implemented on an interceptor instance in the list can perform side effects, and modify the data, before passing it on
1345
+ to the corresponding method on the next interceptor in the list. Your interceptor classes need not implement every
1346
+ method; the default implementation is always to pass the data on to the next method in the interceptor chain.
1347
+
1348
+ The remaining four categories are worker calls. To modify these, define a class inheriting from
1349
+ [ ` worker.Interceptor ` ] ( https://python.temporal.io/temporalio.worker.Interceptor.html ) and implement methods on that
1350
+ class to define the
1351
+ [ ` ActivityInboundInterceptor ` ] ( https://python.temporal.io/temporalio.worker.ActivityInboundInterceptor.html ) ,
1352
+ [ ` ActivityOutboundInterceptor ` ] ( https://python.temporal.io/temporalio.worker.ActivityOutboundInterceptor.html ) ,
1353
+ [ ` WorkflowInboundInterceptor ` ] ( https://python.temporal.io/temporalio.worker.WorkflowInboundInterceptor.html ) , and
1354
+ [ ` WorkflowOutboundInterceptor ` ] ( https://python.temporal.io/temporalio.worker.WorkflowOutboundInterceptor.html ) classes
1355
+ that you wish to use to effect your modifications. Then, pass a list containing an instance of your ` worker.Interceptor `
1356
+ class as the ` interceptors ` argument of the [ ` Worker() ` ] ( https://python.temporal.io/temporalio.worker.Worker.html )
1357
+ constructor.
1358
+
1359
+ It often happens that your worker and client interceptors will share code because they implement closely related logic.
1360
+ For convenience, you can create an interceptor class that inherits from _ both_ ` client.Interceptor ` and
1361
+ ` worker.Interceptor ` (their method sets do not overlap). You can then pass this in the ` interceptors ` argument of
1362
+ ` Client.connect() ` when starting your worker _ as well as_ in your client/starter code. If you do this, your worker will
1363
+ automatically pick up the interceptors from its underlying client (and you should not pass them directly to the
1364
+ ` Worker() ` constructor).
1365
+
1366
+ This is best explained by example. The [ Context Propagation Interceptor
1367
+ Sample] ( https://github.com/temporalio/samples-python/tree/main/context_propagation ) is a good starting point. In
1368
+ [ context_propagation/interceptor.py] ( https://github.com/temporalio/samples-python/blob/main/context_propagation/interceptor.py )
1369
+ a class is defined that inherits from both ` client.Interceptor ` and ` worker.Interceptor ` . It implements the various
1370
+ methods such that the outbound client and workflow calls set a certain key in the outbound ` headers ` field, and the
1371
+ inbound workflow and activity calls retrieve the header value from the inbound workflow/activity input data. An instance
1372
+ of this interceptor class is passed to ` Client.connect() ` when [ starting the
1373
+ worker] ( https://github.com/temporalio/samples-python/blob/main/context_propagation/worker.py ) and when connecting the
1374
+ client in the [ workflow starter
1375
+ code] ( https://github.com/temporalio/samples-python/blob/main/context_propagation/starter.py ) .
1376
+
1377
+
1313
1378
### Nexus
1314
1379
1315
1380
⚠️ ** Nexus support is currently at an experimental release stage. Backwards-incompatible changes are anticipated until a stable release is announced.** ⚠️
0 commit comments