Skip to content

Update tests to use test.support.captured_std* #136632

Description

@inventshah

Python's test suite has test.support.captured_std* utilities to temporarily swap sys.std* with an io.StringIO(). These wrapper functions are used at least 240 times throughout the tests. A few greps1 show quite a few instances where these helpers could be used but are not (45 use contextlib.redirect_std*, 30 use test.support.captured_output("std*"), and 15 use a manual solution).

Should we update these calls to use the support utilities to improve consistency or is it not worth the risk of accidentally changing a test's meaning and cluttering git blame? I found 31d1d72 from last year which did similar updating in test_pydoc/test_pydoc.py. There's also an argument the "update" should go from test.support.captured_std* to the newer contextlib.redirect_std* instead, and remove the testing specific utilities.

Since this touches a wide range of domains, my thought was to split this up into separate PRs for each test file so the appropriate person can more easily review. But we can also do this in larger chunks, at least for the lower risk changes.

Instances of contextlib.redirect_std* swappable for test.support.captured_std*. In some cases allowing the removal of io and contextlib imports.

Instances of test.support.captured_output("std*") swappable for a corresponding test.support.captured_std*. Low risk to change since the latter calls the former.

Tests with a manual stream save/restore (most are stdin). Higher risk especially when replacing over long blocks.

  • libregrtest/single.py - L297
  • test_builtin.py - L1748
  • test_capi/check_config.py - L70
  • test_concurrent_futures/test_deadlock.py - L148
  • test_exceptions.py - L71
  • test_faulthandler.py - L399, L806
  • test_fileinput.py - L129, L151
  • test_optparse.py - L160
  • test_profile.py - L90 & L101 call silent which could be test.support.captured_stdout instead of reimplementing the function.
  • test_pydoc.py - L478
  • test_unittest/test_runner.py - L1425
  • test_winconsoleio.py - L170
  • test_wsgiref.py - L74

Linked PRs

Footnotes

  1. Searched for captured_output("std, sys.std* =, and redirect_std* so there may be some missing instances. I manually reviewed the ones listed to check if replacement seemed doable (for instance test_calendar.py needs TextIOWrapper(BytesIO) not StringIO, so cannot be easily swapped). ↩

Activity

  1. picnixz commented on Jul 14, 2025

    @picnixz
    Member

    I'd be ok with those changes but there seem to be many of them. I think it's better if we only change the code if we need to. Now, many recent occurrences in CLI tests are because of my original implementation that I gave as an example so I can take the blame here. I didn't know about this helper and if I did, I would have used it. So for the CLI tests that use redirect_*, I'm ok with changing them in one go. For the others, especially tests that are very old, I wouldn't bother changing them.

    Also, to prevent future conflicts, it'd be good to separate the CLI tests into two PR. One PR that touches the CLI tests added in 3.15 only and one PR that touches the CLI tests added in 3.14 so that we can backport it.

    Now, before starting any work, I want @serhiy-storchaka's opinion here.

  2. serhiy-storchaka commented on Jul 14, 2025

    @serhiy-storchaka
    Member

    I do not think that there is a reason to switch between contextlib.redirect_std* and test.support.captured_std*. The latter is a little simpler, but the difference is only in passing an explicit io.StringIO() argument. This is just a code churn.

    There is little benefit of changing test.support.captured_output("std*") to test.support.captured_std*.

    Only the third group has potential small long-term benefit, because using any of helpers would make the code clearer and easier to read and modify in future. But it has also higher risk of breaking code. This may outweigh the long-term benefit.

    I suggest leaving the code as is, but if it is changed in the future, use the standard helper.

  3. inventshah commented on Jul 21, 2025

    @inventshah
    ContributorAuthor

    That makes sense. Tests can be updated to use the helper as they change over time. I think for the third group, the cases where a helper would have the greatest code readability impact also have higher risk of breaking the test purpose. Closing the sample PR too.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    testsTests in the Lib/test dirtype-refactorCode refactoring (with no changes in behavior)

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions