Skip to content

refactor representations as module - #13688

Closed
nstarman wants to merge 6 commits into
astropy:mainfrom
nstarman:coords-rep-module
Closed

nstarman wants to merge 6 commits into
astropy:mainfrom
nstarman:coords-rep-module

Conversation

@nstarman

@nstarman nstarman commented Sep 16, 2022 •

Copy link
Copy Markdown
Member

Signed-off-by: Nathaniel Starkman (@nstarman) [email protected]

It's a long file that does many things. Now it's one module with many files, each of which do fewer things.

For reviewers: I didn't change the test files, only moved them, so that should show that no (tested) functionality has changed!

TODO?

  • changelog, is one needed for an internal refactor?
  • break up the test files? Low priority, perhaps push to future PR.
  • BREAKING CHANGE: change folder name to representations

Checklist for package maintainer(s)

This checklist is meant to remind the package maintainer(s) who will review this pull request of some common things to look for. This list is not exhaustive.

  • 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 "When to rebase and squash commits".
  • 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?
  • Is a milestone set? Milestone must be set but astropy-bot check might be missing; do not let the green checkmark fool you.
  • 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.

@github-actions

Copy link
Copy Markdown
Contributor

👋 Thank you for your draft pull request! Do you know that you can use [ci skip] or [skip ci] in your commit messages to skip running continuous integration tests until you are ready?

@nstarman

Copy link
Copy Markdown
Member Author

@astropy/testing-infrastructure-maintainers pytest_doctestplus is broken?

@mhvk
mhvk marked this pull request as ready for review September 16, 2022 17:15
@mhvk

mhvk commented Sep 16, 2022

Copy link
Copy Markdown
Contributor

Sorry pressed the "convert to real PR" in the wrong PR. Though I like the idea, so I guess we might as well.

