Hi all, this was discussed before on IRC, but here is an update and a question. currently pytest-trunk implements a convenient new monkeypatch.replace method: # example usage monkeypatch.replace("os.path.abspath", lambda x: "/abc") This effectively performs: import os.path monkeypatch.setattr(os.path, "abspath", lambda x: "/abc") However, i just wanted to use it and couldn't remember the "replace" name and don't find it very telling anymore. I often do something like this:: monkeypatch.setattr("os.path.abspath", lambda x: "/abc") which currently ends up as an error. Would it be too awkward to just make the latter work and forget about introducing a new "replace" method name? IMO it's rather intuitive to just make setattr accept the new signature, although the docstring will need a bit of explaining. cheers, holger
On 13.09.2013, at 12:33, holger krekel wrote:
Hi all,
this was discussed before on IRC, but here is an update and a question. currently pytest-trunk implements a convenient new monkeypatch.replace method:
# example usage monkeypatch.replace("os.path.abspath", lambda x: "/abc")
This effectively performs:
import os.path monkeypatch.setattr(os.path, "abspath", lambda x: "/abc")
However, i just wanted to use it and couldn't remember the "replace" name and don't find it very telling anymore. I often do something like this::
monkeypatch.setattr("os.path.abspath", lambda x: "/abc")
which currently ends up as an error. Would it be too awkward to just make the latter work and forget about introducing a new "replace" method name? IMO it's rather intuitive to just make setattr accept the new signature, although the docstring will need a bit of explaining.
You can easily support both, so I would say go for it and just describe both ways in the docstring. If you think the old way shouldn't be used, you can also add a deprecation warning to it. Regards, Florian Schulze
On Fri, Sep 13, 2013 at 12:50 +0200, Florian Schulze wrote:
On 13.09.2013, at 12:33, holger krekel wrote:
Hi all,
this was discussed before on IRC, but here is an update and a question. currently pytest-trunk implements a convenient new monkeypatch.replace method:
# example usage monkeypatch.replace("os.path.abspath", lambda x: "/abc")
This effectively performs:
import os.path monkeypatch.setattr(os.path, "abspath", lambda x: "/abc")
However, i just wanted to use it and couldn't remember the "replace" name and don't find it very telling anymore. I often do something like this::
monkeypatch.setattr("os.path.abspath", lambda x: "/abc")
which currently ends up as an error. Would it be too awkward to just make the latter work and forget about introducing a new "replace" method name? IMO it's rather intuitive to just make setattr accept the new signature, although the docstring will need a bit of explaining.
You can easily support both, so I would say go for it and just describe both ways in the docstring.
If you think the old way shouldn't be used, you can also add a deprecation warning to it.
forgot to mention: monkeypatch.replace was never released, so we can just remove it. I take it you like the setattr-overload. best, holger
On 2013-09-13 12:33, holger krekel wrote:
Hi all,
this was discussed before on IRC, but here is an update and a question. currently pytest-trunk implements a convenient new monkeypatch.replace method:
# example usage monkeypatch.replace("os.path.abspath", lambda x: "/abc")
This effectively performs:
import os.path monkeypatch.setattr(os.path, "abspath", lambda x: "/abc")
However, i just wanted to use it and couldn't remember the "replace" name and don't find it very telling anymore. I often do something like this::
monkeypatch.setattr("os.path.abspath", lambda x: "/abc")
As long as the old 3 parameter setattr is not going to be away, that seems fine. Depending on the length of the string I would probably still use the 3 parameter version to get the <TAB> completion on the first argument. Unfortunately just writing monkeypatch.setattr(os.path.abspath, lambda x: "/abc") in Python to get the same effect is out of the question AFAIK. Maybe I should try and make an/my editor smart enough to handle strings that are actually object-attribute-hierarchy references and do TAB completion on those.
which currently ends up as an error. Would it be too awkward to just make the latter work and forget about introducing a new "replace" method name? IMO it's rather intuitive to just make setattr accept the new signature, although the docstring will need a bit of explaining.
cheers, holger
_______________________________________________ Pytest-dev mailing list Pytest-dev@python.org https://mail.python.org/mailman/listinfo/pytest-dev
Hi, On 13/09/13 12:33, holger krekel wrote:
monkeypatch.setattr("os.path.abspath", lambda x: "/abc")
speaking about possible APIs, what about the following? monkeypatch("os.path.abspath = foo") I find it much more readable than the two or three argument versions, and it's in a way similar to what we have for py.test.raises("..."). The drawback is that the implementation is probably a bit trickier than what it is now. ciao, Anto
On Tue, Sep 17, 2013 at 10:32 +0200, Antonio Cuni wrote:
On 13/09/13 12:33, holger krekel wrote:
monkeypatch.setattr("os.path.abspath", lambda x: "/abc")
speaking about possible APIs, what about the following?
monkeypatch("os.path.abspath = foo")
I find it much more readable than the two or three argument versions, and it's in a way similar to what we have for py.test.raises("..."). The drawback is that the implementation is probably a bit trickier than what it is now.
Not sure it would even be that useful TBH and would indeed be trickier to implement, i guess. Maybe it's better to rather think about higher level helpers than argue much about the setattr() convenience. Apart from the proposed setattr shortcut, i am also thinking of direct mock (the lib from Michael Foord) support: mock = monkeypatch.setmock("os.path.abspath") call_something_with_triggers_the_mock() assert mock.called_once_with(...) With >=py3.3 this could just work, on older Pythons ``setmock`` would skip the test if "mock" is not importable. However, i dislike parts of the mock API design, in particular that the "mock" object is send to applications but also provides assertions and mock-specific data attributes. If your app has attributes or methods like "method_calls", "called", "call_args" etc. then you can not mock it. Instead mock's API should rather work like this: m = mock.Mock() ... pass m somewhere ... (having virtually no special attributes) assert mock.assert_once_called_with(m, ...) This way "mock.*" and assertions would become a "meta" api, strictly separated from the mock objects that are sent into an application, avoid any conflicts. Practicality might beat purity, though. mock is already widely used ... cheers, holger
On 17/09/13 12:13, holger krekel wrote:
Not sure it would even be that useful TBH and would indeed be trickier to implement, i guess. Maybe it's better to rather think about higher level helpers than argue much about the setattr() convenience.
I think that designing a good API is not only about convenience, but it has a high impact on how easy it is to learn to use the tool. I just though of another variation on the theme: monkeypatch(os.path).abspath = foobar # OR monkey(os.path).abspath = foobar I think this is the easiest to remember, to understand and it's also probably very easy to implement.
Apart from the proposed setattr shortcut, i am also thinking of direct mock (the lib from Michael Foord) support:
mock = monkeypatch.setmock("os.path.abspath")
not sure it's a good idea. I don't think it is much simpler than this: mock = monekypatch.setattr("os.path.abspath", Mock()) apart that in the latter case you must import Mock explicitly, which is a good thing IMHO.
On Tue, Sep 17, 2013 at 14:46 +0200, Antonio Cuni wrote:
On 17/09/13 12:13, holger krekel wrote:
Not sure it would even be that useful TBH and would indeed be trickier to implement, i guess. Maybe it's better to rather think about higher level helpers than argue much about the setattr() convenience.
I think that designing a good API is not only about convenience, but it has a high impact on how easy it is to learn to use the tool.
I just though of another variation on the theme:
monkeypatch(os.path).abspath = foobar # OR monkey(os.path).abspath = foobar
I think this is the easiest to remember, to understand and it's also probably very easy to implement.
not sure it's easy to remember given that several different somewhat popular approaches all don't look like this. But it's a cute trick that indeed wouldn't be hard to implement :)
Apart from the proposed setattr shortcut, i am also thinking of direct mock (the lib from Michael Foord) support:
mock = monkeypatch.setmock("os.path.abspath")
not sure it's a good idea. I don't think it is much simpler than this:
mock = monekypatch.setattr("os.path.abspath", Mock())
apart that in the latter case you must import Mock explicitly, which is a good thing IMHO.
Not sure i agree with "importing is a good thing" :) For fixtures people have come to appreciate, i think, the de-coupling of using values and providing them. Mocking is tightly connected with testing so having a builtin support fixture is reasonable IIRC. Also, with monkeypatch.setattr(..., Mock()) you cannot easily do "autospec" whereas with monkeypatch.setmock(...) you could (And disable with autospec=False). Or maybe a new "mock" and "mock.patch(...)" would be even easier for people knowing mock's API -- except that "mock" would be a fixture provided by pytest or a plugin, and mock.patch would be an imperative call. best, holger
participants (4)
-
Anthon van der Neut -
Antonio Cuni -
Florian Schulze -
holger krekel