Python 匹配装饰器的 EBNF 语法

Python EBNF Syntax to Match a Decorator

我有一个非常大的项目,我需要将一些模块从 bar 移动到 foo.bar,然后更新所有函数调用以反映新位置。

这对函数调用很有效,但我在构建匹配装饰器的模式时遇到问题。

我喜欢匹配的代码是::

  from unittest.mock import patch

  @patch('bar')
  def func(self, patcher):
      ...

我想把上面的补丁改成@patch('foo.bar')

我当前的模式在函数调用时匹配 patch,但在用作装饰器时不匹配::

class FixRenameMockPatch(fixer_base.BaseFix):

    PATTERN = """
    power<
        'patch' args=trailer< '(' [any] ')' >
    >
    """

    def transform(self, node, results):
        ...

我真的很难理解 https://docs.python.org/3.8/reference/grammar.html 上的语法,所以我的修复程序主要基于 lib2to3.

的源代码

如何编写上面的模式来匹配 patch 的所有用途,包括装饰器和上下文管理器?以及完全限定使用的补丁,例如mock.patchunittest.mock.patch?

你可以试试这个

PATTERN = "decorator< '@' 'patch' '(' [any] ')' any* >"

或者

PATTERN = """
    decorator< '@' 'patch' '(' [any] ')' any* >
    | decorator< '@' dotted_name< 'mock' '.' 'patch' > '(' [any] ')' any* >
    """

如果你也想匹配 @mock.patch

a script (written in Python2, Py3 version here) 来解析您的文件并显示解析后的模式,我觉得这很有用。例如,您将 test.py 创建为

from unittest.mock import patch

@patch('bar')
def func(self, patcher):
    print('foo')

然后运行find_pattern.py,回车直到出现你要解析的行,然后输入一些字符(下例是y),回车显示解析结果:

$ python find_pattern.py -f test.py
' unittest.mock'

'from unittest.mock import patch'

'from unittest.mock import patch\n'

"\n@patch('bar')\n"
y
decorator< '@' 'patch' '(' "'bar'" ')' '\n' >