Closed The-Compiler closed 6 years ago
well consider yourself lucky,
pytest-retunfailures is basically broken based on the code you posted, so its lucky we can fix it now, same goes for pytest-qt
welcome to #568 - markers stain on things
in addition the example you posted is likely incorrect as well, as its absolutely unclear which mark from which level should take priority
in the code you posted a mark on the class/module level would always be prioritized over a function scope one (and users most likely want it the other way around)
the main reason there is no marks_by name is, that it would also be incomplete and half-wrong - the api was iterate on in the marks pr and thrown out for lack of clarity in details
Note that that example was from @nicoddemus - which reinforces my point pretty well, this is difficult to get right. This is exactly why pytest should solve this problem and not everyone using it.
@The-Compiler the main problem is that we have no clear idea on how to solve this right either
@The-Compiler is absolutely right that we should nail down the documentation before releasing 3.6 (this is one of the points I made during the review).
I'm confused:
in addition the example you posted is likely incorrect as well, as its absolutely unclear which mark from which level should take priority
The "closest" mark is not what we want in this case?
Guys, gentle ping. This is blocking the 3.6 release so we need to reach a consensus soon.
i propose a require_name argument to iter_markers, allowing to filter by name
i can implement this evening or tommorow
We can't update Node.get_marker
to return the closest marker because currently it returns the "merged" markers for backward compatibility, correct?
i propose a require_name argument to iter_markers, allowing to filter by name
(Little bickshedding: I think calling the parameter just name
is sufficient)
If we add this new parameter to iter_markers
, how the example below should change to use the new API?
fake_os = request.node.get_marker('fake_os')
if not fake_os:
return
name = fake_os.args[0]
What if instead we add a new get_closest_marker(name)
function, which returns the closest marker to the node with the given name, or None
if not found? This would provide a more direct replacement of current get_marker(name)
usages.
I agree that a get_closest_marker(name)
is most likely what most code using marks would want, i.e. the "default" case.
On Mon 07 May 2018 at 11:37 +0000, Bruno Oliveira wrote:
Guys, gentle ping. This is blocking the 3.6 release so we need to reach a consensus soon.
I'd be somewhat careful not to try and do this too hurriedly. It's bad to break things for users. And really bad if we break things for indirect users (a user of a plugin really doesn't care, they just want their tests to keep running).
Thanks for the thorough feedback Florian!
On Thu 03 May 2018 at 22:38 -0700, Florian Bruhin wrote:
Continuing the discussion from #3317 (especially https://github.com/pytest-dev/pytest/pull/3317#issuecomment-377490099) here - cc @nicoddemus @RonnyPfannschmidt @flub
Current situation
I'm trying the 3.6.0 release (#3445), here's what I see with
-Wall
:
pytest-rerunfailures
breaksINTERNALERROR> Traceback (most recent call last): INTERNALERROR> [...] INTERNALERROR> File ".../lib/python3.6/site-packages/pytest_rerunfailures.py", line 123, in pytest_runtest_protocol INTERNALERROR> reruns = get_reruns_count(item) INTERNALERROR> File ".../lib/python3.6/site-packages/pytest_rerunfailures.py", line 80, in get_reruns_count INTERNALERROR> if "reruns" in rerun_marker.kwargs: INTERNALERROR> File ".../lib/python3.6/site-packages/_pytest/mark/structures.py", line 20, in warned INTERNALERROR> warnings.warn(warning, stacklevel=2) INTERNALERROR> _pytest.deprecated.RemovedInPytest4Warning: MarkInfo objects are deprecated as they contain the merged marks. INTERNALERROR> Please use node.iter_markers to iterate over markers correctly
We crash plugins because we want to emit a warning? Is this the intention? This seems not great to me.
With that kind of ugly construct, I can guarantee you: People will make mistakes (or plugins will handle it differently) and you'll end up with more issues regarding marker handling than before. Suddenly, only some markers will smear, others will have other funny issues (what if I forget the
else:
for thefor
), and so on.
You're right. We should aim for a direct replacement for get_marker.
We want the default case to be that only the closest marker applies, right? So get an equivalent API which does that, and document that's the replacement for the "default case" where you don't need to worry about those issues at all.
+1
We crash plugins because we want to emit a warning? Is this the intention? This seems not great to me.
Only for testsuites using -Werror
(or --pythonwarnings error
) - not -Wall
like I said above, sorry. Still, IMHO it's a good idea to turn all warnings into errors in testsuites, so you actually notice deprecation issues. My main point is that we should have a proper replacement in the same release we deprecate the old way.
On Mon 07 May 2018 at 11:50 +0000, Bruno Oliveira wrote:
We can't update
Node.get_marker
to return the closest marker because currently it returns the "merged" markers for backward compatibility, correct?i propose a require_name argument to iter_markers, allowing to filter by name
(Little bickshedding: I think calling the parameter just
name
is sufficient)If we add this new parameter to
iter_markers
, how the example below should change to use the new API?fake_os = request.node.get_marker('fake_os') if not fake_os: return name = fake_os.args[0]
What if instead we add a new
get_closest_marker(name)
function, which returns the closest marker to the node with the given name, orNone
if not found? This would provide a more direct replacement of currentget_marker(name)
usages.
What would happen if we just deprecate having multiple markers of the same name which end up on one node at all? Because everything that's being discussed here so far does not address the part of the feeback that no two plugins will ever handle this situation in the same way. It rightfully is the task of the API pytest provides to do this.
If we do that then maybe even keeping .get_marker() is a sane option? How different are the new MarkInfo objects?
What would happen if we just deprecate having multiple markers of the same name which end up on one node at all?
IIUC that's what was done, Node.get_marker
returns the "merged" markers and is now deprecated.
If we do that then maybe even keeping .get_marker() is a sane option? How different are the new MarkInfo objects?
I think @RonnyPfannschmidt knows that from the top of his head and can answer more easily. š
I'd be somewhat careful not to try and do this too hurriedly. It's bad to break things for users. And really bad if we break things for indirect users (a user of a plugin really doesn't care, they just want their tests to keep running).
Oh definitely. Sorry if it came across that way, I just meant to point out that this is a release blocker so we can't postpone it indefinitely. But you are right to emphasize that we should strive for a good solution, and not just hush for something to get the release out. š
@flub we cant deprecate having multiple marks with the same name, it would break skip, skip_if and xfail_usages that happen in practice
i consider the perception that one "typically" wants the closes mark a false one - for example at a work project - i believe actually all custom marks (about a dozen ones for different purposes) have requirements for correctly working with individual markings, considering them in order
i consider the perception that one "typically" wants the closes mark a false one
I see. Just to share, I reviewed all custom marks we use at work and they all actually are meant to work as the "closest" one. I think the same can be said about all the markers @The-Compiler brought up in his original post, so I guess the mileage varies according to the codebase.
I propose then:
get_closest_mark(name)
function: its semantics are obvious and well defined I believe.get_closest_mark(name)
and find_marks()
as possible alternatives to get_marker
.get_marker()
.What do you guys think?
So maybe it helps enumerating the strategies for handling marks. I'm doing this partially to expose my naivety about this again, but also because if we're finding this so hard then maybe we should spell these various things out very clearly in the documentation complete with sensible examples of how these marking strategies work. Anyway, here my attempt:
Marks compose additive. E.g. skipif(condiion) marks means you just want to evaluate all of them, order doesn't even matter. You probably want to think of your marks as a set here.
Marks overwrite each other. This is the get_closest_mark case, so order matters but you only want to think of your mark as a single item. E.g. log_level('info') at a module level can be overwritten to log_level('debug') for a specific test.
Marks compose, but order matters. I'm struggling to think of an example here. Any ideas?
Marks do not compose and should never appear on more then one level. Not sure there's a sane example here either.
So I think this really should be added to the documentation of marks and then can be referred too in the news for the release as well as justification.
Also this maybe also suggests what the APIs should be like: get_closes_mark() which returns a single mark and a get_all_marks() which returns a set.
What do you all think? Am I completely off the mark or is this useful?
@flub thanks, enumerating all cases is very helpful.
In my mind there were only two use cases, 1) and 2). I also can't think of examples for 3) and 4).
What do you all think? Am I completely off the mark or is this useful?
Please tell me that pun was intentional. :laughing:
I agree there are probably those cases. Some thoughts about each:
Case 1 and 3 are basically the same, just ordered vs. unordered, right? I think the current iter_markers
can easily cover both, as even with 1. you probably want to iterate over them to somehow process them anyways. So even if we can't think of an exact example for 3., I do think they exist (similar to what Ronny said), and it's trivial to support when we support 1. If someone wants a set, assuming that marks are hashable, you can always do set(node.iter_markers())
, right?
Case 2 is what I think is the most common case - I'm not just speaking for qutebrowser here, I've seen many pytest testsuites and plugins by now. We should support it in some straightforward way, and I agree a Node.get_closest_marker(name)
would be that.
Case 4 might be useful, but I'm not sure - @RonnyPfannschmidt do you have a more concrete example? Maybe it's useful when I just never want any marker ambiguity in my testsuite (because this can be hard to reason about)? I'm not sure how the API would look, though. I first thought about a node.get_closest_marker(name, strict=True)
or whatever, but that'd mean e.g. plugins would have to decide. Not sure if it's worth the trouble really - and if it is, I think that's one we could postpone until after the release.
whops, the one about 4 was a typo - i meant 3, sorry for the confusion
@The-Compiler what would strict
mean
i propose 2 steps
a) updating iter_markers
to def iter_markers(name=None)
- where if given, a filter for the name attribute is added
b) adding def get_closest_marker(name, default=None)
which is short for next(iter_markers(name), default)
@RonnyPfannschmidt that sounds great to me. š
my initial implementation has some bugs, will reiterate
almost complete
I think this can now be closed right?
Continuing the discussion from #3317 (especially https://github.com/pytest-dev/pytest/pull/3317#issuecomment-377490099) here - cc @nicoddemus @RonnyPfannschmidt @flub
Current situation
I'm trying the 3.6.0 release (#3445), here's what I see with
-Wall
:pytest-rerunfailures
breakspytest-qt
breakslocal things in my testsuite break
Problems
Insufficient documentation
So, the deprecation warning tells me to use
node.iter_markers
. Looking at the docs, all I can see is "iterate over all markers of the node" and forget_marker(name)
a "warning: deprecated" (with wrong ReST-syntax, too).If I'm not aware of the problem of marker smearing (and I wasn't really, I've never done strange subclassing stuff in tests), I'm quite lost there. If it wasn't for @nicoddemus' example I would be pretty lost here, and write something based on guesswork.
Bad abstraction
The suggested alternative to
Is supposedly:
With that kind of ugly construct, I can guarantee you: People will make mistakes (or plugins will handle it differently) and you'll end up with more issues regarding marker handling than before. Suddenly, only some markers will smear, others will have other funny issues (what if I forget the
else:
for thefor
), and so on.We want the default case to be that only the closest marker applies, right? So get an equivalent API which does that, and document that's the replacement for the "default case" where you don't need to worry about those issues at all.
Conclusion
I feel like this should block a 3.6.0 release. If we release something with deprecation warnings, no replacement, and bad documentation, then plugins and testsuites which want to preserve backwards compatibility will end up with three ways of getting a marker, each probably different in another subtle way.