Python EBNF Syntax to Match a Decorator

Tags: , ,

I have a very large project where I need to move some modules from bar to, and subsequently update all functions calls to reflect new location.

This works fine against function calls, but I am having problems building a pattern to match decorators.

The code I like to match is::

  from unittest.mock import patch

  def func(self, patcher):

I would like to turn the patch above to @patch('').

My current pattern matches patch when it is a function call, but not when used as a decorator::

class FixRenameMockPatch(fixer_base.BaseFix):

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

    def transform(self, node, results):

I am really struggling to understand the grammar on so my fixers has largely been based on the source code of lib2to3.

How do I write the pattern above to match all uses of patch, including a decorator and a context manager? And a full qualified use of patch, such as mock.patch or unittest.mock.patch?


You can try this

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

or alternatively

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

if you want to match @mock.patch too.

There is a script (NOTE: written in Python2) to parse your file and shows the parsed pattern, which I find useful. For example, you create as

from unittest.mock import patch

def func(self, patcher):

Then run, hit enter until the line you want to parse is shown, then enter some character (y in the case below), and hit enter to show the parsing result:

$ python -f
' unittest.mock'

'from unittest.mock import patch'

'from unittest.mock import patchn'

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

Source: stackoverflow