Replace deprecated utcnow() and fix UTC-based timedelta date headers - #496
Merged
Conversation
This was referenced Aug 2, 2026
Contributor
|
Thank you @digitalresistor for moving this forward! |
Member
Author
|
@jenstroeger do you have any comments on this MR? Just looking for a 👍 to make sure I didn't miss something obvious! |
jenstroeger
approved these changes
Aug 3, 2026
jenstroeger
left a comment
Contributor
There was a problem hiding this comment.
@jenstroeger do you have any comments on this MR? Just looking for a 👍 to make sure I didn't miss something obvious!
The changes look ok to me, though I’m not versed with webob details. One note on the tests: maybe consider using the pytest.setenv() monkeypatch context manager?
digitalresistor
pushed a commit
that referenced
this pull request
Aug 3, 2026
Per review feedback on #496, replace the hand-rolled os.environ save/restore in the TZ-sensitive tests with pytest's monkeypatch context manager, wrapped in a local_timezone fixture so the tzset() that repopulates the C library's cached zone still runs after monkeypatch restores TZ. pytest.MonkeyPatch became public in pytest 6.2, so bump the testing extra's floor accordingly.
digitalresistor
added a commit
that referenced
this pull request
Aug 3, 2026
Per review feedback on #496, replace the hand-rolled os.environ save/restore in the TZ-sensitive tests with pytest's monkeypatch context manager, wrapped in a local_timezone fixture so the tzset() that repopulates the C library's cached zone still runs after monkeypatch restores TZ. pytest.MonkeyPatch became public in pytest 6.2, so bump the testing extra's floor accordingly.
digitalresistor
force-pushed
the
fix-utcnow-deprecation
branch
from
August 3, 2026 05:33
4002c11 to
503102c
Compare
The utcnow function was deprecated in Python 3.12[1]. Note that all datetime instances are kept non-timezone-aware to keep backword-compatibility. [1] https://docs.python.org/3.13/library/datetime.html#datetime.datetime.utcnow
Combining #475 and #480 leaves one behavior change worth guarding: 475's unconditional v.astimezone(timezone.utc) treats a *naive* datetime as local time, shifting it by the machine's UTC offset. WebOb has always treated naive datetimes as UTC, and CI runs in UTC so the change is invisible there. Gate the conversion on v.tzinfo, keeping 475's fix for aware datetimes while leaving naive ones alone, and cover the naive/aware/date paths plus the new utcnow() helper.
serialize_date() added the delta to _now(), which was datetime.now and so naive *local* time. The result is then fed to calendar.timegm (which reads its argument as UTC) and serialized with usegmt=True, so the header came out off by the machine's UTC offset. Adapted from #491 for this branch: - Rather than adding a _utcnow = datetime.utcnow hook, _now is now the utcnow() helper introduced alongside the #473 deprecation work. #491's original form would have reintroduced the deprecated call this branch removes, in the same module. - Repointing _now fixes the second occurrence of the same bug, in parse_date_delta(), which resolved delta seconds against local time and made e.g. Response.retry_after return an instant off by the UTC offset. Covered by test_parse_date_delta_is_utc_based. Keeping the single _now hook means the existing _NowRestorer tests continue to apply to both call sites.
Per review feedback on #496, replace the hand-rolled os.environ save/restore in the TZ-sensitive tests with pytest's monkeypatch context manager, wrapped in a local_timezone fixture so the tzset() that repopulates the C library's cached zone still runs after monkeypatch restores TZ. pytest.MonkeyPatch became public in pytest 6.2, so bump the testing extra's floor accordingly.
digitalresistor
force-pushed
the
fix-utcnow-deprecation
branch
from
August 3, 2026 05:38
503102c to
bcc693a
Compare
mmerickel
approved these changes
Aug 3, 2026
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.
Combines the three outstanding
utcnow()/ UTC-correctness pull requests into a single branch, with the conflicts resolved and the gaps closed. All three original authors are credited via their own commits.Issues
datetime.datetime.utcnow()is deprecated as of Python 3.12Response.expiresvia atimedeltaproduces a header off by the local UTC offsetSupersedes
These are all mutually exclusive — they rewrite the same lines — so none can be merged alongside the others. Their content is included here:
serialize_cookie_dateutcnow()helperClosing keywords don't apply to PRs, so those three need closing by hand once this lands.
What's here
The deprecation (#473). Takes #480's approach, which @mmerickel favoured in review: a
webob.datetime_utils.utcnow()helper built ondatetime.now(timezone.utc)that returns a naive UTC datetime. Nothing on the public API surface changes type, which keeps this separate from the tz-aware migration discussed in #473.Aware datetimes in cookies. Takes #475's fix so that a timezone-aware
datetimepassed toserialize_cookie_date()is converted to UTC rather than having its local wall-clock time written out as GMT.One change was needed to make that safe. #475 called
v.astimezone(timezone.utc)unconditionally, which treats a naive datetime as local time and shifts it by the machine's UTC offset — WebOb has always treated naive datetimes as UTC. UnderTZ=America/New_York:CI runs in UTC, so this is invisible there. The conversion is now gated on
v.tzinfo is not None, andtest_serialize_cookie_date_naive_datetime_is_utcpins it.UTC-based timedelta headers (#430).
datetime_utils._nowwasdatetime.now, i.e. naive local. The result is fed tocalendar.timegm(which reads its argument as UTC) and serialized withusegmt=True, so the value came out off by the local UTC offset.#491 fixed this by adding a
_utcnow = datetime.utcnowhook — but that would reintroduce the deprecated call this branch removes, in the same module. Instead_nowis repointed at theutcnow()helper. That also fixes a second occurrence of the same bug that #491 doesn't reach:parse_date_delta()resolved delta seconds against local time, so reading backResponse.retry_afterreturned an instant off by the UTC offset. Keeping the single_nowhook means the existing_NowRestorertests continue to apply to both call sites.Verified end-to-end against a real
ResponseunderTZ=Europe/Berlin:Testing
toxacross lint, py310–py314, pypy39, pypy310, coverage and docs: all green, 2398 passed / 1 xfailed per interpreter,coverage --fail-under=100at exactly 100%.No
datetime.utcnow()calls remain insrc/ortests/. The onlyDeprecationWarningthe suite still raises is WebOb's own pre-existingacceptparse.__contains__one.New tests: naive/aware/
datehandling inserialize_cookie_date, theutcnow()helper itself, and tz-sensitive regression tests for bothserialize_dateandparse_date_delta(skipped wheretime.tzset()is unavailable).Not addressed
Response.retry_afterstill returns a naive datetime on the delta-seconds path whileparse_datereturns an aware UTC one, so comparing the two raisesTypeError. Reconciling that is the tz-aware migration @mmerickel called "much more controversial" in #473, and is deliberately left alone here.