Robot Framework 7.1 RC2 发布详解:Listener 与 VAR 语法增强实战指南
Robot Framework 7.1 RC2 发布详解:Listener 与 VAR 语法增强实战指南
Robot Framework 7.1 是继 7.0 之后的功能版本,重点增强了 Listener 监听器接口与 VAR 变量语法。本文基于官方发布说明 rf-7.1rc2.rst,结合仓库源码深度解析 7.1 RC2 的全部新特性、向后不兼容变更与修复清单,帮助你在升级前充分评估影响、升级后快速上手新 API。
版本概览与安装方式
Robot Framework 7.1 RC2 发布于 2024 年 9 月 3 日(星期二),是 7.1 的第二个发布候选版本,包含所有计划中的功能与变更。相对于第一个 RC(rf-7.1rc1),本版本主要变更集中在:
- 控制台结果文件路径输出为超链接(issue [#5189])
- 允许 Listener 修改 WHILE 循环限制(issue [#5194])
官方邀请社区成员在各自环境中充分测试,以便在 9 月 9 日(星期一)正式版发布前修复可能的回归问题。如发现问题,可通过 #devel 频道反馈到 Robot Framework Slack,或在 issue tracker 提交缺陷。
安装命令
已安装 pip 的情况下,安装最新可用版本:
pip install --pre --upgrade robotframework
或精确安装此版本:
pip install robotframework==7.1rc2
也可以从 PyPI 下载包后手动安装。更完整的安装方式参见 INSTALL.rst。
最重要的增强:Listener 接口
Listener 接口在 Robot Framework 7.0 中被大幅增强,7.1 在此基础上又增加了四项重要能力。
1. 用 ROBOT_LISTENER_PRIORITY 控制监听器调用顺序
监听器现在可以通过 ROBOT_LISTENER_PRIORITY 属性控制调用顺序(issue [#3473])。
从源码实现看,优先级解析发生在 Listener 门面(Facade)初始化阶段。在 src/robot/output/listeners.py 中:
self.priority = self._get_priority(listener)
def _get_priority(self, listener):
priority = getattr(listener, 'ROBOT_LISTENER_PRIORITY', 0)
try:
return float(priority)
except (ValueError, TypeError):
raise DataError(f"Invalid listener priority '{priority}'.")
要点:
- 未设置该属性时默认优先级为
0; - 优先级会被转换为浮点数,数值越小越先被调用;
- 若设置为非法值(无法转为 float),会抛出
DataError,并提示Invalid listener priority。
示例(监听器类中声明):
class MyListener:
ROBOT_LISTENER_PRIORITY = -10 # 比其他默认监听器更早执行
def start_suite(self, data, result):
print("early suite start")
2. 监听器可以改变执行状态
监听器现在可以修改执行状态(issue [#5090])。例如,将关键字状态从 PASS 改为 FAIL:
- 旧行为:只影响该关键字本身,执行继续;
- 新行为:执行停止,当前测试或任务被标记为失败。
这使得监听器能够实现更精细的执行控制,例如在特定条件下强制让用例失败并终止后续步骤。
3. Listener API 版本 3 补齐导入相关方法
Listener API 版本 3 新增了库、资源文件和变量文件导入相关方法(issue [#5008]),至此 V3 拥有与 V2 完全一致的方法集。
从 src/robot/output/listeners.py 可以看出 V3 门面采用了「回退」机制——例如 start_keyword 会回退到 start_body_item,WHILE 相关方法也会回退到 body item 方法:
class ListenerV3Facade(ListenerFacade):
def __init__(self, listener, name, log_level, library=None):
...
start_body_item = get('start_body_item')
end_body_item = get('end_body_item')
self.start_keyword = get('start_keyword', start_body_item)
self.end_keyword = get('end_keyword', end_body_item)
# WHILE
self.start_while = get('start_while', start_body_item)
self.end_while = get('end_while', end_body_item)
self.start_while_iteration = get('start_while_iteration', start_body_item)
self.end_while_iteration = get('end_while_iteration', end_body_item)
注意:V3 门面同时支持蛇形命名(start_suite)与驼峰命名(startSuite)两种方法名,见 _get_method_names 实现。
4. 监听器可修改 WHILE 循环限制
监听器现在可以修改 WHILE 循环的限制值(issue [#5194],由社区贡献者 Adrian Błasiak 实现)。在 V3 监听器的 start_while 中修改 result.limit 即可影响循环的执行次数。
VAR 语法增强
VAR 语法在 7.0 中引入,用于以统一语法在不同作用域创建变量,7.1 对其进行了两处关键增强。
1. 创建变量时记录变量值
VAR 创建的变量值现在会被记录到日志,与 Set Test Variable、Set Suite Variable 等关键字的行为一致(issue [#5077])。
从 src/robot/running/model.py 的 Var.run 实现可以看到:
def run(self, result, context, run=True, templated=False):
result = result.body.create_var(self.name, self.value, self.scope, self.separator)
with StatusReporter(self, result, context, run):
if self.error and run:
raise DataError(self.error, syntax=True)
if not run or context.dry_run:
return
scope, config = self._get_scope(context.variables)
set_variable = getattr(context.variables, f'set_{scope}')
try:
name, value = self._resolve_name_and_value(context.variables)
set_variable(name, value, **config)
context.info(format_assign_message(name, value))
except DataError as err:
raise VariableError(f"Setting variable '{self.name}' failed: {err}")
其中 format_assign_message(name, value) 正是生成日志消息的关键调用。
安全注意(向后不兼容):由于变量值会被记录,可能泄露机密信息。若担心这一点,可用 --max-assign-length 命令行选项禁用/限制所有变量赋值的日志输出。
2. 新增 SUITES 作用域
新增 SUITES 作用域,用于将变量设置到当前套件及其所有子套件(issue [#5060])。此前 SUITE 作用域只影响当前套件而不影响其子套件。此增强使 VAR 语法在功能上与 Set Suite Variable 关键字兼容(后者以略有不同的语法支持相同功能)。
源码中作用域解析逻辑位于 src/robot/running/model.py:
def _get_scope(self, variables):
if not self.scope:
return 'local', {}
try:
scope = variables.replace_string(self.scope)
if scope.upper() == 'TASK':
return 'test', {}
if scope.upper() == 'SUITES':
return 'suite', {'children': True}
if scope.upper() in ('LOCAL', 'TEST', 'SUITE', 'GLOBAL'):
return scope.lower(), {}
raise DataError(...)
SUITES 被映射为 set_suite(..., children=True),即向当前及所有子套件设置变量。语法层面的合法取值定义在 src/robot/parsing/model/statements.py:
class Var(Statement):
type = Token.VAR
options = {
'scope': ('LOCAL', 'TEST', 'TASK', 'SUITE', 'SUITES', 'GLOBAL'),
'separator': None
}
VAR 的完整合法作用域为:LOCAL、TEST、TASK、SUITE、SUITES、GLOBAL。
使用示例:
*** Test Cases ***
Example
VAR ${name} value scope=SUITES
# 当前套件及其全部子套件均可见 ${name}
其他增强
控制台超链接
执行完成后写入控制台的日志与报告路径现在是超链接,便于直接在浏览器中打开(issue [#5189])。这要求终端支持超链接:
- 几乎所有 Linux 和 macOS 终端均支持;
- 经典的 Windows Console 不支持,但较新的 Windows Terminal 以及 Windows 上大多数第三方终端兼容。
底层实现采用终端 OSC 8 超链接语法,见 src/robot/output/console/highlighting.py:
def link(self, path):
if not self._links:
return path
try:
uri = path.as_uri()
except ValueError:
return path
# Terminal hyperlink syntax is documented here:
# https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda
return f'\033]8;;{uri}\033\\{path}\033]8;;\033\\'
控制台链接行为可通过命令行选项控制,robot 与 rebot 均支持:
--consolelinks auto|off Control making paths to results files hyperlinks.
该选项在 src/robot/run.py 与 src/robot/rebot.py 中均有声明。
命名参数的程序化 API
7.1 引入了以编程方式使用命名参数的新 API(issue [#5143]),面向在测试执行前或执行中修改测试/任务的 pre-run modifiers 与 listeners。7.0 曾尝试添加类似 API(issue [#5000]),但因引发向后不兼容问题而在 7.1 中回退(issue [#5031]),新 API 希望规避这些问题。
从源码看,该 API 以类型别名与运行模型字段的形式落地。在 src/robot/api/interfaces.py 中:
NamedArgs = Mapping[str, Any]
运行模型中的 Keyword 支持 named_args 字段,src/robot/running/model.py:
"""When creating keywords programmatically, it is possible to set :attr:`named_args`"""
__slots__ = ['named_args', 'lineno']
...
def __init__(self, ..., named_args: 'Mapping[str, Any]|None' = None, ...):
self.named_args = named_args
...
if self.named_args is not None:
data['named_args'] = self.named_args
同时关键字运行器在解析参数时会合并命名参数(如 src/robot/running/librarykeywordrunner.py):
if data.named_args:
args += tuple(f'{n}={v}' for n, v in data.named_args.items())
翻译与兼容性更新
- 新增韩语翻译(issue [#5187],由 Hyeonho Kang 贡献);
- 更新荷兰语翻译(issue [#5148],由 J. Foederer 贡献),部分旧术语不再生效;
- 官方兼容 Python 3.13(issue [#5091])。官方声明无需修改代码,因此较旧版本的 Robot Framework 也应能正常工作。
向后不兼容变更
升级到 7.1 前请特别关注以下三点:
-
变量值日志可能泄露机密信息(issue [#5077]):
VAR语法现在会记录变量值。若担心泄露,用--max-assign-length禁用或限制变量赋值日志。 -
荷兰语翻译更新(issue [#5148]):部分旧术语不再生效。如有问题,可创建包含旧变体的自定义语言文件;若影响面较大,官方也可能考虑调整本地化系统,让旧术语仍然可用但产生弃用警告。
-
BDD 前缀行为变更(issue [#4577]):若关键字名形如
${kind} example,并以Given good example方式使用,变量${kind}现在只包含good,而以前包含Given good。这通常是一种良性增强,但依赖此前包含前缀的既有代码可能需要更新。
实现上,该变更由 J. Foederer 贡献——当关键字以嵌入参数开头时,不再把 BDD 前缀计入参数值。
完整修复与增强列表(共 25 个 issue)
下表完整列出 7.1 RC2 里程碑中的全部 25 个 issue,按优先级排序:
| ID | 类型 | 优先级 | 摘要 | 加入版本 |
|---|---|---|---|---|
| #3473 | enhancement | critical | 用 ROBOT_LISTENER_PRIORITY 属性控制监听器调用顺序 |
rc 1 |
| #5090 | enhancement | critical | 允许监听器改变执行状态 | rc 1 |
| #5091 | enhancement | critical | Python 3.13 兼容 | rc 1 |
| #5094 | bug | high | 关键字接受 **named 时,含 = 的位置限定参数被误认为命名参数 |
rc 1 |
| #5181 | bug | high | 含可变值的变量在部分场景下解析错误 | rc 1 |
| #5008 | enhancement | high | 为 listener 版本 3 增加库、资源文件与变量文件导入相关方法 | rc 1 |
| #5060 | enhancement | high | VAR 语法支持 scope=SUITES 为子套件设置值 |
rc 1 |
| #5077 | enhancement | high | VAR 语法不像 Set * Variable 那样记录变量值 |
rc 1 |
| #5143 | enhancement | high | 以编程方式使用命名参数的新 API | rc 1 |
| #5187 | enhancement | high | 韩语翻译 | rc 1 |
| #5189 | enhancement | high | 终端中结果文件路径显示为超链接 | rc 1 |
| #5010 | bug | medium | 设置 PYTHONWARNDEFAULTENCODING 产生警告 |
rc 1 |
| #5151 | bug | medium | Evaluate 关键字未考虑添加到 builtins 模块的属性 |
rc 1 |
| #5159 | bug | medium | 使用 Rebot 处理不存在的 JSON 输出文件时错误信息不佳 | rc 1 |
| #5177 | bug | medium | 舍入错误导致状态色条显示异常 | rc 1 |
| #3418 | enhancement | medium | Import Resource 应在 dry-run 中执行 |
rc 1 |
| #4577 | enhancement | medium | 若 BDD 关键字以嵌入参数开头,从参数值中剥离前缀 | rc 1 |
| #4821 | enhancement | medium | Format String:允许使用含 = 的模板字符串而无需转义 |
rc 1 |
| #5038 | enhancement | medium | Dialogs:Get Selection From User 支持默认选项 |
rc 1 |
| #5054 | enhancement | medium | Should Contain 更好地支持 bytes |
rc 1 |
| #5087 | enhancement | medium | 将 output.xml 的生成时间添加到 Result 对象 |
rc 1 |
| #5135 | enhancement | medium | 支持含 week 值的时间字符串 |
rc 1 |
| #5148 | enhancement | medium | 更新荷兰语翻译 | rc 1 |
| #5194 | enhancement | medium | 允许在 listener V3 中修改 WHILE 限制 | rc 2 |
| #5169 | bug | low | 匹配含嵌入参数的关键字时空格未规范化 | rc 1 |
其中 #5194(WHILE 限制修改)是第二个 RC 新增的条目,其余均在第一版 RC 中已包含。
与 7.1 RC1 的差异
相比第一个发布候选(详见 rf-7.1rc1.rst),RC2 的变更集中在两处:
- 控制台超链接(#5189):结果文件路径在支持超链接的终端中可点击;
- WHILE 循环限制可由监听器修改(#5194):在 V3 监听器的
start_while钩子中调整result.limit即可生效。
致谢与后续行动
Robot Framework 开发由 Robot Framework Foundation 及其 60+ 成员组织赞助。本版本亦获得多位社区成员的贡献:
- J. Foederer:增强嵌入参数语法(#4577),更新荷兰语翻译(#5148);
- Hyeonho Kang:韩语翻译(#5187);
- Adrian Błasiak:监听器修改 WHILE 循环限制(#5194);
- @ChristopherJHart:时间字符串支持
week(#5135); - @wendi616:
Import Resource在 dry-run 中执行(#3418); - Peter:
Get Selection From User默认值支持(#5038); - Tatu Aalto:从 output.xml 提取生成时间到
Result对象(#5087); - @droeland:
Should Contain对 bytes 的支持(#5054)。
如果你在测试 7.1 RC2 时发现回归,请在正式版(原定 2024 年 9 月 9 日发布)前通过 issue tracker 反馈,以帮助官方在最终版本中修复问题。相关的自动化测试用例可在 atest 与 utest 目录中查阅,源码实现集中在 src/robot 下的 output、running、parsing 等模块。