0

我有 python 装饰器,我需要在装饰器中传递 contextVariable 或作为函数 request_id中的参数

第 1 步:声明 contextVariables 和方法

_correlation_id_ctx_var: ContextVar[str] = ContextVar(CORRELATION_ID_CTX_KEY, default=None)
_request_id_ctx_var: ContextVar[str] = ContextVar(REQUEST_ID_CTX_KEY, default=None)


def get_correlation_id() -> str:
    return _correlation_id_ctx_var.get()


def get_request_id() -> str:
    return _request_id_ctx_var.get()

第 2 步:我在中间件中声明的上下文变量(使用 FastApi)

@app.middleware("http")
async def log_request(request: Request, call_next):
    correlation_id = _correlation_id_ctx_var.set(request.headers.get('X-Correlation-ID', str(uuid4())))
    request_id = _request_id_ctx_var.set(str(uuid4()))

Step3:我尝试将 contextVariable 传递给装饰器 - 它总是 None 尝试在函数本身中作为参数传递 - 它总是 None

这是什么问题?

为什么 contextVars 只能在函数体中访问,不能在装饰器或参数函数中访问?

是否有任何解决方案可以在函数体之前访问 contextVar?

@app.get('/test')
@decorator(request_id=get_request_id())
def test_purpose(request_id=get_request_id()):
    print('get_correlation_id() start', get_request_id())
    return 'ok'

装饰师:

def decorator(request_id=None, *args, **kwargs):
    def logger(func, request_id=None, *args, **kwargs):
        @wraps(func, *args, **kwargs)
        def wrapper(*args, **kwargs):
            try:
                res = func()
                print()
                return res
            except Exception:
              pass
        return wrapper
    return logger

4

1 回答 1

1

@decorator(request_id=get_request_id())<- 导入模块时执行此行。这意味着您的 getter 函数仅在导入模块时被调用一次,而不是每次调用您的装饰函数时。

要修复它,只需将 getter 函数而不是其结果传递给装饰器,然后在装饰器内部的包装函数内部进行调用。(为此,只需去掉括号):



def decorator(request_id_getter=None, *d_args, **d_kw): 
    def logger(func):  # other arguments than the first positional arg here are not used at all. 
        @wraps(func, *d_args, **d_kw)
        def wrapper(*args, **kwargs):
            request_id = request_id_getter()  # and here the value is retrieved when the function is actually called
            try:
                res = func()
                print()
                return res
            except Exception:
              pass
        return wrapper
    return logger

@app.get('/test')
@decorator(request_id=get_request_id)  # <- here is the change - do not _call_ the function!
def test_purpose(request_id=get_request_id()):
    print('get_correlation_id() start', get_request_id())
    return 'ok'

于 2022-02-18T13:53:39.840 回答