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

testresult: correctly apply verbose word markup and avoid crash #12472

Merged
merged 5 commits into from
Jul 1, 2024

Conversation

pbrezina
Copy link
Contributor

@pbrezina pbrezina commented Jun 18, 2024

The following snippet would have resulted in crash on multiple places since
_get_verbose_word expects only string, not a tuple.

    @pytest.hookimpl(tryfirst=True)
    def pytest_report_teststatus(report: pytest.CollectReport | pytest.TestReport, config: pytest.Config):
        if report.when == "call":
            return ("error", "A",  ("AVC", {"bold": True, "red": True}))

        return None
Traceback (most recent call last):
  File "/home/pbrezina/workspace/sssd/.venv/bin/pytest", line 8, in <module>
    sys.exit(console_main())
             ^^^^^^^^^^^^^^
  File "/home/pbrezina/workspace/pytest/src/_pytest/config/__init__.py", line 207, in console_main
    code = main()
           ^^^^^^
  File "/home/pbrezina/workspace/pytest/src/_pytest/config/__init__.py", line 179, in main
    ret: Union[ExitCode, int] = config.hook.pytest_cmdline_main(
                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/pbrezina/workspace/sssd/.venv/lib64/python3.11/site-packages/pluggy/_hooks.py", line 513, in __call__
    return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/pbrezina/workspace/sssd/.venv/lib64/python3.11/site-packages/pluggy/_manager.py", line 120, in _hookexec
    return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/pbrezina/workspace/sssd/.venv/lib64/python3.11/site-packages/pluggy/_callers.py", line 139, in _multicall
    raise exception.with_traceback(exception.__traceback__)
  File "/home/pbrezina/workspace/sssd/.venv/lib64/python3.11/site-packages/pluggy/_callers.py", line 103, in _multicall
    res = hook_impl.function(*args)
          ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/pbrezina/workspace/pytest/src/_pytest/main.py", line 333, in pytest_cmdline_main
    return wrap_session(config, _main)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/pbrezina/workspace/pytest/src/_pytest/main.py", line 321, in wrap_session
    config.hook.pytest_sessionfinish(
  File "/home/pbrezina/workspace/sssd/.venv/lib64/python3.11/site-packages/pluggy/_hooks.py", line 513, in __call__
    return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/pbrezina/workspace/sssd/.venv/lib64/python3.11/site-packages/pluggy/_manager.py", line 120, in _hookexec
    return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/pbrezina/workspace/sssd/.venv/lib64/python3.11/site-packages/pluggy/_callers.py", line 139, in _multicall
    raise exception.with_traceback(exception.__traceback__)
  File "/home/pbrezina/workspace/sssd/.venv/lib64/python3.11/site-packages/pluggy/_callers.py", line 122, in _multicall
    teardown.throw(exception)  # type: ignore[union-attr]
    ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/pbrezina/workspace/pytest/src/_pytest/logging.py", line 872, in pytest_sessionfinish
    return (yield)
            ^^^^^
  File "/home/pbrezina/workspace/sssd/.venv/lib64/python3.11/site-packages/pluggy/_callers.py", line 124, in _multicall
    teardown.send(result)  # type: ignore[union-attr]
    ^^^^^^^^^^^^^^^^^^^^^
  File "/home/pbrezina/workspace/pytest/src/_pytest/terminal.py", line 899, in pytest_sessionfinish
    self.config.hook.pytest_terminal_summary(
  File "/home/pbrezina/workspace/sssd/.venv/lib64/python3.11/site-packages/pluggy/_hooks.py", line 513, in __call__
    return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/pbrezina/workspace/sssd/.venv/lib64/python3.11/site-packages/pluggy/_manager.py", line 120, in _hookexec
    return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/pbrezina/workspace/sssd/.venv/lib64/python3.11/site-packages/pluggy/_callers.py", line 139, in _multicall
    raise exception.with_traceback(exception.__traceback__)
  File "/home/pbrezina/workspace/sssd/.venv/lib64/python3.11/site-packages/pluggy/_callers.py", line 124, in _multicall
    teardown.send(result)  # type: ignore[union-attr]
    ^^^^^^^^^^^^^^^^^^^^^
  File "/home/pbrezina/workspace/pytest/src/_pytest/terminal.py", line 923, in pytest_terminal_summary
    self.short_test_summary()
  File "/home/pbrezina/workspace/pytest/src/_pytest/terminal.py", line 1272, in short_test_summary
    action(lines)
  File "/home/pbrezina/workspace/pytest/src/_pytest/terminal.py", line 1205, in show_simple
    line = _get_line_with_reprcrash_message(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/pbrezina/workspace/pytest/src/_pytest/terminal.py", line 1429, in _get_line_with_reprcrash_message
    word = tw.markup(verbose_word, **word_markup)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/pbrezina/workspace/pytest/src/_pytest/_io/terminalwriter.py", line 114, in markup
    text = "".join(f"\x1b[{cod}m" for cod in esc) + text + "\x1b[0m"
           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~
TypeError: can only concatenate str (not "tuple") to str

@pbrezina pbrezina force-pushed the testresult-markup branch 4 times, most recently from 7b46496 to 68ac980 Compare June 18, 2024 10:38
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.

Thank you! This looks reasonable, but will need a test case to avoid similar regressions in the future.

@pbrezina
Copy link
Contributor Author

Thank you! This looks reasonable, but will need a test case to avoid similar regressions in the future.

Please, could you contribute a test case by pushing to this pull request? I guess I could base something on test_line_with_reprcrash but this will only trigger one of the cases.

@The-Compiler
Copy link
Member

The-Compiler commented Jun 18, 2024

I'm afraid I got my hands full with stuff we're currently working on at the development sprint already.

It might be easier to write an integration test using pytester for this. Actually I'm confused: We already have test_report_teststatus_explicit_markup which is very close to what you showed in your original post, but that passes! So the first step would probably be to find out why that passes just fine. Maybe it just needs an additional check for the exit status or something?

I tried minimizing your example to:

def pytest_report_teststatus(report):
    return ("error", "A",  ("AVC", {"bold": True, "red": True}))

and I can still reproduce the failure, yet the test seems to pass just fine, despite doing almost the same:

        pytester.makeconftest(
            """
            def pytest_report_teststatus(report):
                return 'foo', 'F', ('FOO', {'red': True})
        """
        )

edit: No, the run in the test does indeed pass just fine:

> /home/florian/proj/pytest/testing/test_terminal.py(352)test_report_teststatus_explicit_markup()->None
-> breakpoint()
(Pdb) p result
<RunResult ret=0 len(stdout.lines)=11 len(stderr.lines)=0 duration=0.02s>

@webknjaz
Copy link
Member

@pbrezina please also compose a change log note/fragment, while you're on it. It's important to explain how the behavior changed to the end-users.

@psf-chronographer psf-chronographer bot added the bot:chronographer:provided (automation) changelog entry is part of PR label Jun 24, 2024
@pbrezina
Copy link
Contributor Author

@The-Compiler it fails only with error or failed category, not with foo. And if you set error in the test, the line matcher still works since the traceback is in stderr. I fixed the test and added changelog.

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'll let someone else check how this influences other parts of pytest, this review only contains a few simple improvement suggestions)

changelog/12472.bugfix.rst Outdated Show resolved Hide resolved
src/_pytest/reports.py Outdated Show resolved Hide resolved
src/_pytest/reports.py Outdated Show resolved Hide resolved
testing/test_terminal.py Outdated Show resolved Hide resolved
src/_pytest/reports.py Outdated Show resolved Hide resolved
@pbrezina pbrezina force-pushed the testresult-markup branch 2 times, most recently from c515a10 to 39ec880 Compare June 26, 2024 10:07
@pbrezina
Copy link
Contributor Author

All comments should be addressed now.

@@ -0,0 +1 @@
Fixed a crash when returning category ``"error"`` or ``"failed"`` with a custom test status from :hook:`pytest_report_teststatus` hook -- :user:`pbrezina`.
Copy link
Member

Choose a reason for hiding this comment

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

@webknjaz webknjaz requested a review from nicoddemus June 26, 2024 12:11
@webknjaz
Copy link
Member

All's good on my side, let's wait for Florian and perhaps somebody else to re-review.

src/_pytest/reports.py Outdated Show resolved Hide resolved
@pbrezina pbrezina force-pushed the testresult-markup branch 4 times, most recently from eb3dcd3 to 1031703 Compare June 27, 2024 13:16
Copy link
Member

@nicoddemus nicoddemus left a comment

Choose a reason for hiding this comment

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

Awesome, thanks!

src/_pytest/reports.py Outdated Show resolved Hide resolved
@nicoddemus nicoddemus enabled auto-merge (squash) June 27, 2024 13:29
src/_pytest/reports.py Outdated Show resolved Hide resolved
pbrezina and others added 5 commits July 1, 2024 13:23
The following snippet would have resulted in crash on multiple places since
`_get_verbose_word` expects only string, not a tuple.

```python
    @pytest.hookimpl(tryfirst=True)
    def pytest_report_teststatus(report: pytest.CollectReport | pytest.TestReport, config: pytest.Config):
        if report.when == "call":
            return ("error", "A",  ("AVC", {"bold": True, "red": True}))

        return None
```

```
Traceback (most recent call last):
  File "/home/pbrezina/workspace/sssd/.venv/bin/pytest", line 8, in <module>
    sys.exit(console_main())
             ^^^^^^^^^^^^^^
  File "/home/pbrezina/workspace/pytest/src/_pytest/config/__init__.py", line 207, in console_main
    code = main()
           ^^^^^^
  File "/home/pbrezina/workspace/pytest/src/_pytest/config/__init__.py", line 179, in main
    ret: Union[ExitCode, int] = config.hook.pytest_cmdline_main(
                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/pbrezina/workspace/sssd/.venv/lib64/python3.11/site-packages/pluggy/_hooks.py", line 513, in __call__
    return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/pbrezina/workspace/sssd/.venv/lib64/python3.11/site-packages/pluggy/_manager.py", line 120, in _hookexec
    return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/pbrezina/workspace/sssd/.venv/lib64/python3.11/site-packages/pluggy/_callers.py", line 139, in _multicall
    raise exception.with_traceback(exception.__traceback__)
  File "/home/pbrezina/workspace/sssd/.venv/lib64/python3.11/site-packages/pluggy/_callers.py", line 103, in _multicall
    res = hook_impl.function(*args)
          ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/pbrezina/workspace/pytest/src/_pytest/main.py", line 333, in pytest_cmdline_main
    return wrap_session(config, _main)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/pbrezina/workspace/pytest/src/_pytest/main.py", line 321, in wrap_session
    config.hook.pytest_sessionfinish(
  File "/home/pbrezina/workspace/sssd/.venv/lib64/python3.11/site-packages/pluggy/_hooks.py", line 513, in __call__
    return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/pbrezina/workspace/sssd/.venv/lib64/python3.11/site-packages/pluggy/_manager.py", line 120, in _hookexec
    return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/pbrezina/workspace/sssd/.venv/lib64/python3.11/site-packages/pluggy/_callers.py", line 139, in _multicall
    raise exception.with_traceback(exception.__traceback__)
  File "/home/pbrezina/workspace/sssd/.venv/lib64/python3.11/site-packages/pluggy/_callers.py", line 122, in _multicall
    teardown.throw(exception)  # type: ignore[union-attr]
    ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/pbrezina/workspace/pytest/src/_pytest/logging.py", line 872, in pytest_sessionfinish
    return (yield)
            ^^^^^
  File "/home/pbrezina/workspace/sssd/.venv/lib64/python3.11/site-packages/pluggy/_callers.py", line 124, in _multicall
    teardown.send(result)  # type: ignore[union-attr]
    ^^^^^^^^^^^^^^^^^^^^^
  File "/home/pbrezina/workspace/pytest/src/_pytest/terminal.py", line 899, in pytest_sessionfinish
    self.config.hook.pytest_terminal_summary(
  File "/home/pbrezina/workspace/sssd/.venv/lib64/python3.11/site-packages/pluggy/_hooks.py", line 513, in __call__
    return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/pbrezina/workspace/sssd/.venv/lib64/python3.11/site-packages/pluggy/_manager.py", line 120, in _hookexec
    return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/pbrezina/workspace/sssd/.venv/lib64/python3.11/site-packages/pluggy/_callers.py", line 139, in _multicall
    raise exception.with_traceback(exception.__traceback__)
  File "/home/pbrezina/workspace/sssd/.venv/lib64/python3.11/site-packages/pluggy/_callers.py", line 124, in _multicall
    teardown.send(result)  # type: ignore[union-attr]
    ^^^^^^^^^^^^^^^^^^^^^
  File "/home/pbrezina/workspace/pytest/src/_pytest/terminal.py", line 923, in pytest_terminal_summary
    self.short_test_summary()
  File "/home/pbrezina/workspace/pytest/src/_pytest/terminal.py", line 1272, in short_test_summary
    action(lines)
  File "/home/pbrezina/workspace/pytest/src/_pytest/terminal.py", line 1205, in show_simple
    line = _get_line_with_reprcrash_message(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/pbrezina/workspace/pytest/src/_pytest/terminal.py", line 1429, in _get_line_with_reprcrash_message
    word = tw.markup(verbose_word, **word_markup)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/pbrezina/workspace/pytest/src/_pytest/_io/terminalwriter.py", line 114, in markup
    text = "".join(f"\x1b[{cod}m" for cod in esc) + text + "\x1b[0m"
           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~
TypeError: can only concatenate str (not "tuple") to str
```

Signed-off-by: Pavel Březina <[email protected]>
auto-merge was automatically disabled July 1, 2024 11:23

Head branch was pushed to by a user without write access

@pbrezina
Copy link
Contributor Author

pbrezina commented Jul 1, 2024

Rebased.

@webknjaz webknjaz enabled auto-merge July 1, 2024 14:25
@webknjaz
Copy link
Member

webknjaz commented Jul 1, 2024

@The-Compiler FYI this is waiting for you to unblock.

@webknjaz webknjaz merged commit 90459a8 into pytest-dev:main Jul 1, 2024
29 checks passed
Copy link

patchback bot commented Jul 1, 2024

Backport to 8.2.x: 💚 backport PR created

✅ Backport PR branch: patchback/backports/8.2.x/90459a8fd3e308ba89f4d1a43cb2ee0412db523c/pr-12472

Backported as #12555

🤖 @patchback
I'm built with octomachinery and
my source is open — https://github.com/sanitizers/patchback-github-app.

@nicoddemus
Copy link
Member

@webknjaz thanks for merging.

In the future consider squashing when the commits are not atomic/make sense, otherwise we end up with a polluted history:

image

@webknjaz
Copy link
Member

webknjaz commented Jul 1, 2024

In the future consider squashing when the commits are not atomic/make sense, otherwise we end up with a polluted history:

Ah, fair. I usually do this, but this time I forgot to check. I saw that your auto-merge got reset and clicked again w/o noticing that it was in a different mode, not natural merge.

nicoddemus pushed a commit that referenced this pull request Jul 12, 2024
nicoddemus pushed a commit that referenced this pull request Jul 12, 2024
(cherry picked from commit 90459a8)

Co-authored-by: Sviatoslav Sydorenko (Святослав Сидоренко) <[email protected]>
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.

4 participants