Skip to content

Exclude test_suite() from pytest collection (fixes #481) - #505

Open
agu2347 wants to merge 2 commits into
Pylons:mainfrom
agu2347:fix-test-suite-pytest-collection-issue-481
Open

Exclude test_suite() from pytest collection (fixes #481)#505
agu2347 wants to merge 2 commits into
Pylons:mainfrom
agu2347:fix-test-suite-pytest-collection-issue-481

Conversation

@agu2347

@agu2347 agu2347 commented Aug 24, 2026

Copy link
Copy Markdown

Fixes #481.

tests/test_regression.py defines a module-level test_suite() function that returns a doctest.DocTestSuite, following the classic unittest/zope.testrunner convention for external test discovery. Its name matches pytest's default python_functions pattern (test_*), so pytest also collects and runs it directly as an ordinary test, then emits:

PytestReturnNotNoneWarning: Expected None, but tests/test_regression.py::test_suite returned <doctest._DocTestSuite ...>, which will be an error in a future version of pytest.

This fix sets test_suite.__test__ = False, the standard pytest idiom for excluding a specifically named function from collection. test_suite() remains available and unchanged for any external runner that still calls it by the classic module-level convention -- only pytest's own collection of it as a test is suppressed.

Testing

  • Added test_test_suite_excluded_from_pytest_collection, asserting test_suite.__test__ is False.
  • Reproduced the warning on main (pytest tests/test_regression.py -W always), confirmed it disappears with this change, and confirmed the new test fails with AttributeError if the fix is reverted (with the test kept).
  • Ran the full suite before and after: identical 838 passed / 8 skipped, and the same 13 pre-existing unrelated ruff lint failures in both runs, with the only change being one fewer warning (the PytestReturnNotNoneWarning) in the after run.

tests/test_regression.py defines a module-level test_suite() function
returning a doctest.DocTestSuite, following the classic unittest/
zope.testrunner convention for external test discovery. Because its
name matches pytest's default python_functions pattern ("test_*"),
pytest also collects and runs it directly as an ordinary test, then
emits PytestReturnNotNoneWarning since it returns a DocTestSuite
instead of None -- a warning slated to become a hard error in a
future pytest version.

Set test_suite.__test__ = False, the standard pytest idiom for
excluding a specifically named function from collection, so pytest
leaves it alone while it remains available for any external runner
that still calls test_suite() by the classic convention.

Added a regression test asserting the attribute stays set.
@kgaughan

Copy link
Copy Markdown
Member

It shouldn't be excluded, not really. It really needs to be converted from doctests to regular unit tests.

… on Pylons#505)

Per @kgaughan on Pylons#505: excluding test_suite() from pytest collection
papered over the underlying issue rather than fixing it. The doctest
it wrapped was already dead code -- it used Python 2-only syntax
(tuple-unpacking `def bind(self, (ip, port))`, print statements) that
cannot execute under Python 3 at all, and referenced a server/channel
API (`channel.next_channel_cleanup[0]`, `HTTPChannel.connected`
flipping synchronously) that predates the current
will_close/maintenance() design.

Removed test_suite()/doctest.DocTestSuite() and the now-unrunnable
docstring doctest entirely, and replaced them with real pytest
functions against the current API, covering the same regression the
doctest was meant to guard:
  - a channel idle past channel_timeout gets marked will_close by
    server.maintenance()
  - a channel with recent write activity (via handle_write()) does not
  - a channel with recent read activity (via handle_read()) does not
  - a channel with requests in flight is left alone regardless of
    last_activity (the original doctest's "main loop window" case)

Verified these are not vacuous: temporarily removed the
`self.last_activity = time.time()` updates from HTTPChannel's
handle_read()/handle_write() (reintroducing the original bug) and
confirmed the two activity tests fail while the other two still pass,
then restored the fix and reconfirmed all four pass.

Ran the full suite before and after: 798 passed / 8 skipped, no
failures, and the PytestReturnNotNoneWarning from Pylons#481 is gone (there
is no longer a test_suite() for pytest to collect and warn about).
@agu2347

agu2347 commented Aug 25, 2026

Copy link
Copy Markdown
Author

You're right, that was the better fix. Removed the exclusion hack and the dead Python 2-only doctest entirely (it couldn't run under Python 3 at all -- tuple-unpacking bind() params, print statements), and replaced it with real pytest functions covering the same regression against the current API: a channel idle past channel_timeout gets marked will_close, one with recent read or write activity doesn't, and one with requests in flight is left alone. Verified they're not vacuous by temporarily reintroducing the original bug (removing the last_activity updates from handle_read/handle_write) and confirming the activity tests fail. Full suite: 798 passed, 8 skipped. Ready for re-review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Test functions should return None, but tests/test_regression.py::test_suite returned <class 'doctest._DocTestSuite'>

2 participants