Transaction callbacks and middlewares¶
In most cases, the handler is executed by calling one of the Application methods: call(),
execute(), or publish(), which creates the transaction context under the
hood.
When call() or similar method is executed, the lifecycle of handler execution is as following:
on_enter_transaction_contextcallback is invokedtransaction_middlewarefunctions, wrapping the handler are invokedhandler function is invoked
on_exit_transaction_contextcallback is invoked
The application can be configured to use any of the callbacks by using decorators:
from typing import Optional, Callable
from lato import Application, TransactionContext
app = Application()
@app.on_enter_transaction_context
def on_enter_transaction_context(ctx: TransactionContext):
print("starting transaction")
@app.on_exit_transaction_context
def on_exit_transaction_context(ctx: TransactionContext,
exception: Optional[Exception]=None):
print("exiting transaction context")
@app.transaction_middleware
def middleware1(ctx: TransactionContext, call_next: Callable):
print("entering middleware1")
result = call_next() # will call middleware2
print("exiting middleware1")
return result
@app.transaction_middleware
def middleware2(ctx: TransactionContext, call_next: Callable):
print("entering middleware2")
result = call_next() # will call the handler
print("exiting middleware2")
return f"[{result}]"
def to_uppercase(s):
print("calling handler")
return s.upper()
print(app.call(to_uppercase, "foo"))
This will generate the output:
starting transaction
entering middleware1
entering middleware2
calling handler
exiting middleware2
exiting middleware1
exiting transaction context
[FOO]
Any of the callbacks is optional, and there can be multiple transaction_middleware callbacks.
on_enter_transaction_context is a good place to set up the transaction level dependencies, i.e. the dependencies
that change with every transaction - correlation id, current time, database session, etc. call_next is a functools.partial object,
so you can inspect is arguments using call_next.args and call_next.keywords.