python-3.x - 装饰器中基于 Python 点击选项的登录
问题描述
新的开始。我有一个 CLI 应用程序,它使用 click 来处理参数解析。对于主要的“可执行”脚本,我有一个定义的详细程度标志(-v、-vv、-vvv、...)来控制日志记录的详细程度。我想“跟踪”特定函数的函数调用。下面是一个示例,希望可以清楚地说明。
import click
import logging
import functools
class MyLogger(object):
def __init__(self):
self.__logger = None
def init_logger(self, name, verbosity):
logging_levels = {0: logging.CRITICAL,
1: logging.ERROR,
2: logging.INFO,
3: logging.DEBUG}
logging.basicConfig(level=logging_levels.get(verbosity, logging.WARNING))
self.__logger = logging.getLogger(name)
@property
def logger(self):
return self.__logger
myLogger = MyLogger()
class TraceFunction(object):
def __init__(self, logger):
self.logger = logger
def __call__(self, function):
name = function.__name__
@functools.wraps(function)
def wrapped(*args, **kwargs):
self.logger.debug(f'{name}({list(*args)}, {dict(**kwargs)})')
result = function(*args, **kwargs)
self.logger.debug(f'{result}')
return result
return wrapped
# (1) @TraceFunction(myLogger.logger)
def echo(message):
return message.upper()
@click.command('echo')
@click.option('-e', '--echo', 'message', required=True, type=str)
def echo_command(message):
myLogger.logger.info('echo_command')
return echo(message)
@click.group()
@click.option('-v', 'verbosity', count=True)
def main(verbosity: int):
myLogger.init_logger(__name__, verbosity)
# (2) TraceFunction(myLogger.logger)(echo)
myLogger.logger.info('main')
if __name__ == '__main__':
main.add_command(echo_command)
main()
以上如果执行将正确产生以下输出:
script.py -vv echo -e "Hello World"
INFO: __main__:main
INFO: __main__:echo_command
我想“追踪”这个功能:echo。更准确地说,我想用实际参数和返回值记录实际的函数调用。好的,不止于此,但我需要一个最小的样本。为此,我尝试了两件事,在评论中标有 (1) 和 (2)。
@TraceFunction(myLogger.logger)
def echo(message):
return message.upper()
它完全不起作用,因为我最初的问题 python 将执行 TraceFunction。在“main”中调用(echo)之前我调用了init_logger,它本质上会配置记录器本身。作为 TraceFunction 的结果。调用记录器是无,我得到:
AttributeError: 'NoneType' object has no attribute 'debug'
好吧,我以后可以注册它,至少我想用(2)。好吧,异常肯定消失了,但是从不调用调用中定义的“包装”,并且除了已经显示的内容之外,再一次没有记录任何内容
script.py -vvv echo -e "Hello World"
INFO: __main__:main
INFO: __main__:echo_command
@Update 按照afterburner的回答,事情走得更远了,但它没有做它应该做的事情:
script.py -vvv echo -e "Hello World"
DEBUG:__main__:echo(['F','o','o'],{})
DEBUG:__main__:FOO
INFO: __main__:main
INFO: __main__:echo_command
预计哪个井。另一方面,预期的输出将是:
script.py -vvv echo -e "Hello World"
INFO: __main__:main
INFO: __main__:echo_command
DEBUG:__main__:echo(['Hello World'],{})
DEBUG:__main__:HELLO WORLD
解决方案
我设法让它工作,但它确实很丑……至少在目前的形式下。唯一需要的改变是:
@click.group()
@click.option('-v', 'verbosity', count=True)
def main(verbosity: int = 0):
global echo
myLogger.init_logger(__name__, verbosity)
echo = TraceFunction(myLogger.logger)(echo) # <<< !!!
myLogger.logger.info('main')
这样做的输出变为:
INFO:__main__:main
INFO:__main__:echo_command
TRACE:__main__:echo(['Hello World'], {})
TRACE:__main__:HELLO WORLD
所以答案是,我完全错过了:
TraceFunction(myLogger.logger)(echo)
很好,但我需要将它分配给原始的 echo 函数:
echo = TraceFunction(myLogger.logger)(echo)
推荐阅读
- c# - 如何在 asp.net core 3.1 中使用来自 asp.net framework 4.8 的 StatisticFormula 类
- javascript - 如何正确在Vue中全局导入css样式?
- python - 将带有时间的python folium热图导出到html
- python - 在财经新闻列表中查找公司匹配项
- c# - EF - 多对多插入
- assembly - 读取二进制文件并输出 base64:如何提取 6 位组?
- android - 写入/下载到 Android 后,如何使下载的文件在文件管理器中可见?
- javascript - 如何在更改窗口或选项卡时让浏览器发送通知
- dart - Dart 中的材料设计
- c - 如何在不跟踪全局索引的情况下将 BST 的节点数据递归存储到全局数组中