Skip to content

Handle chararray deprecation - #19267

Merged
pllim merged 10 commits into
astropy:mainfrom
saimn:fits-warn-chararray
Feb 20, 2026
Merged

pllim merged 10 commits into
astropy:mainfrom
saimn:fits-warn-chararray

Conversation

@saimn

@saimn saimn commented Feb 4, 2026 •

Copy link
Copy Markdown
Contributor

Possible fix for #19216
Also fix #3862

  • Hide deprecation warning from numpy about chararray (and avoid possible future warning about np.char.array since we no more use it)
  • Deprecate special chararray methods

Description

This pull request is to address ...

Fixes #

  • By checking this box, the PR author has requested that maintainers do NOT use the "Squash and Merge" button. Maintainers should respect this when possible; however, the final decision is at the discretion of the maintainer that merges the PR.

@github-actions

github-actions Bot commented Feb 4, 2026

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.

Comment thread astropy/utils/compat/numpycompat.py Resolved
Comment on lines +94 to +95
with warnings.catch_warnings():
warnings.filterwarnings("ignore", category=DeprecationWarning)

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.

I think this is okay but it does require #19233

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.

Oh, sorry, I didn't see this before I commented below.

@mhvk mhvk left a comment

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.

I think this works well for 8.0, nice solution for now! Only one suggestion for a simplification.

For 7.2., this cannot work since np.strings is not available for numpy < 2.0, but I think for that we can just filter out the deprecation warning; np.char will surely continue to exist until Fall '26.

Comment thread astropy/utils/compat/numpycompat.py Outdated
return super().__getattribute__(name)


def get_chararray(obj, itemsize=None, copy=True, unicode=None, order=None):

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.

I'd tend to just import from np.char import array as get_chararray inside a warnings filter above.

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.

+1
Both approach seem fine to me, but I'd rather we avoid mixing them.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@mhvk - do you mean to use that for a simplified version in 7.2.x ? (which seems reasonable to me but will be in a follow up PR).

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.

I realize now that what I wrote was confusing, since of course the numpy version of get_chararray returns the numpy chararray rather than our new one that shows deprecation warnings. In that sense, my comment is moot. But, arguably still a bit cleaner -- since we are deprecating -- might be to use np.char.array and then just view the result as our class. Since I wasn't absolutely sure this would not give warnings, I just tried, and it seemed to work -- I submitted it as a PR to your branch (see saimn#10).

