Skip to content

Allow BaseCoordinateFrames to be stored in tables (by giving them .info) - #16831

Merged
mhvk merged 3 commits into
astropy:mainfrom
mhvk:basecoordframe-info
Aug 26, 2024
Merged

mhvk merged 3 commits into
astropy:mainfrom
mhvk:basecoordframe-info

Conversation

@mhvk

@mhvk mhvk commented Aug 14, 2024 •

Copy link
Copy Markdown
Contributor

This pull request is to address the fact that coordinate frames cannot be written to tables (with or without data). It does this in two commits:

  1. Let BaseCoordinateFrame have an .info attribute. Here, since the code is almost the same as that for SkyCoord, I base SkyCoordInfo on the new CoordinateFrameInfo. Also, to avoid duplication of .insert methods, I define an _insert method on the info class, which can be used for both BaseCoordinateFrame and SkyCoord.
  2. Allow the use of BaseCoordinateFrame in tables, including tests for SkyOffsetFrame with and without associated data.

To get this to work, Table can no longer rely on the input having a len - since data-less coordinate frames raise on that. So, instead it prefers col.shape[0] if that is available. Obviously, instead one could we decided the best route was to give data-less frames a len (see #16833) , but that seemed a rather big API change (even though it is perhaps reasonable) (EDITED). Furthermore, since SkyOffsetFrame classes are defined on the fly, I treat these as special cases in the white list of allowed classes for serialization.

Fixes #16823

  • 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

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.

@pllim
pllim requested a review from lpsinger August 14, 2024 17:01

@mhvk mhvk left a comment

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.

I added some in-line comments.

defaultunit: str | Unit = "recommended"


class CoordinateFrameInfo(MixinInfo):

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.

This is essentially a copy of the old SkyCoordInfo, but with sc replaced by coord and with a single line removed (which gets added back in the new SkyCoordInfo, and with the new _insert method.


return out

def _insert(self, obj, values, axis=0):

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.

This is essentially the old SkyCoord.insert method - this avoids defining effectively the same method twice. I considered defining a mixin class, but since this method uses _new_like, it seemed reasonable to define it on the info class.

if "_sky_coord_frame" in self.__dict__:
if self._is_name(attr):
raise AttributeError(f"'{attr}' is immutable")
if attr != "info" and not attr.startswith("_"):

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 old __setattr__ caused me a lot of trouble, with its setting by default on the frame rather than on the coordinate instance itself. This ensures that info does not get propagated down, and also does a bit more sanity checks before proceeding.

return

frame_cls = frame_transform_graph.lookup_name(attr)
if frame_cls is not None and self.frame.is_transformable_to(frame_cls):

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.

This was mirroring what __getattr__ does, but surely for __setattr__ we should just forbid any name that is in the frame transform graph!? (I can change it back, of course).

cls_full_name = "astropy.coordinates.SkyOffsetFrame"
elif (
cls_full_name not in __construct_mixin_classes
and not cls_full_name.startswith("astropy.coordinates.builtin_frames")

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.

I did not really want to bother to add all the built-in frames explicitly to the white list. We may want to re-think the list more generally. E.g., anything starting with astropy.coordinates probably best is gotten from that directly, since that __init__ is much less likely to contain any other class that might be used to wreck mayhem.

Comment thread astropy/table/table.py Resolved
@mhvk mhvk changed the title Basecoordframe info Allow BaseCoordinateFrames to be stores in tables (by giving them .info) Aug 14, 2024
@mhvk mhvk changed the title Allow BaseCoordinateFrames to be stores in tables (by giving them .info) Allow BaseCoordinateFrames to be stored in tables (by giving them .info) Aug 14, 2024
@lpsinger

Copy link
Copy Markdown
Contributor

Very nice!

@ayshih

ayshih commented Aug 17, 2024

Copy link
Copy Markdown
Contributor

So, how can the serialization support be extended so that it will also work for SunPy stuff? We have both:

  • our own frame classes, so they are not under astropy.coordinates.builtin_frames
  • our own frame creator a la SkyOffsetFrame, so the created frame classes start with abc.RotatedSun...

@mhvk

mhvk commented Aug 17, 2024

Copy link
Copy Markdown
Contributor Author

@ayshih - maybe a better solution than what I have is to check for BaseFrame subclasses. But the on-the-fly frames are trickier, since there needs to be some way to know how to recreate them. Let me ping @taldcroft about that, since we discussed how to solve this serialization issue before (without a good solution, clearly!). Note that I don't think this PR necessarily should wait for that to be resolved - this problem really holds for any class that uses .info to provide a way for it to be included in a Table - right now, it is not possible to roundtrip through ECSV.

@mhvk
mhvk force-pushed the basecoordframe-info branch from 655eb3e to 8bbadfd Compare August 22, 2024 20:59
@mhvk

mhvk commented Aug 22, 2024

Copy link
Copy Markdown
Contributor Author

@taldcroft - coordinate frames now have a len() even without data, so I rebased this attempt to allow them to be stored in tables -- now the only changes to table itself are additions to serialize to ensure the frames can get recognized.

@StuartLittlefair, @adrn - this is the PR that actually uses the length of data-less frames (triggered by @lpsinger's need for storing such frames).

@taldcroft taldcroft left a comment

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 looks great from the table perspective! To be clear I did not dig into the changes to __setattr__ and __getattr__ here, so someone else needs to check that.

I do wonder if this deserves a What's New entry. It's a nice addition that might otherwise fly under the radar.

mhvk added 3 commits August 23, 2024 13:37
The code is almost the same as that for SkyCoord, so base
SkyCoordInfo on the new CoordinateFrameInfo.

Also, use that class to define an _insert method, which
can be used for both BaseCoordinateFrame and SkyCoord.
Includes test for SkyOffsetFrame
@mhvk
mhvk force-pushed the basecoordframe-info branch from 4881322 to 382acb2 Compare August 23, 2024 17:38
@mhvk

mhvk commented Aug 26, 2024

Copy link
Copy Markdown
Contributor Author

I added the what's-new, so will merge this now. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Frame objects without data to a Table

5 participants