E.g. patch("module.function") -> patch(module.function)

A lot of people use IDEs like PyCharm. My proposed feature will make Python work better with IDEs.

This solves 2 problems (that I know of):

  1. Writing wrong module name in string.
    Currently it does not show an error while the proposed feature does.

  2. Refactoring:
    When using IDE built in refactoring tools it’ll not refactor strings. With the current solution you’ll need to manually edit those strings while in the proposed solution you don’t. This saves a lot of time and prevents bugs.
    @patch(module.function) will be refactored while @patch("module.function") will not.

1 Like

Hi Lukas, and welcome! Marry Christmas, happy holidays, and best wishes for the new year!

Thank you for giving an example, but unfortunately, you will have to tell us what your proposed feature actually is, we can’t read your mind. Please explain in words what your change is, and how it will work.

What is @patch? Is it part of your proposal? Is your proposal specific to just patch, or all decorators, or all strings, or what?

OP refers to unittest.mock.patch


  1. Regarding showing error:
    a) Using unittest.mock.patch.object will get you most of the way there, only missing the final attribute

    b) Your unit-tests will fall anyway (unless you pass create=True

  2. Upvote the functionality to refactor on your IDE, eg for PyCharm: https://youtrack.jetbrains.com/issue/PY-7921/Make-refactoring-aware-of-mock-library


Your proposal doesn’t work for re-exported members (eg collections.abc, os.path), as their export details are stored anywhere

1 Like

Thank you for the welcome :slight_smile: . I wish you the best too!

I made a video. Hope this makes it clear.

@patch is from the unittest.mock module as Laurie mentioned.

  1. if @patch.object already exists, why not go all the way and make it an option by default for @patch?

  2. That would fix one part, the type hints and error hints won’t be there.

Sorry, I’m not sure what you mean by you latest sentence.

I’m new to this but if you approve, I can make a PR for this and video showcasing the specific feature.

I asked:

And you replied:

Okay, that’s my fault. When I said “words”, I meant written words, not a video.

I tried to watch your video, I really did. It is impossible for me to read the text in on the screen, the text is too small (shrunk to about 4pt high) and the contrast is too low (looks like grey text on a dark grey background).

In your video, you said “As you can see…” – no, I can’t see. This is what it looks like in my browser:

So I have no clue what you were typing, or what you were doing. My guess is that you are using some sort of IDE but it is impossible for me to see which one.

Let me try to explain Lukas’ proposal.

Currently unittest.mock.patch requires the target argument to be written as a string. Using one of the examples:

@patch('__main__.SomeClass')
def function(normal_argument, mock_class):
    ...

Lukas is proposing that patch() also accept unquoted targets, like @patch(__main__.SomeClass).

Is that correct Lukas?

If so, please explain how this can work in the general case.

For example @patch('sys.stdout', new_callable=StringIO). If we remove the quoting, sys.stdout is evaluated and returns (usually) a _io.TextIOWrapper object, which is all that the patch function receives. How is patch supposed to know it is supposed to patch the sys module, just from a TextIOWrapper instance?

In case that problem is not clear, let’s say we want to patch math.pi to be a different value:

from unittest.mock import patch
@patch('math.pi', new_callable=lambda: 3.0)
def func(pi):
    import math
    return 2*math.pi

func()  # returns 6.0

If I write it as @patch(math.pi, ...) the patch function receives the float 3.1415… how does it know which module to patch?

1 Like

Nvm, just realised this was a bad idea. Sorry.

It’s not inherently a bad idea, it’s just not workable in the way you originally suggested for the reasons @steven.daprano mentioned.

However there is another way that your original goals could be achieved. Currently the patch methods first argument is annotated as a plain string. We could introduce a new type hint class to the typing module that inherits from str, but indicates that the string is a Python module/attribute reference?

I.e

from typing import Reference

def patch(target: Refrerence, …):
   …

This could be used in third party libraries to annotate functions that also accept string references, and it would simplify IDE support for refactoring them as these argument types would be clearly delineated and wouldn’t need to be stored in some global allowlist of “string arguments that are actually not just any string and are Python references instead”.

This would be useful for Django for example, which uses these string reference types heavily in project settings.

2 Likes

This seems related to what I wrote about Language specifications for strings