Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support keyword arguments in marker expressions #12500

Conversation

lovetheguitar
Copy link
Contributor

@lovetheguitar lovetheguitar commented Jun 20, 2024

Fixes #12281

With this change you can restrict a test run to only run tests matching one or multiple marker
keyword arguments
Only keyword argument matching is supported in marker expressions.
Only int, str, bool & None values are supported in marker expressions.

@lovetheguitar lovetheguitar force-pushed the feat/support_marker_kwarg_in_marker_expressions branch 2 times, most recently from 9451472 to ac18898 Compare June 20, 2024 21:10
@webknjaz
Copy link
Member

@lovetheguitar could you rebase the PR, fill out the PR description/commits, and compose a change note?

@lovetheguitar lovetheguitar force-pushed the feat/support_marker_kwarg_in_marker_expressions branch from e49f06a to c672f9f Compare June 21, 2024 14:31
@psf-chronographer psf-chronographer bot added the bot:chronographer:provided (automation) changelog entry is part of PR label Jun 21, 2024
@lovetheguitar lovetheguitar force-pushed the feat/support_marker_kwarg_in_marker_expressions branch from c672f9f to 7d4f437 Compare June 21, 2024 14:43
@lovetheguitar
Copy link
Contributor Author

@RonnyPfannschmidt Could you please have a look? :)

@lovetheguitar lovetheguitar force-pushed the feat/support_marker_kwarg_in_marker_expressions branch 3 times, most recently from 3e0ebe6 to edbbbaf Compare June 21, 2024 16:06
@lovetheguitar
Copy link
Contributor Author

lovetheguitar commented Jun 21, 2024

@webknjaz https://readthedocs.org/projects/pytest/builds/24773128/ does the preview fail to link that or did I reference it wrongly?

Edit:

The solution was to define a "title" when referencing since at the anchor there was no explicit headline.

:ref:`marker examples <marker_keyword_expression_example>`

Thanks for the tip @webknjaz.

@lovetheguitar lovetheguitar force-pushed the feat/support_marker_kwarg_in_marker_expressions branch from edbbbaf to 11c9435 Compare June 21, 2024 16:21
Copy link
Member

@webknjaz webknjaz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mostly reviewed this promptly. The walrus thing seems to be the most important blocker.

changelog/12281.feature.rst Outdated Show resolved Hide resolved
AUTHORS Show resolved Hide resolved
changelog/12281.feature.rst Outdated Show resolved Hide resolved
doc/en/example/markers.rst Outdated Show resolved Hide resolved
doc/en/how-to/usage.rst Show resolved Hide resolved
doc/en/how-to/usage.rst Outdated Show resolved Hide resolved
src/_pytest/mark/__init__.py Show resolved Hide resolved
src/_pytest/mark/__init__.py Show resolved Hide resolved
@lovetheguitar lovetheguitar force-pushed the feat/support_marker_kwarg_in_marker_expressions branch from 11c9435 to 2b960c3 Compare June 21, 2024 18:36
Copy link
Contributor Author

@lovetheguitar lovetheguitar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be good to go @RonnyPfannschmidt. 🚀 🤞

doc/en/how-to/usage.rst Show resolved Hide resolved
doc/en/how-to/usage.rst Outdated Show resolved Hide resolved
src/_pytest/mark/__init__.py Show resolved Hide resolved
src/_pytest/mark/__init__.py Show resolved Hide resolved
doc/en/example/markers.rst Outdated Show resolved Hide resolved
changelog/12281.feature.rst Outdated Show resolved Hide resolved
changelog/12281.feature.rst Outdated Show resolved Hide resolved
AUTHORS Show resolved Hide resolved
@lovetheguitar lovetheguitar force-pushed the feat/support_marker_kwarg_in_marker_expressions branch from 2b960c3 to ae6c7d4 Compare June 21, 2024 18:50
@lovetheguitar lovetheguitar force-pushed the feat/support_marker_kwarg_in_marker_expressions branch from ae6c7d4 to b1255a9 Compare June 21, 2024 18:51
if not (matches := self.own_mark_name_mapping.get(name, [])):
return False

if not kwargs:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This case cannot happen, I'll fix it when I get back to the computer, but feel free to beat me to it

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was an early return to keep the logic for "non-keyword" markers fast/similar to before.

The case happens every time a "non-keyword" marker is looked up

