Pyparsing:将支票分成 2 个单独的部分时出现问题,如果第一部分的检查失败,则应继续检查第二部分

内梅利斯

对于我们的客户,我们定义了一种特定于域的(自动)测试(脚本)语言来简化测试用例的设置。

ASC 文件中的每个测试由三部分组成:

test <name> - <options> # <-- defines the start of a test and some general options
    <testheader>        # <-- contains a number of header commands which need to be always filled in
    <testbody>          # <-- the real test-actions

允许在 a<testheader><testbody>whitelines 中(使测试用例更好地可读)

为了检查这些 ASC 文件中的定义是否正确,我们制作了一个验证器脚本,它以以下方式检查测试:

# definitions of valid_header_command and valid_test_command not listed here since they themselves are not of importance for the question (just lists with definitions of keywords for those particular sections)
anyotherline = restOfLine - Optional(LineEnd())

test_command = NotAny(OneOfKeywords('if', 'elif', 'else', 'fi') | eot) - (valid_header_command | valid_test_command | anyotherline)

block = Forward()
pre_post_block = Forward()

if_statement = Keyword('if') - vp_expression - eol
then_block = ZeroOrMore(block)
elif_block = Keyword('elif') - vp_expression - eol - ZeroOrMore(block)
else_block = Keyword('else') - eol - ZeroOrMore(block)
fi_statement = Keyword('fi') - eol

conditional_block = if_statement - then_block - ZeroOrMore(elif_block) - Optional(else_block) - fi_statement
block << ( OneOrMore(test_command) | conditional_block ) # pylint: disable=expression-not-assigned

test_implementation = (OneOrMore(block) + eot).setParseAction(self._parseaction_validate_mandatory_header_commands)

test_name = CharsNotIn(' +:!,?;@=()\n\r')
test_options = ( #option-definitios
               )
test_definition = Keyword('test') - White(' ') - test_name.addParseAction(self._parseaction_validate_unique_testcase).addParseAction(self._parseaction_reset_per_testcase_data) - test_options - eol
# if we can't find a test_definition, but we can find a line with something on it (so not the end of file), then report an error
testcase = (test_definition - test_implementation) | (restOfLine + ~StringEnd() + LineEnd()).setParseAction(self._parseaction_errorExpectingNextTest)

这适用于最重要的部分,但我们看到当有人if<testheader>命令周围放置一个以防止必须编写 2 个仅在标题中不同的测试用例时,会发生一些奇怪的行为

经过长时间的考虑,我们决定不允许if<testheader>命令周围使用,因为只有<testheader>不同的情况非常罕见

所以现在我们想要改变实现,让它不再允许if围绕 a 的语句<testheader>为此,我们想尝试一种方法,就像我们所做的那样,testcase在检查的其余部分之前使用单独的检查test_definition(定义test关键字)(注意:我们必须保持向后兼容,因为几乎从不使用周围的标题部分)。<testheader><testbody>if

我们尝试的是:

  • 将旧的拆分test_command为一个header_command部分和一个test_command部分(仅上面片段中更改的代码):

    header_command = NotAny(OneOfKeywords('if', 'elif', 'else', 'fi') | eot) - (valid_header_command)
    test_command = NotAny(OneOfKeywords('if', 'elif', 'else', 'fi') | eot) - (valid_test_command | anyotherline)
    ....
    test_implementation = OneOrMore(header_command).setParseAction(self._parseaction_validate_mandatory_header_commands) + OneOrMore(block) + eot
    

    对于<testheader>命令,此解决方案有效。但是现在它在每个<testbody>命令上都失败了,因为它们与该header_command部分不匹配,test_command如果它在该header_command部分中失败,我们希望它继续部分。

    再次注意:标题和正文部分中都允许使用空格,因此我们不能将它们用作分隔符。而且我们必须保持向后兼容,因此很难/不可能引入任何其他分隔符。

  • 我们还尝试保留原始代码但向该valid_header_command部分添加检查,但这不起作用,因为尽管conditional_block定义是block它的一部分,但它也包含block,因此只有当if语句的部分已经被处理时,它才会处理test_command包含检查的剩余部分在valid_header_command所以在那里处理它太晚了。

  • 最后:我们考虑过更改_parseaction_validate_mandatory_header_commands方法,但是我们如何确保当失败时它首先转到test_commandbefore 真正引发错误?因此,我们目前没有进一步遵循这种方法。

我们认为我们最初将旧的testcommand分成 2 个部分的方法是正确的,但我们已经为此折腾了好几天才能让它发挥作用。所以我们最终在这里寻求帮助。

--> 有没有人知道我们如何确保在我们的验证器发现它不是<testheader>命令之后,它会<testbody>在引发错误之前继续检查命令?

注意:实现是在 python 2.7 中使用 pyparsing 2.3.0 完成的

内梅利斯

我的一位同事找到了一个可行的解决方案。

他还将块分成包括全部和仅测试命令部分的部分,并用仅测试命令块替换了 if 语句中的块部分。他还添加了一些额外的解析操作:

    test_command      = NotAny(conditional_construct | eot) - (valid_header_command | valid_test_command | anyotherline)
    no_header_command = NotAny(conditional_construct | eot) - (valid_test_command | anyotherline)

    block = Forward()
    no_header_block = Forward()

    if_statement = (Keyword('if') - vp_expression - eol).addParseAction(self._parseaction_in_if_statement)
    then_block = ZeroOrMore(no_header_block)
    elif_block = Keyword('elif') - vp_expression - eol - ZeroOrMore(no_header_block)
    else_block = Keyword('else') - eol - ZeroOrMore(no_header_block)
    fi_statement = (Keyword('fi') - eol).addParseAction(self._parseaction_out_if_statement)

    conditional_block = if_statement - then_block - ZeroOrMore(elif_block) - Optional(else_block) - fi_statement
    block << ( OneOrMore(test_command) | conditional_block ) # pylint: disable=expression-not-assigned
    no_header_block << ( OneOrMore(no_header_command) | conditional_block ) # pylint: disable=expression-not-assigned

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章