【问题标题】:Determine indentation level of line currently running Python Code确定当前运行 Python 代码的行的缩进级别
【发布时间】:2016-07-13 13:15:14
【问题描述】:

是否可以在程序运行时确定 Python 中一行的缩进级别?我希望能够根据正在运行的脚本的大纲结构来组织日志文件。

在下面的示例中,“第一条消息”函数会产生 0,“第二条消息”会产生 1,“第三条消息”会产生 2,“第四条、第五条、第六条”消息会产生 0

logger.debug('First message')
if True:
    logger.info('Second message')
    if True:
        logger.info('Third message')


logger.warning('Fourth message')
logger.error('Fifth message')
logger.critical('Sixth message')

相应的日志文件如下所示:

First message
    Second message
        Third message
Fourth message
Fifth message
Sixth message

【问题讨论】:

  • 你可以看看封闭的框架:stackoverflow.com/q/5326539/3001761,但这似乎是很多工作却收效甚微。
  • @jonrsharpe - 要求这样做的原因是要有一个测试用例的大纲,这样人们就可以看到失败发生的原因,而不必深入研究测试源代码。不同的缩进级别使其更易于阅读。

标签: python python-3.x logging indentation


【解决方案1】:

我能够使用 inspect.getouterframes() 函数确定缩进级别。这假定使用 4 个 ' ' 字符而不是 '\t' 字符进行缩进。

import inspect

def getIndentationLevel():    

    # get information about the previous stack frame
    frame, filename, line_number,function_name, lines, index = inspect.getouterframes(inspect.currentframe())[1]

    # count the number of characters in the line
    original_length = len(lines[0])

    # trim the line of left spaces and record the new length
    new_length = len(lines[0].lstrip())

    # take the difference between the two values and divide by 4 to get the level number
    return int((original_length - new_length) / 4)

【讨论】:

    【解决方案2】:

    首先,我检索调用者的代码上下文:

    import inspect
    
    context = inspect.getframeinfo(frame.f_back).code_context 
    

    这给了我一个代码行列表;我忽略了除第一行之外的所有内容。然后我使用正则表达式来获取这一行开头的空格;如果您使用制表符,则必须先将它们替换为适当数量的空格:

    import re
    
    indent = re.compile("^ *")
    
    firstline = context[0]
    firstline = firstline.replace("\t", "    ")
    match = indent.match(firstline)
    

    出于测试目的,我将空格替换为点,这样我就可以看到发生了什么,并构造我的前缀:

    white = "." * match.span(0)[1] # Change "." to " "!
    

    现在我可以用它来修改我发送给我的记录器的消息msg

    do_some_logging(white + msg)
    

    如果我可以简单地用一个装饰器包装一个现有的记录器,将这个记录器变成一个空白记录器,那就太好了。假设出于测试目的,我有一个非常原始的记录器,它只是将消息打印到标准输出:

    def pseudo_logger(msg):
        """Pseudo-logging function."""
        print(msg)
    

    这个记录器有一个记录消息的第一个参数,可能还有一些其他的位置参数和关键字。我想为这样的功能写一个装饰器:

    from functools import wraps
    
    def indented(func):
        """Turns the logger func(msg) into an indented logger."""
        @wraps(func)
        def wrapped(msg, *args, **kwargs):
            # ... compute white ...
            func(white + msg, *args, **kwargs)
        return wrapped
    

    现在我可以得到一个新的记录器,例如:

    new_logger = intented(pseudo_logger)
    

    把它们放在一起给出:

    from functools import wraps
    import inspect
    import re
    
    
    def indented(func):
        """Turns the logger func(msg) into an indented logger."""
        indent = re.compile("^ *")    
        @wraps(func)
        def wrapped(msg, *args, **kwargs):
            frame = inspect.currentframe()
            context = inspect.getframeinfo(frame.f_back).code_context
            firstline = context[0]
            match = indent.match(firstline)
            white = "." * match.span(0)[1] # Change "." to " "!
            func(white + msg, *args, **kwargs)
        return wrapped
    
    @indented
    def pseudo_logger(msg):
        """Pseudo-logging function."""
        print(msg)
    
    def main():
        pseudo_logger("This is an indented message!")
        if True:
            pseudo_logger("Another message, but more indented!")
    
        pseudo_logger("This "
                      "will ignore the indention of the second "
                      "or third line.")
    
    if __name__ == "__main__":
        main()
    

    不过,我会犹豫在生产代码中使用它。像这样使用代码检查很脆弱,并且根据你调用它的位置,它可能会导致意想不到的效果。

    【讨论】:

    • 你能提供一些你的代码示例的上下文/解释吗?
    • 当然,我添加了一些评论。
    猜你喜欢
    • 2020-05-08
    • 2017-01-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-11
    • 1970-01-01
    • 1970-01-01
    • 2016-03-30
    相关资源
    最近更新 更多