@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 like splitting up the module, but am less sure about also moving tests (partially since that means my memory of where test files lives will go haywire, while for representations itself, I'll tab-complete my way easily). I also think the tests themselves could do with a bit of reorganization, so I'd suggest separating the two steps. (An additional advantage is that then the test files will help ensure all from astropy.coordinates.reoresentation import ??? continue to work.)

I would also not split the various spherical representations - they are so clearly related that I think it is easier if they stay together (e.g., to keep the various quick transformations in one place), i.e., I would combine back radial, spherical, base_sphere, physicssphere and unitsphere, leaving as new modules base, cartesian, cylindrical, and spherical.

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

pllim commented Sep 16, 2022

Copy link
Copy Markdown
Member

Re: doctestplus -- no idea, hard to see with +3,796 −3,523

Comment thread astropy/table/tests/test_mixin.py Resolved
@nstarman

Copy link
Copy Markdown
Member Author

Interesting. De-serialization is an issue since the qualitative name has changed.

@pllim

pllim commented Sep 16, 2022

Copy link
Copy Markdown
Member

I just reran the last CI on main to see if the failure is caused by your changes or something else: https://github.com/astropy/astropy/actions/runs/3069379004

@pllim

pllim commented Sep 16, 2022

Copy link
Copy Markdown
Member

main still is green, so it is definitely you... 😅

@nstarman
nstarman force-pushed the coords-rep-module branch 2 times, most recently from d24f7f6 to 679db4c Compare September 16, 2022 22:04
@taldcroft

taldcroft commented Sep 16, 2022 •

Copy link
Copy Markdown
Member

About the test failure

        if cls_full_name not in __construct_mixin_classes:
>           raise ValueError(f'unsupported class for construct {cls_full_name}')
E           ValueError: unsupported class for construct astropy.coordinates.representation.cartesian.CartesianRepresentation

This is real assuming the class full name changed. There is a hard-coded lists of classes that the mixin constructor is allowed to construct, which is there for security reasons. It's hard-coded as strings so table doesn't have to import the rest of astropy always. @mhvk have talked about this before and never come up with a better way.

It's probably OK to just changed that hardcoded list, but doing stuff like this always makes you wonder if you will be breaking anything outside of astropy. [EDIT: now I see you did exactly that in 679db4c]

@mhvk

mhvk commented Sep 16, 2022

Copy link
Copy Markdown
Contributor

Oops, I guess that also means that we have to continue to support both astropy.coordinates.representation.CartesianRepresentation as well as the same but with cartesian inserted. Unless we start to muck with the __module__ in this PR, but I've never liked if things do not point to where they are defined. Another option might be for __construct_mixin_classes to allow wildcards.

@pllim

pllim commented Sep 16, 2022

Copy link
Copy Markdown
Member

Maaaybe we need to hold off on this until we unpin Sphinx. The coordinate imports galore already broke compat with Sphinx. I don't want this to break it more.

@nstarman

Copy link
Copy Markdown
Member Author

I opened #13689 to discuss improvements to __construct_mixin_classes.

@nstarman
nstarman force-pushed the coords-rep-module branch 2 times, most recently from aa9f0da to 7e21a10 Compare November 19, 2022 22:11
Signed-off-by: Nathaniel Starkman (@nstarman) <[email protected]>
Signed-off-by: Nathaniel Starkman (@nstarman) <[email protected]>
Signed-off-by: Nathaniel Starkman (@nstarman) <[email protected]>
Signed-off-by: Nathaniel Starkman (@nstarman) <[email protected]>
@nstarman

nstarman commented Dec 18, 2022 •

Copy link
Copy Markdown
Member Author

v5.2 is out, so this PR is ready to go in 🤞!

@pllim

pllim commented Dec 19, 2022

Copy link
Copy Markdown
Member

@mhvk requested changes in the review status, so maybe he wants to re-review?

And just to make sure, you did rebase recently enough to pick up the Sphinx unpinning, right?

@nstarman

Copy link
Copy Markdown
Member Author

I believe so. That was some time last week, right?

@eerovaher eerovaher 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.

Splitting up the representation classes into separate files is a good idea, but such a pull request that is moving lots of code around should try to avoid making any changes that are not necessary, at least not in the same commit that moves the code. @nstarman, it looks like you have some automatic tool (at least I hope it's an automatic tool) that edits comments and multi-line strings so that they fit into an 80 character line length limit. This does not affect the working of the code, but it is creating a lot of clutter in the diffs. Furthermore, the actual line length limit in astropy is now 88 characters. I've only marked two cases where the difference of enforced line lengths has led to needless editing of multi-line strings, but I expect there are more.

diff = differentials[key]
except TypeError as err:
raise TypeError(
"'differentials' argument must be a " "dictionary-like object"

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 is this split into two strings? It's a single string in main:

raise TypeError(
"'differentials' argument must be a dictionary-like object"
) from err

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.

Fixed!

Comment on lines +717 to +719
"To attach a RadialDifferential to a "
"UnitSphericalRepresentation, you must supply "
"a dictionary with an appropriate key."

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.

On main this error message is written in two lines, both within the 88 character line length limit, so there's no reason to split this into three lines here.

raise ValueError(
"To attach a RadialDifferential to a UnitSphericalRepresentation,"
" you must supply a dictionary with an appropriate key."
)

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.

Fixed!

@nstarman

Copy link
Copy Markdown
Member Author

@nstarman, it looks like you have some automatic tool (at least I hope it's an automatic tool) that edits comments and multi-line strings so that they fit into an 80 character line length limit.

Yes, VSCode. I'll go thru the comments and compare. Revert where necessary. Or maybe change VSCode to have an 88 character limit and let it auto format... 🤔

Signed-off-by: Nathaniel Starkman (@nstarman) <[email protected]>
Signed-off-by: Nathaniel Starkman (@nstarman) <[email protected]>
@nstarman

Copy link
Copy Markdown
Member Author

@eerovaher, I did the latter and then went and compressed error message strings into the minimum number of lines.

from .base import _invalidate_reprdiff_cls_hash # noqa: F401
from .base import get_reprdiff_cls_hash # noqa: F401

__all__ = [

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.

Should this __all__ be empty so only the coordinates namespace explicitly holds these objects?

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 with @eerovaher -- as few changes here as possible. So, let's keep this the same as the one originally in representations.py.

Comment on lines +717 to +719
"To attach a RadialDifferential to a "
"UnitSphericalRepresentation, you must supply "
"a dictionary with an appropriate key."

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.

Fixed!

diff = differentials[key]
except TypeError as err:
raise TypeError(
"'differentials' argument must be a " "dictionary-like object"

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.

Fixed!

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

Looks mostly OK, but I'm still unhappy with the empty __all__ -- readability counts, and this is done not for people reading the code, for whom it would be useful to know what classes are actually the ones being defined that get used elsewhere. Plus, that's how its done elsewhere in astropy.

from .base import _invalidate_reprdiff_cls_hash # noqa: F401
from .base import get_reprdiff_cls_hash # noqa: F401

__all__ = [

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 with @eerovaher -- as few changes here as possible. So, let's keep this the same as the one originally in representations.py.

from astropy.utils.data_info import MixinInfo
from astropy.utils.exceptions import DuplicateRepresentationWarning

__all__: list[str] = [] # Not public. Import from ``astropy.coordinates``

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 disagree with this still. Let's put things in here that get transferred up (like is done in modules in builtin_frames). If you feel strongly otherwise, I would suggest for now not having any __all__ (like in matrix_utilities, which is also not exposed). Though to me it is helpful to the code reader -- the ones for whom this matters most -- that it lists the classes, etc., that this file actually defines for use elsewhere.


def _fqn_class(cls: type) -> str:
"""Get the fully qualified name of a class."""
return f"{cls.__module__}.{cls.__qualname__}"

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.

Oddly, changed line.


from .base import BaseDifferential, BaseRepresentation

__all__: list[str] = [] # Not public. Import from ``astropy.coordinates``

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.

As above

@@ -0,0 +1,156 @@
from __future__ import annotations

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.

So, why is the license file still not added automatically then?

@@ -0,0 +1,1600 @@
from __future__ import annotations

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 there is no need for this if we remove __all__.

from .base import BaseDifferential, BaseRepresentation
from .cartesian import CartesianRepresentation

__all__: list[str] = [] # Not public. Import from ``astropy.coordinates``

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.

As above.


The representation can be supplied either as a
`~astropy.coordinates.representation.BaseRepresentation` class (e.g.,
`~astropy.coordinates.representation.base.BaseRepresentation` class (e.g.,

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 better changed to ~astropy.coordinates.BaseRepresentation, since that is where we want users to import from.

@eerovaher eerovaher 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.

I've highlighted a couple of needless changes to strings that are still present, but I have not made a thorough search even in the one file I looked at, so I expect there to be many more cases of this.

@nstarman, the tool that you are using now seems to respect the 88 character line length limit, but it is being very aggressive about it, i.e. it adds words from the beginning of the next line to the end of the current line if they fit. This is being done in multi-line strings, comments and docstrings.

I supported adopting black because I was annoyed with the number of commits in this repository that introduced formatting problems that had to be fixed in a separate commit. Such formatting fixes add clutter to the commit log and make it more difficult to track actual functional changes. black together with pre-commit can prevent such formatting-only commits from ever happening (although not everyone is making use of these tools). The tool you are using is making cosmetic changes to a codebase that black is happy with. This is adding clutter to the commit history, which I cannot approve. I recommend you turn off any code-formatting features VSCode might have and just let black do its job.


if not hasattr(cls, "attr_classes"):
raise NotImplementedError(
"Representations must have an " '"attr_classes" class attribute.'

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 string is still getting needlessly split up. Here's what it looks like in current main:

'Representations must have an "attr_classes" class attribute.'

Comment on lines +639 to +643
msg = (
f'Representation "{repr_name}" already defined, removing it to avoid '
f'confusion. Use qualnames "{fqn_cls}" and "{fqn_existing}" or class '
"instances directly"
)

@eerovaher eerovaher Dec 20, 2022 •

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.

Here the placement of spaces separating the sub-strings is being needlessly changed. Currently on main the spaces are put in the beginning of the sub-strings:

msg = (
f'Representation "{repr_name}" already defined, removing it to avoid'
f' confusion.Use qualnames "{fqn_cls}" and "{fqn_existing}" or class'
" instances directly"
)

def __setitem__(self, item, value):
if not isinstance(value, BaseRepresentation):
raise TypeError(
f"value must be a representation instance, " f"not {type(value)}."

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.

One f-string is good enough for main, it should be good enough here as well.

f"value must be a representation instance, not {type(value)}."

@larrybradley
larrybradley removed their request for review February 3, 2023 19:03
@pllim

pllim commented Apr 18, 2023

Copy link
Copy Markdown
Member

Wow +3,728 −3,658. I think this is too late to squeeze into v5.3 only 3 days before feature freeze. I am re-milestoning this.

I would recommend you wrap this up early in the v6.0 dev cycle. Or maybe breaking it into a series of smaller PRs.

@mhvk

mhvk commented May 8, 2023

Copy link
Copy Markdown
Contributor

See #14792 for a re-implementation of this - I still think this is an excellent idea but this PR was getting behind main and there were still a few additions that were a little controversial. I tried to keep it as much of a copy & paste effort as possible.

@mhvk mhvk closed this in #14792 May 10, 2023
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.

7 participants