Skip to content

Commit ba428ef

Browse files
astrofrogeteq
authored andcommitted
Merge pull request #5021 from eteq/fix-frame-attr-set-delayed
Fix SkyCoord AttributeError when a new frame is created after a SkyCoord has been inited
1 parent 4199703 commit ba428ef

File tree

3 files changed

+54
-2
lines changed

3 files changed

+54
-2
lines changed

CHANGES.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1538,6 +1538,10 @@ Bug Fixes
15381538

15391539
- ``astropy.coordinates``
15401540

1541+
- ``SkyCoord`` objects created before a new frame which has frame attributes
1542+
is created no longer raise ``AttributeError`` when the new attributes are
1543+
accessed [#5021]
1544+
15411545
- Fix some errors in the implementation of aberration for ``get_sun``. [#4979]
15421546

15431547
- ``astropy.cosmology``

astropy/coordinates/sky_coordinate.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -424,7 +424,17 @@ def __getattr__(self, attr):
424424
if attr in self.frame.get_frame_attr_names():
425425
return getattr(self.frame, attr)
426426
else:
427-
return getattr(self, '_' + attr)
427+
try:
428+
return getattr(self, '_' + attr)
429+
except AttributeError:
430+
# this can happen because FRAME_ATTR_NAMES_SET is
431+
# dynamic. So if a frame is added to the transform
432+
# graph after this SkyCoord was created, the "real"
433+
# underlying attribute - e.g. `_equinox` does not exist
434+
# on the SkyCoord. So we add this case to just use
435+
# None, as it wouldn't have been possible for the user
436+
# to have set the value until the frame existed anyway
437+
return None
428438

429439
# Some attributes might not fall in the above category but still
430440
# are available through self._sky_coord_frame.

astropy/coordinates/tests/test_sky_coord.py

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@
2121
from ..representation import REPRESENTATION_CLASSES
2222
from ...coordinates import (ICRS, FK4, FK5, Galactic, SkyCoord, Angle,
2323
SphericalRepresentation, CartesianRepresentation,
24-
UnitSphericalRepresentation, AltAz)
24+
UnitSphericalRepresentation, AltAz,
25+
BaseCoordinateFrame, FrameAttribute,
26+
frame_transform_graph)
2527
from ...coordinates import Latitude, EarthLocation
2628
from ...time import Time
2729
from ...utils import minversion
@@ -1214,3 +1216,39 @@ def test_spherical_offsets():
12141216
dra, ddec = i00.spherical_offsets_to(i1deg)
12151217
assert_allclose(dra, 1*u.deg)
12161218
assert_allclose(ddec, 1*u.deg)
1219+
1220+
def test_frame_attr_changes():
1221+
"""
1222+
This tests the case where a frame is added with a new frame attribute after
1223+
a SkyCoord has been created. This is necessary because SkyCoords get the
1224+
attributes set at creation time, but the set of attributes can change as
1225+
frames are added or removed from the transform graph. This makes sure that
1226+
everything continues to work consistently.
1227+
"""
1228+
sc_before = SkyCoord(1*u.deg, 2*u.deg, frame='icrs')
1229+
1230+
assert 'fakeattr' not in dir(sc_before)
1231+
1232+
class FakeFrame(BaseCoordinateFrame):
1233+
fakeattr = FrameAttribute()
1234+
1235+
# doesn't matter what this does as long as it just puts the frame in the
1236+
# transform graph
1237+
transset = (ICRS, FakeFrame, lambda c,f:c)
1238+
frame_transform_graph.add_transform(*transset)
1239+
try:
1240+
assert 'fakeattr' in dir(sc_before)
1241+
assert sc_before.fakeattr is None
1242+
1243+
sc_after1 = SkyCoord(1*u.deg, 2*u.deg, frame='icrs')
1244+
assert 'fakeattr' in dir(sc_after1)
1245+
assert sc_after1.fakeattr is None
1246+
1247+
sc_after2 = SkyCoord(1*u.deg, 2*u.deg, frame='icrs', fakeattr=1)
1248+
assert sc_after2.fakeattr == 1
1249+
finally:
1250+
frame_transform_graph.remove_transform(*transset)
1251+
1252+
assert 'fakeattr' not in dir(sc_before)
1253+
assert 'fakeattr' not in dir(sc_after1)
1254+
assert 'fakeattr' not in dir(sc_after2)

0 commit comments

Comments
 (0)