Skip to content

ENH: machinery for Astropy-style deprecation messages with stdlib deprecated - #20407

Draft
nstarman wants to merge 6 commits into
astropy:mainfrom
nstarman:utils/deprecation-msg
Draft

nstarman wants to merge 6 commits into
astropy:mainfrom
nstarman:utils/deprecation-msg

Conversation

@nstarman

@nstarman nstarman commented Sep 15, 2026

Copy link
Copy Markdown
Member

Trying an alternative starting point to #15307

Things I found:

  1. deprecation_msg needs the function name, which is a little inconvenient.
  2. astropy.utils.deprecated does docstring manipulation, so I needed to add another decorator that handles that.
  3. This construction works with static type checkers, but is more lines of code at the deprecation site than astropy.utils.deprecated :(
  4. I don't think I did the Check environment lowest resolution against reference (pull_request) correctly...

AI Disclosure

Claude Code to help with the refactors, and then to clean up my 20+ commit history into something reasonable.
And to write the docstrings / changelog.

  • I certify that I am human and that I take full responsibility for this pull request including all interactions with reviewers.

Merge method

@github-actions

Copy link
Copy Markdown
Contributor

Thank you for your contribution to Astropy! 🌌 This checklist is meant to remind the package maintainers who will review this pull request of some common things to look for.

  • Do the proposed changes actually accomplish desired goals?
  • Do the proposed changes follow the Astropy coding guidelines?
  • Are tests added/updated as required? If so, do they follow the Astropy testing guidelines?
  • Are docs added/updated as required? If so, do they follow the Astropy documentation guidelines?
  • Is rebase and/or squash necessary? If so, please provide the author with appropriate instructions. Also see instructions for rebase and squash.
  • Did the CI pass? If no, are the failures related? If you need to run daily and weekly cron jobs as part of the PR, please apply the "Extra CI" label. Codestyle issues can be fixed by the bot.
  • Is a change log needed? If yes, did the change log check pass? If no, add the "no-changelog-entry-needed" label. If this is a manual backport, use the "skip-changelog-checks" label unless special changelog handling is necessary.
  • Is this a big PR that makes a "What's new?" entry worthwhile and if so, is (1) a "what's new" entry included in this PR and (2) the "whatsnew-needed" label applied?
  • At the time of adding the milestone, if the milestone set requires a backport to release branch(es), apply the appropriate "backport-X.Y.x" label(s) before merge.

nstarman and others added 4 commits September 15, 2026 11:25
Factor the message building of the ``@deprecated`` decorator into a public
``deprecation_msg`` function, so that the identical message can be produced
without the decorator, e.g. ``warnings.deprecated(deprecation_msg(...))``.

A custom ``message`` is now validated. An unsupported format specifier
previously failed with a bare ``KeyError`` from ``str.format``, and an
``alternative`` that the message never interpolates was silently dropped;
both now raise a ``ValueError`` naming the supported specifiers.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: nstarman <nstarman@users.noreply.github.com>
…ated``

Factor the docstring handling out of ``@deprecated`` into a public
``deprecate_doc`` decorator, which takes the deprecation message from the
object's ``__deprecated__`` attribute. It can therefore document objects
deprecated by ``warnings.deprecated``, which sets that attribute but leaves
the docstring alone.

``warnings.deprecated`` is typed as taking a ``LiteralString``, so overload
``deprecation_msg`` to return one when every argument is a literal, while the
runtime ``str`` that ``@deprecated`` itself passes still gives a ``str``.

Also annotate the return type of ``deprecated`` itself. It was ``Callable``,
which erased the signature of whatever it decorated; it now returns
``Callable[[T], T]``, or the deprecated function for the call that passes one
as ``message``.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
``warnings.deprecated`` is only available from Python 3.13 onwards, so
``typing_extensions`` is needed to provide it on 3.12.

It is also a build requirement because ``setup.py`` imports ``astropy.units``
in the isolated build environment to generate the unit type stubs, the same
reason ``pyerfa`` is listed there.

Key ``min_versions`` in the docs configuration by canonical name, so the
lookup does not depend on whether the requirement is recorded with a hyphen
or an underscore.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: nstarman <nstarman@users.noreply.github.com>
Move every ``@deprecated`` use in ``astropy.cosmology`` onto ``warnings.deprecated``
(PEP 702), combining ``deprecation_msg`` for the message and ``deprecate_doc``
for the ``.. deprecated::`` directive. Type checkers report uses of an object
deprecated this way, which astropy's own decorator cannot express.

The messages and warning categories are unchanged: each site passes the
``obj_type`` that the decorator used to infer, and the category it used to
issue.

In ``representation/base.py`` the ``classmethod`` had to become the outer
decorator, since the standard library decorator rejects a classmethod object.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: nstarman <nstarman@users.noreply.github.com>
@nstarman
nstarman force-pushed the utils/deprecation-msg branch from 5ee28a1 to f660e04 Compare September 15, 2026 15:26
@nstarman nstarman added this to the v8.1.0 milestone Sep 15, 2026
@nstarman nstarman changed the title Utils/deprecation msg ENH: machinery for Astropy-style deprecation messages on stdlib deprecated Sep 15, 2026
@nstarman nstarman changed the title ENH: machinery for Astropy-style deprecation messages on stdlib deprecated ENH: machinery for Astropy-style deprecation messages with stdlib deprecated Sep 15, 2026
@neutrinoceros

Copy link
Copy Markdown
Contributor
  1. uv run scripts/check-lowest-resolved-tree.py --overwrite (--quiet)

message="Use ``angular_diameter_distance(z1, z2)`` instead.",
obj_type="function",
),
category=AstropyDeprecationWarning,

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As an API change I'm happy to drop this argument. I don't think any users are filtering for only Astropy deprecations.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hard to tell. In any case this should be done piecemeal, so this part would go to another PR

