如何执行存储为字符串的布尔逻辑,最好不使用 eval()?

How to execute a boolean logic stored as a string, preferably without eval()?

我有以下 YAML 文件,包含匹配文件名的解析逻​​辑:

rules_container = """
file_name:
  rule_1: {module: str, method: startswith, args: [s_]}
  rule_2: {module: str, method: endswith, args: [.log]}
  logic: rule_1 and rule_2
"""

我加载它并得到以下映射:

>>> import yaml
>>> import pprint
>>> d_yml = yaml.safe_load(rules_container)
>>> pprint.pprint(d_yml)
{'file_name': {'logic': 'rule_1 and rule_2',
               'rule_1': {'args': ['s_'],
                          'method': 'startswith',
                          'module': 'str'},
               'rule_2': {'args': ['.log'],
                          'method': 'endswith',
                          'module': 'str'}}}

下面的函数帮我解析了上面的规则,最终得到:

def rule_compiler(module, method, args=None, kwargs=None):

    def str_parser(string):
        return getattr(string, method)(*args)
    
    return str_parser
def rule_parser(container):
  
  compiled_rules = {
      k:rule_compiler(**v) for k,v in container.items() if k.startswith('rule')
  }
  
  logic = container.get('logic', None)
  
  return compiled_rules, logic

注意:我在这里没有使用 modulekwargs,但它们在配置文件的其他地方用于其他规则。

所以,如果我在规则的容器上调用 rule_parser(),我会得到:

>>> rp = rule_parser(d_yml['file_name'])
>>> print(rp)
({'rule_1': <function __main__.rule_compiler.<locals>.str_parser(string)>,
  'rule_2': <function __main__.rule_compiler.<locals>.str_parser(string)>},
 'rule_1 and rule_2')

如果我尝试使用每条规则解析字符串,它们会按预期工作:

>>> fname = 's_test.out'
>>> rp[0]['rule_1'](fname)
True
>>> rp[0]['rule_2'](fname)
False

我想应用“逻辑”定义的逻辑并得到这个:

>>> rp[0]['rule_1'](fname) and rp[0]['rule_2'](fname)
False

我已经想到了一种方法,通过解析包含布尔逻辑的字符串,将其转换为与上述类似的内容,然后对其调用 eval()

你能想到不涉及eval()的其他方法吗?

注意:可能有两个以上的规则,因此用简单的“and/or”替换逻辑,然后使用某种 if 语句来确定应用哪一个不会要么工作。

注意:在这种特定情况下,使用正则表达式并完全消除对多个规则的需求不是一种选择。

谢谢!

这是一个想法,使用 pandas.eval,不确定它是否适合你的情况。

import pandas as pd

fname = 's_test.out'
rule, logic = rp

def apply_rule(rule_dict: dict, fname: str) -> dict:
    return {rule: func(fname) 
        for rule, func in rule_dict.items()}

print(pd.eval(logic, local_dict = apply_rule(rule, fname)))

输出:

False

正确的答案可以跨越任何地方,从使用像 boolean.py to parse your strings (less flexible, probably easiest choice other than eval) to reading up on lexers and parsers 这样的库并从头开始实施,或者使用解析器生成软件(最灵活,但困难且耗时)。在这种情况下,我可能会建议从专门为评估布尔表达式而构建的库开始,如果这不起作用,请尝试更复杂的东西。

编辑:我应该注意,如果没有用户注入恶意代码的入口点,您可以可以使用eval,您可以正确清理 eval 字符串等.- 可能不值得你花时间,但如果其他选项不适合你的需要,则值得考虑。