Open RonnyPfannschmidt opened 5 years ago
Hi @RonnyPfannschmidt,
Thanks for the write up.
A few questions:
1) It is not clear to me if you are proposing pytest_make_marker
and @pytest.markspec
as alternatives, or proposing to have both at the same time. Can you clarify that?
2) Regarding xfail
symbol, does that pytest.markspec.symbol
declaration means users will use it as @pytest.mark.xfail
or @pytest.mark(xfail)
?
I definitely agree that object-based markers as in:
from pytest_timeout.marks import timeout
@pytest.mark(timeout(10))
def test():
...
Have the clear advantage of providing a consolidated namespace and clear signature that can easily be looked-up and validated by linters, but I worry that now we have two very distinct ways of doing the same thing, and this duality might be confusing/seem complicated at first. Any thoughts on that?
Another concern is that @pytest.mark(timeout(10))
and @pytest.mark.timeout(10)
are too close to each other, again possibly being a cause for confusion. Perhaps we should define a different way to apply class-based marks than reusing @pytest.mark
?
Btw should we send an e-mail to the mailing list so others can join this discussion? Many people there don't follow GitHub discussions closely.
markspec
would be implemented in terms of make_marker
pytest.mark.foo
instead of importing the symbolThanks for the reply.
Question 2) answered, thanks.
Back to 1), still not clear to me why would we need the pytest_make_marker
hook at all.
Without understanding the need for the hook, I believe this could work:
@pytest.markspec
def timeout(value, *, method=None):
return TimeoutMark(value=value, method=method)
# used as
from pytest_timeout.mark import timeout
@pytest.mark(timeout(10))
def test_foo():
pass
And to keep the previous @pytest.mark.foo
syntax to work, we could add an option to markspec
to auto-register it:
@pytest.markspec(register=True)
def skip_if(condition, *, reason=None):
return ConditionalSkip(condition, reason=reason)
# used as
@pytest.mark.skipif('sys.platform == "win32"')
def test_foo():
pass
?
Also when you have the time, would like to know if you have the same concerns that I wrote at the bottom of my post. 👍
markspecs always register else a function would do
the concern is kinda valid, but practically, if you use markspecs, you use pytest.mark.foo
if you use plain classes, you use pytest.mark(SomethingFun(....))
fun will happen when we mix things like
@markspec
def skipif(condition: MarkCondition, reaon: Option[str] =None):
...
@markspec
def skipif(bug: IssueIdentifier):
...
I see, this clarifies things a bit, but I still don't know what's the use for the pytest_make_marker
hook then. 🤔
@nicoddemus i was thinking of disambiguating markers for example, but it seems it currently lacks the parameters for that, so that hook is probably a bad idea
Thanks.
I would like to note that we can introduce markspec
are independent from class-based markers, they don't need to get implemented at the same time (at least seems to me that should be possible).
Another thought:
@pytest.markspec
def skipif(condition, reason=None) -> Option[ConditionalSkip]:
return ConditionalSkip(condition, reason)
With the above declaration, it means that node.get_closest_marker('skipif')
will now return a ConditionalSkip
object rather than a Mark
. This might be a problem if someone access mark.args
or mark.kwargs
unconditionally:
for m in node.iter_markers():
print(m.args)
But I think that's rare and should not really be a roadblock.
actually, we should return Marks there, the api to get objects should be different
Hmm yet another API? Wouldn't that bring even more confusion users? 🤔
its a different thing with a different behaviour, its absolutely unacceptable to dump it into a old api
note that we dont need a new function name,
iter_markers("name")
-> Mark objects
iter_markers(named_by="name")
-> objects that got returned from registred markers
iter_markers(instance_of=type)
-> objects that are instances of the named types
named_by
and instance_of
may be combined in a call and should compound in effect/selectiveness
this is a rough sketch of alternative to #5418 and builds upon marks - proposals for a new api and a path forward
as a starting point we should elevate actual markers from the need to register a name with us, they should use the solid python name-spacing systems using modules and type names
so we would switch the marker implementation from something like
Mark("skipif", (), {"condition": ..., "reason": ...})
toConditionalSkip(condition=..., reason=...)
after freeing the actual marker registration up, we can then follow up with registering the names of marker constructors something like the following in a
conftest.py
/pluginlookup should happen in terms of types for the new style markers the name based lookup need to be expanded in terms of looking up the types that natch a name