Repository navigation
CallInfo: store a timing.Duration, keep start/stop/duration as aliases - #15205
Open
RonnyPfannschmidt wants to merge 4 commits into
Open
RonnyPfannschmidt wants to merge 4 commits into
RonnyPfannschmidt wants to merge 4 commits into
Conversation
Add CallInfo._from_duration() so internal construction sites take their start/stop/duration from one _pytest.timing.Duration instead of passing three loose floats. from_call, subtests and unittest subtests now all go through it. Subtests measured time with time.time()/time.perf_counter() directly, so a test mocking time under subtests.test() produced negative or bogus durations (#15201, same class of bug as #185). unittest subtests reported start=stop=0 (the epoch); they now report a zero-length call at the current time. Fixes #15201. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
CallInfo now keeps the measured timing.Duration and exposes start, stop and duration as properties derived from it, instead of three loose floats that could disagree. External code still constructs CallInfo from floats (pytest-subtests, pytest-asyncio-concurrent) and assigns the timing attributes (pytest-asyncio-cooperative, pytest-velo), so the float constructor and the attribute setters keep working on top of the stored Duration. timing.Instant fields become init-able to support that. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
unittest reports subtests after the fact without timing. Keep the start=stop=duration=0 values inherited from pytest-subtests, but spell them as a named timing.UNMEASURED Duration instead of literal zeros. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
RonnyPfannschmidt
marked this pull request as ready for review
October 18, 2026 20:13
RonnyPfannschmidt
requested review from
bluetech and
nicoddemus
and removed request for
bluetech
October 18, 2026 20:13
|
I reported #15201 and tested this branch (claude/callinfo-duration-8dkzw2) against my original reproducer, which patches time.time and time.perf_counter to lambda: 0.0 inside the test and inside a subtest, and sleeps 0.3s. main @ 420cf37: test_with_subtest reported −1194.217s The negative duration is gone. Thanks for the quick fix. |
Store the Duration directly instead of splitting it into floats for __init__ and then overwriting the rebuilt Duration. __init__ remains only as the legacy float entry point. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Requested by Ronny · project thread
🤖 Written by Claude Opus 5.5 via Claude Code for the pytest maintainers; I prompted it, it did the work, I read it.
Closes #15201. Supersedes #15204.
Before:
CallInfostoresstart,stopanddurationas three independent floats, and its three construction sites fill them differently.from_callusestiming.Instant.subtests.test()callstime.time()/time.perf_counter()directly, so a test that mockstimeinside a subtest gets negative or bogus durations (the #185 class of bug). unittestsubTestpasses literalstart=stop=duration=0.After:
CallInfostores one privatetiming.Duration, andstart/stop/durationare properties derived from it. All internal construction goes throughCallInfo._from_duration(result, excinfo, duration, when). The pattern for any new site is to take atiming.Instant()when the phase begins and passinstant.elapsed(). Where there is nothing to measure, the site passes the namedtiming.UNMEASUREDplaceholder (all zeros). unittest subtests are the one such site: unittest reports them after the fact, without timing. Their reported values are unchanged frommain; the zeros, inherited from pytest-subtests in #13738, are now just named.Backward compatibility: external code still uses the float API, so it is kept.
CallInfo(result, excinfo, start, stop, duration, when, _ispytest=True)is used by pytest-subtests (still widely installed, plus Debian/deepin packaging), pytest-asyncio-concurrent and NVIDIA's isvtest. It now builds theDurationfrom the floats.call.start = ...,call.stop = ...,call.duration = ...) is used by pytest-asyncio-cooperative and pytest-velo. Setters update the storedDurationindependently, so each field keeps its old meaning.timing.Instantfields becomeinit-able (still defaulting to "now") so aDurationcan represent externally supplied floats.Visible change:
dataclasses.fields(CallInfo)now lists_durationinstead ofstart/stop/duration, and==compares the storedDuration.Tests: a subtest regression test for mocked
time(fails onmain), a unittest subtest test assertingtiming.UNMEASURED, andtest_runner.pycoverage for_from_durationand for the float constructor and setters.