def __bool__(self) -> bool:
return self.matcher(self.name)

@RonnyPfannschmidt RonnyPfannschmidt merged commit 9f134fc into pytest-dev:main Jun 21, 2024
29 checks passed
@RonnyPfannschmidt
Copy link
Member

awesome - @lovetheguitar thanks for seeing this tough and @webknjaz thanks for sorting out the last details to land this

Copy link
Member

@The-Compiler The-Compiler left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is amazing work, and it's great how many tests there are and corner cases there are handled!

I'm really unhappy how this was merged in a seemingly unfinished state (TODO comments in various places), while we were eating dinner together, 2 hours after the final version was pushed. I found a couple of pretty obvious things from a cursory review, and now I'll either need to do a follow-up PR to pick up the lose ends, or someone else needs to go back to opening a new PR and all that additional work.

Also, @bluetech wrote the parser in the first place if memory serves me right, so I feel like it'd be appropriate to give him a chance to look at this, no?


.. code-block:: pytest

$ pytest -v -m 'device(serial="123")'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Single quotes won't work on Windows, which is why we use double quotes above.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In poweshell they do, but good to know they don’t in cmd.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Changed to ".


.. code-block:: bash

pytest -m slow(phase=1)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs quotes to work.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Added " around the marker expression.

@@ -233,6 +233,54 @@ def test_two():
assert passed_str == expected_passed


