Behave 的装饰器是如何创建/声明的?

3 投票
2 回答
1252 浏览
提问于 2025-04-18 16:37

我现在正在使用Behave(Python的行为驱动开发工具),并且一直在研究它的源代码,想弄明白@given@when@then这些装饰器是怎么定义的。

我查到的最远的地方是step_registry.py,在这里我发现了一个函数setup_step_decorators(context=None, registry=registry),看起来这个函数就是在做这件事。

不过,我不太明白这些装饰器是怎么创建的,因为在源代码中并没有以def when(...):这样的形式明确声明。我觉得它们是根据一个字符串列表来定义的(for step_type in ('given', 'when', 'then', 'step'):),然后通过调用make_decorator()来处理。

有没有人能帮我看一下代码,解释一下这些装饰器是在哪里以及怎么被声明的

你可以在这里访问Behave的源代码

2 个回答

3

它们是在大约第90行被注入到 globals() 中的(那时 contextglobals(),因为 contextNone):

# -- Create the decorators
def setup_step_decorators(context=None, registry=registry):
    if context is None:
        context = globals()
    for step_type in ('given', 'when', 'then', 'step'):
        step_decorator = registry.make_decorator(step_type)
        context[step_type.title()] = context[step_type] = step_decorator

你也可以自己这样做(globals() 的工作方式就像一个普通的字典):

>>> a
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'a' is not defined
>>> globals()['a'] = 5
>>> a
5
5

好吧,我们从外面开始讲:

if context is None:
    context = globals()
for step_type in ('given', 'when', 'then', 'step'):
    step_decorator = registry.make_decorator(step_type)
    context[step_type.title()] = context[step_type] = step_decorator

我觉得让你困惑的是最后一行。

每个模块的全局命名空间其实就是一个字典。函数 globals() 会返回这个字典。如果你修改这个字典,就会创建新的模块全局变量。例如:

>>> globals()['a'] = 2
>>> a
2

在这种情况下,默认情况下,context = globals()。所以,对于第一个 step_type,你实际上是在做这个:

>>> globals()['given'] = step_decorator

撰写回答