pythonunit-testingmockingdecoratormonkeypatching

Can I patch a Python decorator before it wraps a function?


I have a function with a decorator that I'm trying test with the help of the Python Mock library. I'd like to use mock.patch to replace the real decorator with a mock 'bypass' decorator which just calls the function.

What I can't figure out is how to apply the patch before the real decorator wraps the function. I've tried a few different variations on the patch target and reordering the patch and import statements, but without success. Any ideas?


Solution

  • Decorators are applied at function definition time. For most functions, this is when the module is loaded. (Functions that are defined in other functions have the decorator applied each time the enclosing function is called.)

    So if you want to monkey-patch a decorator, what you need to do is:

    1. Import the module that contains it
    2. Define the mock decorator function
    3. Set e.g. module.decorator = mymockdecorator
    4. Import the module(s) that use the decorator, or use it in your own module

    If the module that contains the decorator also contains functions that use it, those are already decorated by the time you can see them, and you're probably S.O.L.

    Edit to reflect changes to Python since I originally wrote this: If the decorator uses functools.wraps() and the version of Python is new enough, you may be able to dig out the original function using the __wrapped__ attribute and re-decorate it, but this is by no means guaranteed, and the decorator you want to replace also may not be the only decorator applied.