Modernize test infrastructure (Python 3.12+, uv oldestdeps), replace pytz with zoneinfo, fix LocalTimeConstraint - #641
tepickering wants to merge 17 commits into
Conversation
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
_mock_remote_data, _unmock_remote_data, EarthLocation_mock and FixedTarget._from_name_mock were never called; the conftest that used them no longer exists. Network-dependent tests use @pytest.mark.remote_data instead. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Doctests in docs/tutorials call plt.show(), which opened (and blocked on) GUI windows when pytest was run outside tox with an interactive default backend. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…line pytest-mpl defaults to a 'baseline/' directory, but the baselines live in 'baseline_images/', so --mpl runs could never find them. Point the markers at the right directory and regenerate the baseline, whose text rendering no longer matched current matplotlib (RMS 8.9). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- Bump requires-python to >=3.12 to match astropy; drop 3.11 and add 3.14 to the tox envlist. - Raise numpy/matplotlib minimums (and oldestdeps pins) to 1.26.0 and 3.7.3, the oldest releases with Python 3.12 wheels. - CI: default Python 3.14; base-dependency runs on Linux 3.12/3.13 and macOS/Windows 3.14; oldest deps on 3.12; all deps + remote data + coverage on 3.14; allowed-failure devdeps + remote data on 3.14. - Build docs on RTD with Python 3.14. - Ignore setuptools_scm-generated astroplan/_version.py. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Use tox-uv so the oldestdeps environments install every dependency at the lowest version allowed by the declared lower bounds, rather than hand-maintained pins that break when an unpinned transitive dependency (e.g. pyparsing) releases. Transitive lower bounds live in oldestdeps-constraints.txt, applied via UV_CONSTRAINT. Testing the real lower bounds surfaced floors that don't work on Python 3.12, so raise pytz to 2023.3.post1 and pytest to 7.4.0, and add lower bounds for astroquery, pytest-astropy and pytest-mpl. Also switch pip-specific settings to their uv equivalents: use tox's list_dependencies_command instead of pip freeze, and UV_EXTRA_INDEX_URL with pip-like index strategy for devdeps. See astropy#631. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
pytz is only needed on Python < 3.9; use zoneinfo.ZoneInfo for string timezones and datetime.timezone.utc for UTC. Depend on tzdata so timezone data is available on platforms without a system database (e.g. Windows). Timezones created with pytz are still accepted: UTC->local conversion uses astimezone(), which is correct for both, and naive datetimes are attached with localize() when the timezone provides it. The alldeps environments install pytz so this remains tested. Closes astropy#601. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
compute_constraint looked up the observer's (or the limits') timezone but then compared the limits against UTC times. Convert times to that timezone with Time.to_datetime before comparing, as proposed in astropy#470. Also evaluate the window element-wise so any shape of time grid works (replacing a bare except fallback), compare naive wall-clock limits so a tzinfo on min/max only selects the timezone, and stop overwriting self.min when only max is given. The Hawaii regression test previously encoded the buggy behaviour; move its time so the expectations only hold with local-time conversion, and add offline tests (zoneinfo and pytz) based on the issue's example. The periodic tutorial's example output was stale: with the fix, 7 of 100 HD 209458 b transits are observable from APO rather than none. Closes astropy#466. Co-authored-by: Lagrang3 <32647090+Lagrang3@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #641 +/- ##
==========================================
+ Coverage 76.57% 78.05% +1.48%
==========================================
Files 14 14
Lines 1844 1800 -44
==========================================
- Hits 1412 1405 -7
+ Misses 432 395 -37 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
The transitive lower bounds were generated on macOS, so Linux-only dependencies (keyring -> secretstorage -> cryptography) had none and uv's lowest resolution picked cryptography 2.0, which fails to build on Python 3.12. Regenerate the list from a universal resolution, adding Linux- and Windows-only packages, and drop entries no longer reached. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Maintainers: branch protection needs updating before this can merge. This PR renames the CI jobs, and The new names describe roles rather than Python versions ("oldest", "current" = Could an admin update the required status checks (Settings → Branches → Remove (old names that no longer report):
Add:
Keep as they are: I'd suggest not requiring The two skipped "report overall test coverage" jobs are expected. They're also skipped on |
It plotted at datetime.now(), so no baseline could ever match, never returned its figure, and needed remote data only to look up Subaru's location. Use a fixed time and explicit coordinates, return the figure, and add a baseline image. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Add an 'image' tox factor that pins matplotlib to the version the baseline images were generated with (rendering changes between releases) and runs only the mpl_image_compare tests with --mpl, writing an HTML summary to results/. Run it in CI as py314-test-image and upload results/ as an artifact. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… notes Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Job names are required status checks on main, so versioned names force an admin to update branch protection on every Python bump. Name jobs by role instead: oldest/intermediate/current Python (current being default_python), and latest for devdeps, which moves to a new Python first. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Python 3.15 release candidates are available through uv, so move the allowed-failure devdeps job (the "latest Python" job) to 3.15. Nightly numpy and astropy install there; matplotlib has no 3.15 nightlies yet and falls back to its latest release until it does. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Add py314t and py315t tox envs and a "Linux - threaded Python, base dependencies" CI job on py314t. Set PYTHON_GIL=0 there, as astropy does, since pyerfa doesn't declare free-threading support yet and importing it would otherwise re-enable the GIL with a RuntimeWarning. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Running the tox environments locally needs tox and tox-uv, which no extra installed. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This PR fixes several problems in the testing infrastructure, raises the minimum Python version to 3.12, and fixes two long-standing issues. Each commit is self-contained and can be reviewed separately.
Closes #466, closes #601, closes #631.
Testing infrastructure
requires-pythonis now>=3.12, matching astropymain, where 3.11 has been dropped ahead of its next release. 3.11 is removed from tox and CI in this PR.Linux - threaded Python, base dependencies, runs the tests on free-threaded Python 3.14 (py314t-test).py314tandpy315ttox environments are added. They setPYTHON_GIL=0, as astropy's free-threaded environments do: pyerfa doesn't declare free-threading support yet, so importing it would turn the GIL back on with aRuntimeWarning, whichfilterwarnings = errormakes fatal.py314-test-image, runs the image comparison tests with--mpl. It pins matplotlib to 3.11.2, the version the reference images were made with, because rendering changes between releases. Itsresults/folder (HTML summary and any diff images) is uploaded as an artifact.default_python), plus "latest" for the devdeps job, which moves to a new Python first. For example,Linux - oldest Python, oldest dependenciesandmacOS - current Python, base dependencies. Required checks are matched by name, so future Python bumps only change the tox envs anddefault_python, never branch protection. A comment inci_tests.ymlexplains the convention.oldestdepsuses uv'slowestresolution (TST: improving reliability ofoldestdeps? #631). tox now usestox-uv, which tox installs automatically throughrequires. The oldest-deps environment installs every package at the lowest version allowed, instead of relying on hand-written pins such as thepyparsing==3.2.5workaround.pyproject.toml.oldestdeps-constraints.txt, applied throughUV_CONSTRAINT. Each one is the first release after Python 3.12.0 came out, and the rule is written in the file's header.uv pip compile --universal), because some dependencies are only installed on one platform. For example, keyring pulls insecretstorageandcryptographyonly on Linux.{list_dependencies_command}instead ofpip freeze, andUV_EXTRA_INDEX_URLwithunsafe-best-matchsodevdepscan mix nightly and PyPI packages.devextra.pip install -e ".[dev]"installs theallandtestextras plustoxandtox-uv, so the tox environments can be run locally without installing tox separately.conftest.pyforces theAggmatplotlib backend. Doc examples that callplt.show()were opening windows when pytest ran outside tox.mpl_image_comparemarkers now point atbaseline_images/. pytest-mpl looks inbaseline/by default, so--mplcould never find the images.test_image_example.pngis regenerated for current matplotlib; the only change is font rendering.test_timezonefixed. It plotted atdatetime.now(), so no reference image could ever match. It also didn't return its figure and needed the network only to look up Subaru's location. It now uses a fixed time and Subaru's coordinates directly, returns its figure, and has a reference image, so it runs offline in every job._mock_remote_data,_unmock_remote_data,EarthLocation_mock,FixedTarget._from_name_mockand_open_shelvewere never called.Replace pytz with
zoneinfo(#601)zoneinfo.ZoneInfo, andpytz.utcbecomesdatetime.timezone.utc. pytz is no longer a dependency.tzdatais now a dependency. Windows has no system timezone database, sozoneinfoneeds it.astimezone(), which is correct for both kinds, and naive datetimes are still passed throughlocalize()when the timezone provides it. Thealldepsenvironments install pytz so this stays tested.timezoneis given as a string,Observer.timezoneis now aZoneInfoobject, so it no longer has pytz methods such aslocalize. This is noted inCHANGES.rst.Fix
LocalTimeConstraintignoring the local timezone (#466)compute_constraintlooked up the observer's timezone, or the timezone attached tomin/max, but never used it, so the limits were compared against UTC. This PR converts times withTime.to_datetime(timezone)before comparing. This fix follows the approach proposed by @Lagrang3 in LocalTimeConstraint fixed timezone (#466) #470, which stalled waiting for tests and a changelog entry; they are credited as co-author on that commit.self.minis no longer overwritten when onlymaxis given.test_local_time_constraint_hawaii_tzpreviously asserted the buggy behavior. Its time is moved so the expectations only hold with the conversion to local time.Testing
All CI jobs have passed on this PR, including the new image comparison job on Linux and the Read the Docs build with Python 3.14. The link check can fail on network problems: one docs example downloads an image from SkyView.
Also run locally with tox:
py312-test-oldestdeps(astropy 6.0.0, numpy 1.26.0, matplotlib 3.7.3, pytest 7.4.0)py312-test,py313-test,py314-test,codestylepy314-test-alldeps-cov, remote datapy314-test-devdeps, remote data (astropy 8.1.dev, numpy 2.6.dev)py315-test-devdeps, remote data (Python 3.15.0rc2; astropy 8.1.dev, numpy 2.6.dev, matplotlib 3.11.2)py314t-test,py314t-test-alldeps,py315t-test(free-threaded,PYTHON_GIL=0)py314-test-image(--mpl); also confirmed to fail on a mismatched imagebuild_docsdotmissing on the local machine; Read the Docs installs graphvizBranch protection
Because the jobs are renamed,
main's required status checks list four names that no longer report, so the PR shows as blocked even though CI passes. A repo admin needs to update the required checks once; with the version-independent names, they shouldn't need changing on future Python bumps. The list of checks to remove, add and keep is in this comment.This PR also adds a
CLAUDE.mdwith development notes for Claude Code.🤖 Generated with Claude Code