Pyparsing:将检查分成 2 个单独的部分时出现问题,如果第一部分的检查失败,它应该继续检查第二部分
Pyparsing: Problem splitting up a check in 2 seperate parts, where it should continue the check with the 2nd part if the check on the first part fails
我们为我们的客户定义了一种domain-specific(自动)测试(脚本)语言来简化测试用例的设置。
ASC-file 中的每个测试都由三部分组成:
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
在 <testheader>
和 <testbody>
中都允许使用白线(以提高测试用例的可读性)
为了检查这些 ASC-file 中的定义是否正确,我们制作了一个 validator-script,它按以下方式检查测试:
# 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)
这在很大程度上是有效的,但是我们看到当有人在 <testheader>
命令周围放置 if
以防止不得不编写 2 个测试用例时,会发生一些奇怪的行为,这两个测试用例仅在header.
经过长时间的考虑,我们决定不允许在 <testheader>
命令周围使用 if
,因为只有 <testheader>
不同的情况非常罕见。
所以现在我们要更改实现,使其不再允许 if
围绕 <testheader>
的语句。为此,我们想尝试一种类似于 testcase
的方法,其中在 [=13= 的其余部分之前使用 test_definition
(定义 test
关键字)的单独检查] 和 <testbody>
被选中。
(注意:我们必须保持向后兼容,因为 header-sections 周围的 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
部分不匹配,我们希望它在 header_command
中失败时继续 test_command
部分] 部分。
再次注意:header- 和 body-sections 都允许使用空格,因此我们不能将它们用作分隔符。而且我们必须保持向后兼容,因此 difficult/impossible 引入任何其他分隔符。
我们也尝试保留原始代码,但在 valid_header_command
部分添加检查,但这不起作用,因为尽管 conditional_block
定义是 [=38= 的一部分] 它还包含 block
,因此只有当 if
语句的部分已经处理时,它才会处理剩余的 test_command
部分,其中包含 valid_header_command
中的检查。所以处理它已经太晚了。
最后:我们考虑过更改 _parseaction_validate_mandatory_header_commands
方法,但我们如何才能确保在失败时它首先进入 test_command
,然后再真正引发错误?
因此,我们目前没有进一步遵循该方法。
我们认为我们最初将旧 testcommand
分成两部分的方法是正确的,但我们已经为此绞尽脑汁好几天才能让它发挥作用。所以我们最终在这里寻求帮助。
--> 有没有人知道我们如何确保在我们的验证器发现它不是 <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
我们为我们的客户定义了一种domain-specific(自动)测试(脚本)语言来简化测试用例的设置。
ASC-file 中的每个测试都由三部分组成:
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
在 <testheader>
和 <testbody>
中都允许使用白线(以提高测试用例的可读性)
为了检查这些 ASC-file 中的定义是否正确,我们制作了一个 validator-script,它按以下方式检查测试:
# 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)
这在很大程度上是有效的,但是我们看到当有人在 <testheader>
命令周围放置 if
以防止不得不编写 2 个测试用例时,会发生一些奇怪的行为,这两个测试用例仅在header.
经过长时间的考虑,我们决定不允许在 <testheader>
命令周围使用 if
,因为只有 <testheader>
不同的情况非常罕见。
所以现在我们要更改实现,使其不再允许 if
围绕 <testheader>
的语句。为此,我们想尝试一种类似于 testcase
的方法,其中在 [=13= 的其余部分之前使用 test_definition
(定义 test
关键字)的单独检查] 和 <testbody>
被选中。
(注意:我们必须保持向后兼容,因为 header-sections 周围的 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
部分不匹配,我们希望它在header_command
中失败时继续test_command
部分] 部分。再次注意:header- 和 body-sections 都允许使用空格,因此我们不能将它们用作分隔符。而且我们必须保持向后兼容,因此 difficult/impossible 引入任何其他分隔符。
我们也尝试保留原始代码,但在
valid_header_command
部分添加检查,但这不起作用,因为尽管conditional_block
定义是 [=38= 的一部分] 它还包含block
,因此只有当if
语句的部分已经处理时,它才会处理剩余的test_command
部分,其中包含valid_header_command
中的检查。所以处理它已经太晚了。最后:我们考虑过更改
_parseaction_validate_mandatory_header_commands
方法,但我们如何才能确保在失败时它首先进入test_command
,然后再真正引发错误? 因此,我们目前没有进一步遵循该方法。
我们认为我们最初将旧 testcommand
分成两部分的方法是正确的,但我们已经为此绞尽脑汁好几天才能让它发挥作用。所以我们最终在这里寻求帮助。
--> 有没有人知道我们如何确保在我们的验证器发现它不是 <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