@pytest.mark.parametrize(
("expr", "expected_passed"),
[ # TODO: improve/sort out
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leftover TODO?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those were the first tests added, any feedback greatly appreciated. Otherwise I can just drop the todo.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Preliminarily dropped.

("mark(empty_list=[])", r'unexpected character/s "\[\]"'),
),
)
def test_invalid_kwarg_name_or_value( # TODO: move to `test_syntax_errors` ?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leftover TODO?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, sorry, forgot to remove that one, talked about it with Ronny to keep it separate as is.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Removed.

("builtin_matchers_mark(z=1)", False),
),
)
def test_builtin_matchers_keyword_expressions( # TODO: naming when decided
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leftover TODO?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same for that one. 🙃

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Removed.

@lovetheguitar
Copy link
Contributor Author

I think this is amazing work, and it's great how many tests there are and corner cases there are handled!

I'm really unhappy how this was merged in a seemingly unfinished state (TODO comments in various places), while we were eating dinner together, 2 hours after the final version was pushed. I found a couple of pretty obvious things from a cursory review, and now I'll either need to do a follow-up PR to pick up the lose ends, or someone else needs to go back to opening a new PR and all that additional work.

Also, @bluetech wrote the parser in the first place if memory serves me right, so I feel like it'd be appropriate to give him a chance to look at this, no?

@The-Compiler Sure thing, I was also thinking about mentioning @bluetech, since, yes he wrote that part and he commented also in the issue.

Feel free to be nitpicky, I'm motivated to learn the customs properly and do the right thing (TM). 😄

Copy link
Member

@bluetech bluetech left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very nice work, I wouldn't have done it better myself :)

I left some comments.


Now tests can be selected by marker keyword arguments.
Supported values are :class:`int`, (unescaped) :class:`str`, :class:`bool` & :data:`None`.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to add an example here, for people skimming the changelog without clicking through to the link.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had one in there in the beginning. 😅 I think @RonnyPfannschmidt suggested to remove it and link to the docs.

Do I add it back here or leave as is?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lovetheguitar I think, it's nice to have examples in such places, but perhaps only small ones. I like Ran's idea. If you make a PR for that, we could discuss something more concrete there, with dedicated review and polishing.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bluetech I believe the previous comment of @lovetheguitar was addressed to you, eh?

@@ -5,7 +5,8 @@
expression: expr? EOF
expr: and_expr ('or' and_expr)*
and_expr: not_expr ('and' not_expr)*
not_expr: 'not' not_expr | '(' expr ')' | ident
not_expr: 'not' not_expr | '(' expr ')' | ident ( '(' name '=' value ( ', ' name '=' value )* ')')*
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Would remove the space after ident ( for consistency.
  • The * (zero-or-more) at the end of this line should be ? (zero-or-one), I think.
  • The definitions of the name and value productions are missing.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. removed the space
  2. true, changed to ?
  3. added a definition, not sure about the semantics though 😅

@@ -5,7 +5,8 @@
expression: expr? EOF
expr: and_expr ('or' and_expr)*
and_expr: not_expr ('and' not_expr)*
not_expr: 'not' not_expr | '(' expr ')' | ident
not_expr: 'not' not_expr | '(' expr ')' | ident ( '(' name '=' value ( ', ' name '=' value )* ')')*

ident: (\w|:|\+|-|\.|\[|\]|\\|/)+

The semantics are:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's describe here the semantics of ident(...).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added, tried to adhere to the previous style. LMKWYT.

@@ -43,6 +47,9 @@ class TokenType(enum.Enum):
NOT = "not"
IDENT = "identifier"
EOF = "end of input"
EQUAL = "="
STRING = "str"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"str" might be confusing, I suggest "string literal"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ I agree, changed accordingly and added test case.

@webknjaz
Copy link
Member

Too bad that it got auto-merged earlier, than intended. I knew that Ronny didn't want to make the process too difficult for a first-time contributor so I fixed a few concerns myself and hit Approve to replace the blocking review, assuming that Ronny did check the other things. And I addressed Ronny's comment and dismissed his review, communicating with him offline. This was in line with Ronny's plan and the auto-merge that he'd set earlier (it wasn't auto-reset after my commits). I should've instead dismissed my review so it'd not be a blocker and not hit approve, as there were parts I didn't really inspect deeply.

@lovetheguitar since you stated you wanted to do better, here are some action items that you could perform (with an attempt to explain why they are important):

  1. Make a follow-up PR or several of them, addressing the comments that Ran and Florian made (I suppose, this one is rather obvious);
  2. When you see examples of things similar to what you're adding, it's usually best to follow the surrounding style — this avoids additional mental overhead for the reviewers since the pre-existing pieces have been battle-tested and any customizations would need to be inspected from various angles and might be missed by some reviewers (I missed it, for example, and didn't even know it was significant);
  3. I usually recommend not closing/dismissing the separate inline review threads started by someone else for the following reasons:
    • only the thread creator knows if you actually addressed their concerns — you can guess but in general, the only way of really knowing is to let them declare the thing solved. A review is a multi-sided conversation and not someone throwing in instructions to be either followed or dismissed silently — be verbose about what's going on and let others get onto the same page with you;
    • clicking the Resolve conversation button silently adds mental overhead for the creator since it doesn't generate any notifications and just becomes collapsed; It then requires someone to click on all the collapsed threads, trying to figure out where that old context exists and attempt to judge whether it was solved and guess why it disappeared as the reason is not always obvious and see the previous point for why it might not actually be resolved. It creates more round-trips, confusion, and frustration. I recommend just re-asking if you feel like it might be resolved or waiting;
    • in the previous point, I mentioned that no-notification disappearance of threads. To solve this point, if you're like really sure you can decide that it's resolved by yourself (for example, when you followed what was requested to the letter), I suggest writing a summary/notification comment about this action — I typically explain why/how the thing was resolved and add explicit text like "Marking this thread as resolved." This lets other people actually find it by following their email notifications and registering that they don't need to follow that conversation anymore. I've also seen great pull request authors including commit links there — in case they didn't click on the Commit suggestion button and re-implemented it.

That point (3) is something that happened here — I suggested a few correct fixes and extra explanations in some places. It was possible to click one button on the GH UI and get them in. However, they ended up being dismissed for a mysterious reason that I still have no idea of. There was a comment about choosing to use an alternative (worse) syntax option but the motivation was never stated. So I still had to just add those things manually which prolonged the PR cycle — because of lack of communication.
And with the other suggestion, it was dismissed with an incorrect perception, forcing me to dig the thread from the pile of others and unresolve it.

To summarize, the PR was good in general, but a few things were missed during the review and the transparency could be better (not just on the PR author's side, though).

@lovetheguitar
Copy link
Contributor Author

Very nice work, I wouldn't have done it better myself :)

I left some comments.

Thanks for the fast review @bluetech. I tried to address the open points in #12523.
Looking forward to some feedback.

The-Compiler added a commit that referenced this pull request Jun 25, 2024
Improve documentation & other kinks of marker keyword expression PR #12500
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bot:chronographer:provided (automation) changelog entry is part of PR
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support marker keyword arguments in marker expression (test selection via -m)
5 participants