This comment was marked as resolved.

This comment was marked as resolved.

This comment was marked as resolved.

Comment thread docs/changes/utils/20407.feature.rst Outdated
Comment thread docs/conf.py Outdated
Comment thread docs/conf.py
for line in metadata.requires("astropy"):
req = Requirement(line.split(";")[0])
min_versions[req.name.lower()] = str(req.specifier)
min_versions[canonicalize_name(req.name)] = str(req.specifier)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this change? Seem unrelated.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typing extensions was complaining.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean a type checker ?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry. I meant it was complaining about typing_extensions.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is a one-to-one replacement. https://packaging.pypa.io/en/stable/utils.html#packaging.utils.canonicalize_name does more than just lowercase.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it typing-extensions or typing_extensions, only the canonicalized may know.

@neutrinoceros neutrinoceros Sep 16, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This discussion triggered my motivation to learn more about packaging APIs, here's the result: #20414

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great. We can wait on that PR to merge before rebasing and simplifying this one.

Comment thread docs/changes/utils/20407.feature.rst Outdated
specifier, or passing ``alternative`` without referencing ``{alternative}`` in
the message (which silently dropped it), raises a ``ValueError``.

Added ``astropy.utils.decorators.deprecate_doc``, a decorator that prepends a

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one is confusing. Is it useful beyond internal usage during transitional period? Should this be private?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agreed. Any new API needed for this PR should be private. We can always consider promoting them later if anyone downstream actually needs them.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. But private in which way: not listed in "all" or  underscore-prefixed?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both ?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't often import underscore-prefixed functions across modules.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But private in which way

That is for your APE to figure out. 😅 As far as I am concern, private in a way we can use internally and then remove without deprecation. 😸

@nstarman nstarman Sep 15, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lol. The problem is my APE did figure it out. Folks just didn't like the implied consequences. There is no good way for me to write this as reasonable private functions at this moment. Underscore-prefixes aren't imported across modules in Astropy and just removing it from __all__ will not dissuade people from using it on discovery, leading to deprecations-before-removal.
I'm fine doing either way since both are broken.

(I challenge anyone to find unclear public vs private API in https://github.com/GalacticDynamics/unxt or https://github.com/GalacticDynamics/coordinax/)

IMO since I think this will probably end up being public, making it look-public-but-it's-actually-not-in-__all__ is probably the better choice.

Comment thread astropy/utils/decorators.py
@pllim

pllim commented Sep 15, 2026

Copy link
Copy Markdown
Member

At a glance, the diff isn't too shocking. I can do a more careful review once current concerns are addressed and CI is green. Thanks!

Comment thread scripts/lowest-resolved-tree.txt
@github-project-automation github-project-automation Bot moved this from Triage to Review in progress in Cosmology, the Expansion Sep 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: Review in progress

Development

Successfully merging this pull request may close these issues.

3 participants