Note that I also went ahead there and moved the deprecated_attributes inside the class; I think that is a bit cleaner (but it is a separate commit, so if you don't like it, just omit it).

@neutrinoceros neutrinoceros linked an issue Feb 4, 2026 that may be closed by this pull request
@pllim pllim added this to the v8.0.0 milestone Feb 4, 2026
@pllim

pllim commented Feb 4, 2026

Copy link
Copy Markdown
Member

Probably need a change log under "api", which I don't see yet.

For v7.2.x, is max pinning numpy an option?

@pllim pllim added the API change PRs and issues that change an existing API, possibly requiring a deprecation period label Feb 4, 2026
@neutrinoceros

Copy link
Copy Markdown
Contributor

For v7.2.x, is max pinning numpy an option?

It's never ideal, but as far as I can tell it's our only option left

@mhvk

mhvk commented Feb 4, 2026

Copy link
Copy Markdown
Contributor

I think your pin of numpy < 2.7 for 7.2 (combined with ignoring the deprecation warnings in tests) is still very liberal, so very unlikely anyone will have a problem other than possibly seeing those numpy deprecation warnings.

@saimn

saimn commented Feb 5, 2026

Copy link
Copy Markdown
Contributor Author

For 7.2., this cannot work since np.strings is not available for numpy < 2.0, but I think for that we can just filter out the deprecation warning; np.char will surely continue to exist until Fall '26.

Sorry I'm a bit lost in versions: we are releasing 8.0 this spring so the next major release (9.0) will not be before spring 2027. Since the deprecation is not yet released in Numpy's side, it seems we can wait 9.0 next year to actually remove chararray from astropy, which leaves enough deprecation time ? If this is OK then the deprecation can be added to 8.0, no need to backport to 7.2 ?

@neutrinoceros

Copy link
Copy Markdown
Contributor

Agreed. We dont need to backport this deprecation.


if strip_spaces and coltype is np.bytes_:
arr = arr.rstrip()
arr = np.strings.rstrip(arr)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

One additional comment here: if in the future rstrip is applied on the io.fits ndarray column it means that trailing spaces will always be stripped for Table. So strip_spaces will become useless and default behavior will change. But I'm not sure if there a good way to warn people about this upcoming change without adding a noisy and annoying warning...

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.

what do you mean "noisy" ? Deprecation warnings are still only visible to direct callers and warnings in general only show up once per session by default.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The warning would need to be emitted by default (since strip_spaces is False by default) and only way to remove it would be to set strip_spaces=True (option that would disappear in the next major version).

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.

I don't think I understand your initial comment in this thread actually, let's get back to it;

if in the future rstrip is applied on the io.fits ndarray column

why would this happen ? do you expect it to be outside our control ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Currently (and on the contrary to io.fits which relies on chararray to do so), Table.read does not strip trailing spaces by default and strip_spaces was added no long ago to offer this option. If in the future rstrip is enforced in io.fits (calling it explicitly instead of relying on chararray) then this would propagate to Table.read and the default behavior would change.
My point above is I don't think there is a good way to warn about this upcoming change. I think it's fine for a major release, but I'm mentioning it because this will likely break some code in the wild (e.g. people using t[t['somecol'] == 'SOMEKEY '] to select some rows).

@neutrinoceros neutrinoceros Feb 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.

I see. If I got this right, there is actually a way to follow a proper deprecation cycle here, and preserve strict backward compatibility for those who want it.

from typing import Literal
UNSET = object()

def foo(*, strip_spaces: bool | Literal[UNSET] = UNSET) -> None:
    if strip_spaces is UNSET:
        warn(
            "default is currently False but will change to True in a future version, "
            "pass the argument explicitly to silence this warning.",
            AstropyDeprecationWarning,
            stacklevel=2,
        )
        strip_spaces = False
    ...

(note that we could use None as a sentinel value but I don't like it because it behaves too much like False where a bool is expected)
This of course assumes that we could maintain the current default behavior (strip_spaces=False) as an opt-in. Is this possible ?

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.

The problem is that this is pretty noisy. Though I guess one could at least only warn if the stripping actually has an effect.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We could use a different default value indeed, this would force people to adapt their code and use strip_spaces=True, but then this option would disappear in the next major release (and probably remain forever in people's code)...

Maintaining the strip_spaces=False option would be difficult since the stripping would be done pretty deep in io.fits. Stripping trailing spaces should be the default and only behavior, it was not done in Table.read for historical reason but at some point this change need to happen and dropping chararray is the good occasion to do it. The question is whether we want to be noisy about it or just make the change at some point with the risk of breaking some code. Anyways people that rely on the old behavior (e.g. t[t['somecol'] == 'SOMEKEY ']) will need to change their code, and there is no way to know who relies on this. We can detect if some spaces are stripped but that does not mean that people's code rely on it.

@neutrinoceros neutrinoceros Feb 18, 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.

ok if we cannot maintain strict backwards compat then I agree my proposal shouldn't be implemented and there is probably no way to warn users ahead of time programmatically. I think the best thing we could do at this time is go with your approach on main and actively seek for feedback in downstream projects (we probably should do the probing ourselves where we already know astropy.io.fits is heavily used).

Comment thread astropy/utils/compat/numpycompat.py Resolved

@neutrinoceros neutrinoceros left a comment

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.

I'm now on board with the current approach. I say let's bite the bullet as soon as possible and see what breaks, since we have no other option.

@neutrinoceros

Copy link
Copy Markdown
Contributor

I believe the only thread left hanging is at #19267 (comment) so let me remind @mhvk about it :)

@mhvk mhvk left a comment

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.

Thanks @neutrinoceros for the ping. I think in principle this is fine as is, but think we can simplify the numpycompat code; see in-line comment.

Comment thread astropy/utils/compat/numpycompat.py Outdated
return super().__getattribute__(name)


def get_chararray(obj, itemsize=None, copy=True, unicode=None, order=None):

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.

I realize now that what I wrote was confusing, since of course the numpy version of get_chararray returns the numpy chararray rather than our new one that shows deprecation warnings. In that sense, my comment is moot. But, arguably still a bit cleaner -- since we are deprecating -- might be to use np.char.array and then just view the result as our class. Since I wasn't absolutely sure this would not give warnings, I just tried, and it seemed to work -- I submitted it as a PR to your branch (see saimn#10).

Note that I also went ahead there and moved the deprecated_attributes inside the class; I think that is a bit cleaner (but it is a separate commit, so if you don't like it, just omit it).

@mhvk

mhvk commented Feb 19, 2026

Copy link
Copy Markdown
Contributor

p.s. Arguably, in numpycompat.py, we should just do the imports guarded by the warning filter on top, and define chararray and get_charray before __getattr__. But no big deal.

MAINT: avoid copy of function; simple docstrings, cleanup
@saimn

saimn commented Feb 20, 2026

Copy link
Copy Markdown
Contributor Author

Note that I also went ahead there and moved the deprecated_attributes inside the class; I think that is a bit cleaner (but it is a separate commit, so if you don't like it, just omit it).

That was causing a RecursionError: maximum recursion depth exceeded (since __getattribute__ is called on the attributes as well) so I moved the list outside of the class.

@saimn
saimn force-pushed the fits-warn-chararray branch from 72eab86 to c889574 Compare February 20, 2026 09:56

@mhvk mhvk left a comment

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.

Oops, sorry for having done incomplete testing, I should have caught that. Anyway, am happy with this now!

Comment thread astropy/utils/compat/numpycompat.py Resolved
Comment thread docs/changes/io.fits/19267.api.rst Resolved
@pllim

pllim commented Feb 20, 2026 •

Copy link
Copy Markdown
Member

Yay, green CI! Two small nitpicks; I will let you decide if you want to incorporate the suggestions.

Some follow-up questions/comments:

  • I think we still need a backward compatible patch to v7.2.x to close out BUG: numpy.char.chararray is deprecated #19216
  • Does this PR fix Deprecate use of chararray in io.fits #3862 ?
  • Need to open follow-up issue to remove dead code stemmed from this PR for the future once deprecation period is over. Since this is FITS, is the deprecation period extra long then? What would be the conditions that would trigger removal? Please list them in the issue.

Thanks, all!

p.s. Posted an update to https://groups.google.com/g/astropy-dev/c/9u0qVkhAq9Q/m/G8y7_nJVBwAJ

@saimn

saimn commented Feb 20, 2026

Copy link
Copy Markdown
Contributor Author

I think we still need a backward compatible patch to v7.2.x to close out BUG: numpy.char.chararray is deprecated #19216

Yes, will do that once the PR is merged.

Does this PR fix Deprecate use of chararray in io.fits #3862 ?

Yes.

Need to open follow-up issue to remove dead code stemmed from this PR for the future once deprecation period is over. Since this is FITS, is the deprecation period extra long then? What would be the conditions that would trigger removal? Please list them in the issue.

The plan is to remove the compatibility code and use of chararray for v9.0 (don't think we can wait longer since at some point Numpy will remove charray). I already have a branch.

@pllim

This comment was marked as resolved.

@pllim

pllim commented Feb 20, 2026

Copy link
Copy Markdown
Member

I opened follow-up issue for eventual removal at:

I also kicked off a nightly wheel run with this patch:

https://github.com/astropy/astropy/actions/runs/22239173192

Thanks, all!

@saimn
saimn deleted the fits-warn-chararray branch February 23, 2026 07:53
@saimn saimn mentioned this pull request Feb 26, 2026
1 task
tonybaloney pushed a commit to tonybaloney/swe-complex-astropy that referenced this pull request Mar 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

API change PRs and issues that change an existing API, possibly requiring a deprecation period io.fits unified-io utils

Projects

None yet

Development

Successfully merging this pull request may close these issues.

BUG: numpy.char.chararray is deprecated Deprecate use of chararray in io.fits

4 participants