From 0f414970e7fe23a28c44587aac4003adefb2362b Mon Sep 17 00:00:00 2001 From: lmichel Date: Wed, 4 Sep 2024 16:21:05 +0200 Subject: [PATCH 01/14] Add the SkyCoord generation from the Mivot object --- docs/mivot/_images/mangoEpochPosition.png | Bin 54869 -> 63202 bytes docs/mivot/index.rst | 21 +++ pyvo/mivot/features/sky_coord_builder.py | 162 +++++++++++++++++++++ pyvo/mivot/tests/test_sky_coord_builder.py | 149 +++++++++++++++++++ pyvo/mivot/viewer/mivot_instance.py | 22 ++- pyvo/mivot/viewer/mivot_viewer.py | 1 + 6 files changed, 347 insertions(+), 8 deletions(-) create mode 100644 pyvo/mivot/features/sky_coord_builder.py create mode 100644 pyvo/mivot/tests/test_sky_coord_builder.py diff --git a/docs/mivot/_images/mangoEpochPosition.png b/docs/mivot/_images/mangoEpochPosition.png index 3d158c7362e47f0b23f159b2f51d1ba381c13061..04a4e748ff6f7a3554f7292cc225deac6efbb669 100644 GIT binary patch literal 63202 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

FClHZTem<{_G0suY)g%1 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 + +This feature works under the condition that the annotations contain a valid instance of ``mango:EPochPosition``. +Although not a standard at the time of writing, the class structure supported by this implementation must match the figure above. +If the annotation do no contain any vali object, `None` is returned. + For XML Hackers --------------- diff --git a/pyvo/mivot/features/sky_coord_builder.py b/pyvo/mivot/features/sky_coord_builder.py new file mode 100644 index 000000000..6b936b254 --- /dev/null +++ b/pyvo/mivot/features/sky_coord_builder.py @@ -0,0 +1,162 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +""" +Utility transforming MIVOT annotation into SKYCoord instances +""" + +from astropy.coordinates import SkyCoord +from astropy import units as u +from astropy.coordinates import ICRS, Galactic, FK4, FK5 + + +class MangoRoles: + """ + Place holder for the roles of the mango:EpochPosition roles + """ + LONGITUDE = "longitude" + LATITUDE = "latitude" + PM_LONGITUDE = "pmLongitude" + PM_LATITUDE = "pmLatitude" + PARALLAX = "parallax" + RADIAL_VELOCITY = "radialVelocity" + EPOCH = "epoch" + FRAME = "frame" + EQUINOX = "equinox" + PMCOSDELTAPPLIED = "pmCosDeltApplied" + + +# Mapping of the MANGO parameters on the SkyCoord parameters +skycoord_param_default = { + MangoRoles.LONGITUDE: 'ra', MangoRoles.LATITUDE: 'dec', MangoRoles.PARALLAX: 'distance', + MangoRoles.PM_LONGITUDE: 'pm_ra_cosdec', MangoRoles.PM_LATITUDE: 'pm_dec', + MangoRoles.RADIAL_VELOCITY: 'radial_velocity', MangoRoles.EPOCH: 'obstime'} + +skycoord_param_galactic = { + MangoRoles.LONGITUDE: 'l', MangoRoles.LATITUDE: 'b', MangoRoles.PARALLAX: 'distance', + MangoRoles.PM_LONGITUDE: 'pm_l_cosb', MangoRoles.PM_LATITUDE: 'pm_b', + MangoRoles.RADIAL_VELOCITY: 'radial_velocity', MangoRoles.EPOCH: 'obstime'} + + +class SkyCoordBuilder(object): + ''' + Utility generating SkyCoord instances from MIVOT annotations + + - SkyCoord instances can only be built from model classes containing the minimal + set of required parameters (a position). + - In this implementation, only the mango:EpochPosition class is supported since + it proposes anything required to compute the epoch propagation which is a major use-case + ''' + + def __init__(self, mivot_instance_dict): + ''' + Constructor + + parameters + ----------- + - mivot_instance_dict: viewer.MivotInstance.to_dict() + Internal dictionary of the dynamic Python object generated + from the MIVOT block + ''' + self._mivot_instance_dict = mivot_instance_dict + self._map_coord_names = None + + def build_sky_coord(self): + """ + Build a SkyCoord instance from the dynamic Python object attribute. + + returns + ------- + - SkyCoord instance or None + """ + if not self._mivot_instance_dict: + return None + if self._mivot_instance_dict["dmtype"] == "mango:EpochPosition": + return self._build_sky_coord_from_mango() + return None + + def _set_year_time_format(self, hk_field): + """ + Format a date expressed in year as J-year + + parameters + ---------- + - hk_field: MIVOT instance parameter as a dict + + returns + ------- + - formatted string + """ + return (f"j{hk_field['value']}" if hk_field["unit"] in ("yr", "year") + else hk_field["value"]) + + def _get_space_frame(self): + """ + Build an astropy space frame instance from the MIVOT annotations. + + - Equinox are supported fot FK4/5 + - Ref location are not supported + + returns + ------- + - Astropy space frame + """ + coo_sys = self._mivot_instance_dict["coordSys"] + equinox = None + frame = coo_sys["spaceRefFrame"]["value"].lower() + + if frame == 'fk4': + self._map_coord_names = skycoord_param_default + if "equinox" in coo_sys: + equinox = self._set_year_time_format(coo_sys["equinox"]) + return FK4(equinox=equinox) + return FK4() + + if frame == 'fk5': + self._map_coord_names = skycoord_param_default + if "equinox" in coo_sys: + equinox = self._set_year_time_format(coo_sys["equinox"]) + return FK5(equinox=equinox) + return FK5() + + if frame == 'galactic': + self._map_coord_names = skycoord_param_galactic + return Galactic() + + self._map_coord_names = skycoord_param_default + return ICRS() + + def _build_sky_coord_from_mango(self): + """ + Build silently a SlyCoord instance from the mango:EpochPosition instance. + No error is trapped, unconsistencies in the mango:EpochPosition instance will + raise Astropy errors. + + - The epoch (obstime) is meant to be given as J-year. + - ICRS frame is taken by default + - The cos-delta correction is meant to be applied. + The case mango:pmCosDeltApplied = False is not suppored yet + + returns + ------- + - SkyCoord instance + """ + kwargs = {} + kwargs["frame"] = self._get_space_frame() + + for key, value in self._map_coord_names.items(): + # ignore not set parameters + if key not in self._mivot_instance_dict: + continue + hk_field = self._mivot_instance_dict[key] + # format the observation time (J-year by default) + if value == "obstime": + kwargs[value] = self._set_year_time_format(hk_field) + # Convert the parallax (mango) into a distance + elif value == "distance": + kwargs[value] = (hk_field["value"] + * u.Unit(hk_field["unit"]).to(u.parsec, equivalencies=u.parallax())) + kwargs[value] = kwargs[value] * u.parsec + elif "unit" in hk_field and hk_field["unit"]: + kwargs[value] = hk_field["value"] * u.Unit(hk_field["unit"]) + else: + kwargs[value] = hk_field["value"] + return SkyCoord(**kwargs) diff --git a/pyvo/mivot/tests/test_sky_coord_builder.py b/pyvo/mivot/tests/test_sky_coord_builder.py new file mode 100644 index 000000000..22d84e678 --- /dev/null +++ b/pyvo/mivot/tests/test_sky_coord_builder.py @@ -0,0 +1,149 @@ +''' +The first service in operation the annotates query responses in the fly is Vizier +https://cds/viz-bin/mivotconesearch/VizierParams +Data are mapped on the mango:EpochPropagtion class as it is implemented in the current code. +This test case is based on 2 VOTables: +Both tests check the generation of SkyCoord instances from the MivotInstances buil +for the output of this service. +''' +import pytest +from pyvo.mivot.version_checker import check_astropy_version +from pyvo.mivot.viewer.mivot_instance import MivotInstance +from pyvo.mivot.features.sky_coord_builder import SkyCoordBuilder + +# annotations generated by Vizier as given to the MivotInstance +vizier_dict = { + "dmtype": "mango:EpochPosition", + "longitude": { + "dmtype": "ivoa:RealQuantity", + "value": 52.26722684, + "unit": "deg", + "ref": "RAICRS", + }, + "latitude": { + "dmtype": "ivoa:RealQuantity", + "value": 59.94033461, + "unit": "deg", + "ref": "DEICRS", + }, + "pmLongitude": { + "dmtype": "ivoa:RealQuantity", + "value": -0.82, + "unit": "mas/yr", + "ref": "pmRA", + }, + "pmLatitude": { + "dmtype": "ivoa:RealQuantity", + "value": -1.85, + "unit": "mas/yr", + "ref": "pmDE", + }, + "epoch": { + "dmtype": "ivoa:RealQuantity", + "value": 1991.25, + "unit": "yr", + "ref": None, + }, + "coordSys": { + "dmtype": "coords:SpaceSys", + "dmid": "SpaceFrame_ICRS", + "dmrole": "coords:Coordinate.coordSys", + "spaceRefFrame": { + "dmtype": "coords:SpaceFrame", + "value": "ICRS", + "unit": None, + "ref": None, + }, + }, +} +# The same edited by hand (parallax added and FK5 + Equinox frame) +vizier_equin_dict = { + "dmtype": "mango:EpochPosition", + "longitude": { + "dmtype": "ivoa:RealQuantity", + "value": 52.26722684, + "unit": "deg", + "ref": "RAICRS", + }, + "latitude": { + "dmtype": "ivoa:RealQuantity", + "value": 59.94033461, + "unit": "deg", + "ref": "DEICRS", + }, + "pmLongitude": { + "dmtype": "ivoa:RealQuantity", + "value": -0.82, + "unit": "mas/yr", + "ref": "pmRA", + }, + "pmLatitude": { + "dmtype": "ivoa:RealQuantity", + "value": -1.85, + "unit": "mas/yr", + "ref": "pmDE", + }, + "parallax": { + "dmtype": "ivoa:RealQuantity", + "value": 0.6, + "unit": "mas", + "ref": "parallax", + }, + "epoch": { + "dmtype": "ivoa:RealQuantity", + "value": 1991.25, + "unit": "yr", + "ref": None, + }, + "coordSys": { + "dmtype": "coords:SpaceSys", + "dmid": "SpaceFrame_ICRS", + "dmrole": "coords:Coordinate.coordSys", + "spaceRefFrame": { + "dmtype": "coords:SpaceFrame.spaceRefFrame", + "value": "FK5", + "unit": None, + "ref": None, + }, + "equinox": { + "dmtype": "coords:SpaceFrame.equinox", + "value": "2012", + "unit": "yr", + }, + }, +} + + +@pytest.mark.skipif(not check_astropy_version(), reason="need astropy 6+") +def test_vizier_output(): + """ Test the SkyCoord issued from the Vizier response + """ + mivot_instance = MivotInstance(**vizier_dict) + scb = SkyCoordBuilder(mivot_instance.to_dict()) + scoo = scb.build_sky_coord() + assert (str(scoo).replace("\n", "").replace(" ", "") + == "") + scoo = mivot_instance.get_SkyCoord() + assert (str(scoo).replace("\n", "").replace(" ", "") + == "") + + +@pytest.mark.skipif(not check_astropy_version(), reason="need astropy 6+") +def test_vizier_output_with_equinox_and_parallax(): + """Test the SkyCoord issued from the modofier Vizier response * + (parallax added and FK5 + Equinox frame) + """ + mivot_instance = MivotInstance(**vizier_equin_dict) + scb = SkyCoordBuilder(mivot_instance.to_dict()) + scoo = scb.build_sky_coord() + assert (str(scoo).replace("\n", "").replace(" ", "") + == "") + scoo = mivot_instance.get_SkyCoord() + assert (str(scoo).replace("\n", "").replace(" ", "") + == "") diff --git a/pyvo/mivot/viewer/mivot_instance.py b/pyvo/mivot/viewer/mivot_instance.py index 2b6a14c32..8502ac900 100644 --- a/pyvo/mivot/viewer/mivot_instance.py +++ b/pyvo/mivot/viewer/mivot_instance.py @@ -10,11 +10,12 @@ Although attribute values can be changed by users, this class is first meant to provide a convenient access the mapped VOTable data """ -from astropy import time -from pyvo.mivot.utils.vocabulary import unit_mapping, Constant +from pyvo.mivot.utils.vocabulary import Constant from pyvo.utils.prototype import prototype_feature from pyvo.mivot.utils.mivot_utils import MivotUtils from pyvo.mivot.utils.dict_utils import DictUtils +from pyvo.mivot.features.sky_coord_builder import SkyCoordBuilder + # list of model leaf parameters that must be hidden for the final user hk_parameters = ["astropy_unit", "ref"] @@ -53,7 +54,7 @@ def to_hk_dict(self): housekeeping data such as column references. This might be used to apply the mapping out of the MivotViewer context """ - return self._get_class_dict(self) + return self._get_class_dict(self, slim=False) def to_dict(self): """ @@ -98,10 +99,6 @@ def _create_class(self, **kwargs): if key == 'unit': # We convert the unit to astropy unit or to astropy time format if possible # The first Vizier implementation used mas/year for the mapped pm unit: let's correct it value = value.replace("year", "yr") if value else None - if value in unit_mapping.keys(): - setattr(self, "astropy_unit", unit_mapping[value]) - elif value in time.TIME_FORMATS.keys(): - setattr(self, "astropy_unit_time", value) def update(self, row, ref=None): """ @@ -128,6 +125,14 @@ def update(self, row, ref=None): setattr(self, self._remove_model_name(key), MivotUtils.cast_type_value(row[ref], getattr(self, 'dmtype'))) + def get_SkyCoord(self): + """ + returns + ------- + - a SkyCoord instance or None + """ + return SkyCoordBuilder(self.to_dict()).build_sky_coord() + @staticmethod def _remove_model_name(value): """ @@ -178,6 +183,7 @@ def _get_class_dict(self, obj, classkey=None, slim=False): dict or object The serializable dictionary representation of the input. """ + if isinstance(obj, dict): data = {} for (k, v) in obj.items(): @@ -188,7 +194,7 @@ def _get_class_dict(self, obj, classkey=None, slim=False): elif hasattr(obj, "__iter__") and not isinstance(obj, str): return [self._get_class_dict(v, classkey, slim=slim) for v in obj] elif hasattr(obj, "__dict__"): - data = dict([(key, self._get_class_dict(value, classkey, slim=slim)) + data = dict([(key, obj._get_class_dict(value, classkey, slim=slim)) for key, value in obj.__dict__.items() if not callable(value) and not key.startswith('_')]) # remove the house keeping parameters diff --git a/pyvo/mivot/viewer/mivot_viewer.py b/pyvo/mivot/viewer/mivot_viewer.py index a6f1224f7..08202c30b 100644 --- a/pyvo/mivot/viewer/mivot_viewer.py +++ b/pyvo/mivot/viewer/mivot_viewer.py @@ -93,6 +93,7 @@ def __init__(self, votable_path, tableref=None): self._mapped_tables = [] self._resource_seeker = None self._dm_instance = None + self._sky_coord = None try: self._set_resource() self._set_mapping_block() From df9aa22f6d1d49ae500d6a1cb4319019b6e3f3f6 Mon Sep 17 00:00:00 2001 From: lmichel Date: Wed, 4 Sep 2024 16:24:11 +0200 Subject: [PATCH 02/14] remove unused attribute --- pyvo/mivot/viewer/mivot_viewer.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pyvo/mivot/viewer/mivot_viewer.py b/pyvo/mivot/viewer/mivot_viewer.py index 08202c30b..a6f1224f7 100644 --- a/pyvo/mivot/viewer/mivot_viewer.py +++ b/pyvo/mivot/viewer/mivot_viewer.py @@ -93,7 +93,6 @@ def __init__(self, votable_path, tableref=None): self._mapped_tables = [] self._resource_seeker = None self._dm_instance = None - self._sky_coord = None try: self._set_resource() self._set_mapping_block() From 7b906fb31487bcae6a84a8d2ef6f2330ee901508 Mon Sep 17 00:00:00 2001 From: lmichel Date: Fri, 6 Sep 2024 09:50:03 +0200 Subject: [PATCH 03/14] Fix for the PR --- docs/mivot/index.rst | 2 +- pyvo/mivot/features/sky_coord_builder.py | 29 +++++++++++++++------- pyvo/mivot/tests/test_mivot_instance.py | 2 -- pyvo/mivot/tests/test_sky_coord_builder.py | 19 +++++++++++++- pyvo/mivot/tests/test_user_api.py | 4 --- pyvo/mivot/viewer/mivot_instance.py | 4 +-- 6 files changed, 41 insertions(+), 19 deletions(-) diff --git a/docs/mivot/index.rst b/docs/mivot/index.rst index b444d1fdf..ce1f50a24 100644 --- a/docs/mivot/index.rst +++ b/docs/mivot/index.rst @@ -165,7 +165,7 @@ with the `astropy.io.votable` API: In this case, it is up to the user to ensure that the read data rows are those mapped by the Mivot annotations. Get a SkyCoord Instance Directly From the Annotations -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +----------------------------------------------------- Once you get a ``MivotInstance`` representing the last row read, you can use it to create an ``astropy.SkyCoord`` object. diff --git a/pyvo/mivot/features/sky_coord_builder.py b/pyvo/mivot/features/sky_coord_builder.py index 6b936b254..3711034be 100644 --- a/pyvo/mivot/features/sky_coord_builder.py +++ b/pyvo/mivot/features/sky_coord_builder.py @@ -67,13 +67,11 @@ def build_sky_coord(self): ------- - SkyCoord instance or None """ - if not self._mivot_instance_dict: - return None - if self._mivot_instance_dict["dmtype"] == "mango:EpochPosition": + if self._mivot_instance_dict and self._mivot_instance_dict["dmtype"] == "mango:EpochPosition": return self._build_sky_coord_from_mango() return None - def _set_year_time_format(self, hk_field): + def _set_year_time_format(self, hk_field, besselian=False): """ Format a date expressed in year as J-year @@ -85,16 +83,22 @@ def _set_year_time_format(self, hk_field): ------- - formatted string """ - return (f"j{hk_field['value']}" if hk_field["unit"] in ("yr", "year") + scale = "J" if not besselian else "B" + return (f"{scale}{hk_field['value']}" if hk_field["unit"] in ("yr", "year") else hk_field["value"]) - def _get_space_frame(self): + def _get_space_frame(self, obstime=None): """ Build an astropy space frame instance from the MIVOT annotations. - Equinox are supported fot FK4/5 - Ref location are not supported + parameters + ---------- + - obstime: string + Observation time is given here because KF4 set an default value + if it is not given returns ------- - Astropy space frame @@ -106,8 +110,10 @@ def _get_space_frame(self): if frame == 'fk4': self._map_coord_names = skycoord_param_default if "equinox" in coo_sys: - equinox = self._set_year_time_format(coo_sys["equinox"]) - return FK4(equinox=equinox) + equinox = self._set_year_time_format(coo_sys["equinox"], True) + print(coo_sys["equinox"]) + print(FK4(equinox=equinox, obstime="B345")) + return FK4(equinox=equinox, obstime=obstime) return FK4() if frame == 'fk5': @@ -149,7 +155,12 @@ def _build_sky_coord_from_mango(self): hk_field = self._mivot_instance_dict[key] # format the observation time (J-year by default) if value == "obstime": - kwargs[value] = self._set_year_time_format(hk_field) + # obstime must be set into the KK4 frame but not as an input parameter + fobstime = self._set_year_time_format(hk_field) + if isinstance(kwargs["frame"], FK4): + kwargs["frame"] = self._get_space_frame(obstime=fobstime) + else: + kwargs[value] = fobstime # Convert the parallax (mango) into a distance elif value == "distance": kwargs[value] = (hk_field["value"] diff --git a/pyvo/mivot/tests/test_mivot_instance.py b/pyvo/mivot/tests/test_mivot_instance.py index d79fae900..1e314bf44 100644 --- a/pyvo/mivot/tests/test_mivot_instance.py +++ b/pyvo/mivot/tests/test_mivot_instance.py @@ -19,14 +19,12 @@ "dmtype": "RealQuantity", "value": 52.2340018, "unit": "deg", - "astropy_unit": {}, "ref": "RAICRS" }, "latitude": { "dmtype": "RealQuantity", "value": 59.8937333, "unit": "deg", - "astropy_unit": {}, "ref": "DEICRS" } } diff --git a/pyvo/mivot/tests/test_sky_coord_builder.py b/pyvo/mivot/tests/test_sky_coord_builder.py index 22d84e678..06088985c 100644 --- a/pyvo/mivot/tests/test_sky_coord_builder.py +++ b/pyvo/mivot/tests/test_sky_coord_builder.py @@ -129,6 +129,20 @@ def test_vizier_output(): == "") + vizier_dict["coordSys"]["spaceRefFrame"]["value"] = "Galactic" + mivot_instance = MivotInstance(**vizier_dict) + scoo = mivot_instance.get_SkyCoord() + assert (str(scoo).replace("\n", "").replace(" ", "") + == "") + + vizier_dict["coordSys"]["spaceRefFrame"]["value"] = "QWERTY" + mivot_instance = MivotInstance(**vizier_dict) + scoo = mivot_instance.get_SkyCoord() + assert (str(scoo).replace("\n", "").replace(" ", "") + == "") + @pytest.mark.skipif(not check_astropy_version(), reason="need astropy 6+") def test_vizier_output_with_equinox_and_parallax(): @@ -142,8 +156,11 @@ def test_vizier_output_with_equinox_and_parallax(): == "") + + vizier_equin_dict["coordSys"]["spaceRefFrame"]["value"] = "FK4" + mivot_instance = MivotInstance(**vizier_equin_dict) scoo = mivot_instance.get_SkyCoord() assert (str(scoo).replace("\n", "").replace(" ", "") - == "") diff --git a/pyvo/mivot/tests/test_user_api.py b/pyvo/mivot/tests/test_user_api.py index 8ed6e9141..04277c0e6 100644 --- a/pyvo/mivot/tests/test_user_api.py +++ b/pyvo/mivot/tests/test_user_api.py @@ -234,28 +234,24 @@ def test_with_dict(path_to_votable): "dmtype": "ivoa:RealQuantity", "value": 359.94372764, "unit": "deg", - "astropy_unit": {}, "ref": "RAICRS", }, "latitude": { "dmtype": "ivoa:RealQuantity", "value": -0.28005255, "unit": "deg", - "astropy_unit": {}, "ref": "DEICRS", }, "pmLongitude": { "dmtype": "ivoa:RealQuantity", "value": -5.14, "unit": "mas/yr", - "astropy_unit": {}, "ref": "pmRA", }, "pmLatitude": { "dmtype": "ivoa:RealQuantity", "value": -25.43, "unit": "mas/yr", - "astropy_unit": {}, "ref": "pmDE", }, "epoch": { diff --git a/pyvo/mivot/viewer/mivot_instance.py b/pyvo/mivot/viewer/mivot_instance.py index 8502ac900..47cfacdea 100644 --- a/pyvo/mivot/viewer/mivot_instance.py +++ b/pyvo/mivot/viewer/mivot_instance.py @@ -18,7 +18,7 @@ # list of model leaf parameters that must be hidden for the final user -hk_parameters = ["astropy_unit", "ref"] +hk_parameters = ["ref"] @prototype_feature('MIVOT') @@ -126,7 +126,7 @@ def update(self, row, ref=None): MivotUtils.cast_type_value(row[ref], getattr(self, 'dmtype'))) def get_SkyCoord(self): - """ + """ returns ------- - a SkyCoord instance or None From a8c76245fb1072017f9ab290a09034b44dc86b83 Mon Sep 17 00:00:00 2001 From: lmichel Date: Fri, 6 Sep 2024 09:51:27 +0200 Subject: [PATCH 04/14] Fix for the PR --- pyvo/mivot/features/sky_coord_builder.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pyvo/mivot/features/sky_coord_builder.py b/pyvo/mivot/features/sky_coord_builder.py index 3711034be..2db2111c0 100644 --- a/pyvo/mivot/features/sky_coord_builder.py +++ b/pyvo/mivot/features/sky_coord_builder.py @@ -69,7 +69,6 @@ def build_sky_coord(self): """ if self._mivot_instance_dict and self._mivot_instance_dict["dmtype"] == "mango:EpochPosition": return self._build_sky_coord_from_mango() - return None def _set_year_time_format(self, hk_field, besselian=False): """ From 094b51928b18b68689374442002066f6cbf2a24b Mon Sep 17 00:00:00 2001 From: Laurent MICHEL Date: Thu, 19 Sep 2024 17:43:18 +0200 Subject: [PATCH 05/14] Typos in comment corrected (#591) --- pyvo/mivot/features/sky_coord_builder.py | 2 +- pyvo/mivot/tests/test_sky_coord_builder.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyvo/mivot/features/sky_coord_builder.py b/pyvo/mivot/features/sky_coord_builder.py index 2db2111c0..b07f70658 100644 --- a/pyvo/mivot/features/sky_coord_builder.py +++ b/pyvo/mivot/features/sky_coord_builder.py @@ -131,7 +131,7 @@ def _get_space_frame(self, obstime=None): def _build_sky_coord_from_mango(self): """ - Build silently a SlyCoord instance from the mango:EpochPosition instance. + Build silently a SkyCoord instance from the mango:EpochPosition instance. No error is trapped, unconsistencies in the mango:EpochPosition instance will raise Astropy errors. diff --git a/pyvo/mivot/tests/test_sky_coord_builder.py b/pyvo/mivot/tests/test_sky_coord_builder.py index 06088985c..0a235c506 100644 --- a/pyvo/mivot/tests/test_sky_coord_builder.py +++ b/pyvo/mivot/tests/test_sky_coord_builder.py @@ -3,7 +3,7 @@ https://cds/viz-bin/mivotconesearch/VizierParams Data are mapped on the mango:EpochPropagtion class as it is implemented in the current code. This test case is based on 2 VOTables: -Both tests check the generation of SkyCoord instances from the MivotInstances buil +Both tests check the generation of SkyCoord instances from the MivotInstances built for the output of this service. ''' import pytest From 0afbd2aed3f51df3be9a44d1a5456a05f33621ea Mon Sep 17 00:00:00 2001 From: Laurent MICHEL Date: Thu, 19 Sep 2024 17:43:48 +0200 Subject: [PATCH 06/14] Typos corrected and unused import removed (#591) --- docs/mivot/index.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/mivot/index.rst b/docs/mivot/index.rst index ce1f50a24..587c14552 100644 --- a/docs/mivot/index.rst +++ b/docs/mivot/index.rst @@ -173,7 +173,6 @@ Once you get a ``MivotInstance`` representing the last row read, you can use it :caption: Accessing the model view of Astropy table rows from pyvo.mivot import MivotViewer - from pyvo.mivot.utils.dict_utils import DictUtils m_viewer = MivotViewer(path_to_votable) mivot_instance = m_viewer.dm_instance @@ -183,7 +182,7 @@ Once you get a ``MivotInstance`` representing the last row read, you can use it This feature works under the condition that the annotations contain a valid instance of ``mango:EPochPosition``. Although not a standard at the time of writing, the class structure supported by this implementation must match the figure above. -If the annotation do no contain any vali object, `None` is returned. +If the annotation do no contain any valid object, `None` is returned. For XML Hackers --------------- From e0f61e7524f125e89e84aa219f7f5c4f743f6b4f Mon Sep 17 00:00:00 2001 From: Laurent MICHEL Date: Tue, 24 Sep 2024 16:37:01 +0200 Subject: [PATCH 07/14] Damian's review + change log --- CHANGES.rst | 3 +++ docs/mivot/index.rst | 2 +- pyvo/mivot/features/sky_coord_builder.py | 4 +--- pyvo/mivot/tests/test_sky_coord_builder.py | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index b7c78bfbc..12be5c7f3 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -45,6 +45,9 @@ Enhancements and Fixes - Tables returned by RegistryResource.get_tables() now have a utype attribute [#576] + +- MIVOT module: If the MIVOT annotation block contains a valid instance of the ``mango:EpochPosition` class, + the dynamic object describing the mapped data can generate a valid SkyCoord instance. [#591] Deprecations and Removals ------------------------- diff --git a/docs/mivot/index.rst b/docs/mivot/index.rst index 587c14552..506a5689d 100644 --- a/docs/mivot/index.rst +++ b/docs/mivot/index.rst @@ -182,7 +182,7 @@ Once you get a ``MivotInstance`` representing the last row read, you can use it This feature works under the condition that the annotations contain a valid instance of ``mango:EPochPosition``. Although not a standard at the time of writing, the class structure supported by this implementation must match the figure above. -If the annotation do no contain any valid object, `None` is returned. +If the annotations do not contain any valid object, `None` is returned. For XML Hackers --------------- diff --git a/pyvo/mivot/features/sky_coord_builder.py b/pyvo/mivot/features/sky_coord_builder.py index b07f70658..2a9abfe9a 100644 --- a/pyvo/mivot/features/sky_coord_builder.py +++ b/pyvo/mivot/features/sky_coord_builder.py @@ -43,7 +43,7 @@ class SkyCoordBuilder(object): - SkyCoord instances can only be built from model classes containing the minimal set of required parameters (a position). - In this implementation, only the mango:EpochPosition class is supported since - it proposes anything required to compute the epoch propagation which is a major use-case + it contains the information required to compute the epoch propagation which is a major use-case ''' def __init__(self, mivot_instance_dict): @@ -110,8 +110,6 @@ def _get_space_frame(self, obstime=None): self._map_coord_names = skycoord_param_default if "equinox" in coo_sys: equinox = self._set_year_time_format(coo_sys["equinox"], True) - print(coo_sys["equinox"]) - print(FK4(equinox=equinox, obstime="B345")) return FK4(equinox=equinox, obstime=obstime) return FK4() diff --git a/pyvo/mivot/tests/test_sky_coord_builder.py b/pyvo/mivot/tests/test_sky_coord_builder.py index 0a235c506..79ed54a36 100644 --- a/pyvo/mivot/tests/test_sky_coord_builder.py +++ b/pyvo/mivot/tests/test_sky_coord_builder.py @@ -1,5 +1,5 @@ ''' -The first service in operation the annotates query responses in the fly is Vizier +The first service in operation that annotates query responses in the fly is Vizier https://cds/viz-bin/mivotconesearch/VizierParams Data are mapped on the mango:EpochPropagtion class as it is implemented in the current code. This test case is based on 2 VOTables: From aad09bed9a094e7685e2cd2ea62e45c0e6cab539 Mon Sep 17 00:00:00 2001 From: Laurent MICHEL Date: Tue, 24 Sep 2024 16:46:16 +0200 Subject: [PATCH 08/14] fix CI --- CHANGES.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 12be5c7f3..e4b6f7758 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -46,7 +46,7 @@ Enhancements and Fixes - Tables returned by RegistryResource.get_tables() now have a utype attribute [#576] -- MIVOT module: If the MIVOT annotation block contains a valid instance of the ``mango:EpochPosition` class, +- MIVOT module: If the MIVOT annotation block contains a valid instance of the ``mango:EpochPosition`` class, the dynamic object describing the mapped data can generate a valid SkyCoord instance. [#591] Deprecations and Removals From 833d9f1c252afe3c64dfcf772acd5c3f6af71dc4 Mon Sep 17 00:00:00 2001 From: lmichel Date: Mon, 30 Sep 2024 09:19:34 +0200 Subject: [PATCH 09/14] raise a exception (NoMatchingDMType) when the API miss a Model object --- CHANGES.rst | 3 + docs/mivot/index.rst | 2 +- pyvo/mivot/features/sky_coord_builder.py | 9 ++- pyvo/mivot/tests/test_sky_coord_builder.py | 68 ++++++++++++++++++++++ pyvo/mivot/utils/exceptions.py | 10 ++++ 5 files changed, 90 insertions(+), 2 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index b7c78bfbc..42a08a399 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -46,6 +46,9 @@ Enhancements and Fixes - Tables returned by RegistryResource.get_tables() now have a utype attribute [#576] +- The MIVOT model is able to automatically build SkyCoord instance from ``mango:EpochPosition`` instances [#591] + + Deprecations and Removals ------------------------- diff --git a/docs/mivot/index.rst b/docs/mivot/index.rst index ce1f50a24..91c68127b 100644 --- a/docs/mivot/index.rst +++ b/docs/mivot/index.rst @@ -183,7 +183,7 @@ Once you get a ``MivotInstance`` representing the last row read, you can use it This feature works under the condition that the annotations contain a valid instance of ``mango:EPochPosition``. Although not a standard at the time of writing, the class structure supported by this implementation must match the figure above. -If the annotation do no contain any vali object, `None` is returned. +If the annotation do no contain any valid object, a ``NoMatchingDMType`` exception is thrown. For XML Hackers --------------- diff --git a/pyvo/mivot/features/sky_coord_builder.py b/pyvo/mivot/features/sky_coord_builder.py index 2db2111c0..d3ed6dcfe 100644 --- a/pyvo/mivot/features/sky_coord_builder.py +++ b/pyvo/mivot/features/sky_coord_builder.py @@ -6,6 +6,7 @@ from astropy.coordinates import SkyCoord from astropy import units as u from astropy.coordinates import ICRS, Galactic, FK4, FK5 +from pyvo.mivot.utils.exceptions import NoMatchingDMType class MangoRoles: @@ -65,10 +66,16 @@ def build_sky_coord(self): returns ------- - - SkyCoord instance or None + - SkyCoord instance + + raises + ------ + - NoMatchingDMType: if the SkyCoord instance cannot be built. """ if self._mivot_instance_dict and self._mivot_instance_dict["dmtype"] == "mango:EpochPosition": return self._build_sky_coord_from_mango() + raise NoMatchingDMType("No INSTANCE with dmtype='mango:EpochPosition' has been found:" + " cannot build a SkyCoord from annotations") def _set_year_time_format(self, hk_field, besselian=False): """ diff --git a/pyvo/mivot/tests/test_sky_coord_builder.py b/pyvo/mivot/tests/test_sky_coord_builder.py index 06088985c..e0618a992 100644 --- a/pyvo/mivot/tests/test_sky_coord_builder.py +++ b/pyvo/mivot/tests/test_sky_coord_builder.py @@ -10,6 +10,7 @@ from pyvo.mivot.version_checker import check_astropy_version from pyvo.mivot.viewer.mivot_instance import MivotInstance from pyvo.mivot.features.sky_coord_builder import SkyCoordBuilder +from pyvo.mivot.utils.exceptions import NoMatchingDMType # annotations generated by Vizier as given to the MivotInstance vizier_dict = { @@ -113,6 +114,73 @@ }, } +# The same edited mapped on a dummy class +vizier_dummy_type = { + "dmtype": "mango:DumyType", + "longitude": { + "dmtype": "ivoa:RealQuantity", + "value": 52.26722684, + "unit": "deg", + "ref": "RAICRS", + }, + "latitude": { + "dmtype": "ivoa:RealQuantity", + "value": 59.94033461, + "unit": "deg", + "ref": "DEICRS", + }, + "pmLongitude": { + "dmtype": "ivoa:RealQuantity", + "value": -0.82, + "unit": "mas/yr", + "ref": "pmRA", + }, + "pmLatitude": { + "dmtype": "ivoa:RealQuantity", + "value": -1.85, + "unit": "mas/yr", + "ref": "pmDE", + }, + "parallax": { + "dmtype": "ivoa:RealQuantity", + "value": 0.6, + "unit": "mas", + "ref": "parallax", + }, + "epoch": { + "dmtype": "ivoa:RealQuantity", + "value": 1991.25, + "unit": "yr", + "ref": None, + }, + "coordSys": { + "dmtype": "coords:SpaceSys", + "dmid": "SpaceFrame_ICRS", + "dmrole": "coords:Coordinate.coordSys", + "spaceRefFrame": { + "dmtype": "coords:SpaceFrame.spaceRefFrame", + "value": "FK5", + "unit": None, + "ref": None, + }, + "equinox": { + "dmtype": "coords:SpaceFrame.equinox", + "value": "2012", + "unit": "yr", + }, + }, +} + + +def test_no_matching_mapping(): + """ + Test that a NoMatchingDMType is raised not mapped on mango:EpochPosition + """ + with pytest.raises(NoMatchingDMType): + mivot_instance = MivotInstance(**vizier_dummy_type) + scb = SkyCoordBuilder(mivot_instance.to_dict()) + scb.build_sky_coord() + @pytest.mark.skipif(not check_astropy_version(), reason="need astropy 6+") def test_vizier_output(): diff --git a/pyvo/mivot/utils/exceptions.py b/pyvo/mivot/utils/exceptions.py index 624e88680..049ba8a33 100644 --- a/pyvo/mivot/utils/exceptions.py +++ b/pyvo/mivot/utils/exceptions.py @@ -25,6 +25,16 @@ class MappingException(Exception): """ +class NoMatchingDMType(Exception): + """ + Exception thrown when some PyVO code misses MIVOT element: + - When trying to build a SkyCoord while there is no position in the annotations + - is mapped to a model unknown to the PyVO code. + This exception is never caught by the mivot package. + It must be handled by the calling code. + """ + + class AstropyVersionException(Exception): """ Exception raised if the version of astropy is not compatible with MIVOT. From 9a7263fcbf69340c56e9b70938d2b1f36fad7763 Mon Sep 17 00:00:00 2001 From: Laurent MICHEL Date: Thu, 19 Sep 2024 17:43:18 +0200 Subject: [PATCH 10/14] Damian's review + change log --- CHANGES.rst | 3 +++ docs/mivot/index.rst | 1 - pyvo/mivot/features/sky_coord_builder.py | 6 ++---- pyvo/mivot/tests/test_sky_coord_builder.py | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 42a08a399..1b7daaa5f 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -45,6 +45,9 @@ Enhancements and Fixes - Tables returned by RegistryResource.get_tables() now have a utype attribute [#576] + +- MIVOT module: If the MIVOT annotation block contains a valid instance of the ``mango:EpochPosition` class, + the dynamic object describing the mapped data can generate a valid SkyCoord instance. [#591] - The MIVOT model is able to automatically build SkyCoord instance from ``mango:EpochPosition`` instances [#591] diff --git a/docs/mivot/index.rst b/docs/mivot/index.rst index 91c68127b..82227ee16 100644 --- a/docs/mivot/index.rst +++ b/docs/mivot/index.rst @@ -173,7 +173,6 @@ Once you get a ``MivotInstance`` representing the last row read, you can use it :caption: Accessing the model view of Astropy table rows from pyvo.mivot import MivotViewer - from pyvo.mivot.utils.dict_utils import DictUtils m_viewer = MivotViewer(path_to_votable) mivot_instance = m_viewer.dm_instance diff --git a/pyvo/mivot/features/sky_coord_builder.py b/pyvo/mivot/features/sky_coord_builder.py index d3ed6dcfe..acbd7ac24 100644 --- a/pyvo/mivot/features/sky_coord_builder.py +++ b/pyvo/mivot/features/sky_coord_builder.py @@ -44,7 +44,7 @@ class SkyCoordBuilder(object): - SkyCoord instances can only be built from model classes containing the minimal set of required parameters (a position). - In this implementation, only the mango:EpochPosition class is supported since - it proposes anything required to compute the epoch propagation which is a major use-case + it contains the information required to compute the epoch propagation which is a major use-case ''' def __init__(self, mivot_instance_dict): @@ -117,8 +117,6 @@ def _get_space_frame(self, obstime=None): self._map_coord_names = skycoord_param_default if "equinox" in coo_sys: equinox = self._set_year_time_format(coo_sys["equinox"], True) - print(coo_sys["equinox"]) - print(FK4(equinox=equinox, obstime="B345")) return FK4(equinox=equinox, obstime=obstime) return FK4() @@ -138,7 +136,7 @@ def _get_space_frame(self, obstime=None): def _build_sky_coord_from_mango(self): """ - Build silently a SlyCoord instance from the mango:EpochPosition instance. + Build silently a SkyCoord instance from the mango:EpochPosition instance. No error is trapped, unconsistencies in the mango:EpochPosition instance will raise Astropy errors. diff --git a/pyvo/mivot/tests/test_sky_coord_builder.py b/pyvo/mivot/tests/test_sky_coord_builder.py index e0618a992..e10e78253 100644 --- a/pyvo/mivot/tests/test_sky_coord_builder.py +++ b/pyvo/mivot/tests/test_sky_coord_builder.py @@ -1,9 +1,9 @@ ''' -The first service in operation the annotates query responses in the fly is Vizier +The first service in operation that annotates query responses in the fly is Vizier https://cds/viz-bin/mivotconesearch/VizierParams Data are mapped on the mango:EpochPropagtion class as it is implemented in the current code. This test case is based on 2 VOTables: -Both tests check the generation of SkyCoord instances from the MivotInstances buil +Both tests check the generation of SkyCoord instances from the MivotInstances built for the output of this service. ''' import pytest From cfdac89516c3a4bc0090ac8307384a61c31edb8a Mon Sep 17 00:00:00 2001 From: Laurent MICHEL Date: Tue, 24 Sep 2024 16:46:16 +0200 Subject: [PATCH 11/14] fix CI --- CHANGES.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 1b7daaa5f..5c58eddb9 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -46,7 +46,7 @@ Enhancements and Fixes - Tables returned by RegistryResource.get_tables() now have a utype attribute [#576] -- MIVOT module: If the MIVOT annotation block contains a valid instance of the ``mango:EpochPosition` class, +- MIVOT module: If the MIVOT annotation block contains a valid instance of the ``mango:EpochPosition`` class, the dynamic object describing the mapped data can generate a valid SkyCoord instance. [#591] - The MIVOT model is able to automatically build SkyCoord instance from ``mango:EpochPosition`` instances [#591] From c614ee40c661521b274b8fb73677e9356b1cd4a6 Mon Sep 17 00:00:00 2001 From: lmichel Date: Tue, 1 Oct 2024 09:28:11 +0200 Subject: [PATCH 12/14] NoMatchingDMType renamed as NoMatchingDMTypeError --- docs/mivot/index.rst | 3 ++- pyvo/mivot/features/sky_coord_builder.py | 6 +++--- pyvo/mivot/tests/test_sky_coord_builder.py | 6 +++--- pyvo/mivot/utils/exceptions.py | 2 +- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/docs/mivot/index.rst b/docs/mivot/index.rst index f19bae385..c86b6df8a 100644 --- a/docs/mivot/index.rst +++ b/docs/mivot/index.rst @@ -180,7 +180,8 @@ Once you get a ``MivotInstance`` representing the last row read, you can use it -This feature works under the condition that the annotations contain a valid instance of ``mango:EPochPosition``. +This feature works under the condition that the annotations contain a valid instance of ``mango:EPochPosition``, otherwise +a ``NoMatchingDMTypeError`` is thrown. Although not a standard at the time of writing, the class structure supported by this implementation must match the figure above. diff --git a/pyvo/mivot/features/sky_coord_builder.py b/pyvo/mivot/features/sky_coord_builder.py index acbd7ac24..898353c51 100644 --- a/pyvo/mivot/features/sky_coord_builder.py +++ b/pyvo/mivot/features/sky_coord_builder.py @@ -6,7 +6,7 @@ from astropy.coordinates import SkyCoord from astropy import units as u from astropy.coordinates import ICRS, Galactic, FK4, FK5 -from pyvo.mivot.utils.exceptions import NoMatchingDMType +from pyvo.mivot.utils.exceptions import NoMatchingDMTypeError class MangoRoles: @@ -70,11 +70,11 @@ def build_sky_coord(self): raises ------ - - NoMatchingDMType: if the SkyCoord instance cannot be built. + - NoMatchingDMTypeError: if the SkyCoord instance cannot be built. """ if self._mivot_instance_dict and self._mivot_instance_dict["dmtype"] == "mango:EpochPosition": return self._build_sky_coord_from_mango() - raise NoMatchingDMType("No INSTANCE with dmtype='mango:EpochPosition' has been found:" + raise NoMatchingDMTypeError("No INSTANCE with dmtype='mango:EpochPosition' has been found:" " cannot build a SkyCoord from annotations") def _set_year_time_format(self, hk_field, besselian=False): diff --git a/pyvo/mivot/tests/test_sky_coord_builder.py b/pyvo/mivot/tests/test_sky_coord_builder.py index e10e78253..346bc6769 100644 --- a/pyvo/mivot/tests/test_sky_coord_builder.py +++ b/pyvo/mivot/tests/test_sky_coord_builder.py @@ -10,7 +10,7 @@ from pyvo.mivot.version_checker import check_astropy_version from pyvo.mivot.viewer.mivot_instance import MivotInstance from pyvo.mivot.features.sky_coord_builder import SkyCoordBuilder -from pyvo.mivot.utils.exceptions import NoMatchingDMType +from pyvo.mivot.utils.exceptions import NoMatchingDMTypeError # annotations generated by Vizier as given to the MivotInstance vizier_dict = { @@ -174,9 +174,9 @@ def test_no_matching_mapping(): """ - Test that a NoMatchingDMType is raised not mapped on mango:EpochPosition + Test that a NoMatchingDMTypeError is raised not mapped on mango:EpochPosition """ - with pytest.raises(NoMatchingDMType): + with pytest.raises(NoMatchingDMTypeError): mivot_instance = MivotInstance(**vizier_dummy_type) scb = SkyCoordBuilder(mivot_instance.to_dict()) scb.build_sky_coord() diff --git a/pyvo/mivot/utils/exceptions.py b/pyvo/mivot/utils/exceptions.py index 049ba8a33..7e688b57c 100644 --- a/pyvo/mivot/utils/exceptions.py +++ b/pyvo/mivot/utils/exceptions.py @@ -25,7 +25,7 @@ class MappingException(Exception): """ -class NoMatchingDMType(Exception): +class NoMatchingDMTypeError(Exception): """ Exception thrown when some PyVO code misses MIVOT element: - When trying to build a SkyCoord while there is no position in the annotations From f60533be7ab126a3d604ab69eab636acdb566bdc Mon Sep 17 00:00:00 2001 From: lmichel Date: Tue, 1 Oct 2024 11:00:41 +0200 Subject: [PATCH 13/14] Make docstrings compliant with the numpy style, make some comments more explicit --- pyvo/mivot/features/sky_coord_builder.py | 60 ++++++++++++++---------- 1 file changed, 35 insertions(+), 25 deletions(-) diff --git a/pyvo/mivot/features/sky_coord_builder.py b/pyvo/mivot/features/sky_coord_builder.py index 898353c51..7bdf48774 100644 --- a/pyvo/mivot/features/sky_coord_builder.py +++ b/pyvo/mivot/features/sky_coord_builder.py @@ -1,6 +1,6 @@ # Licensed under a 3-clause BSD style license - see LICENSE.rst """ -Utility transforming MIVOT annotation into SKYCoord instances +Utility transforming MIVOT annotation into SkyCoord instances """ from astropy.coordinates import SkyCoord @@ -11,7 +11,7 @@ class MangoRoles: """ - Place holder for the roles of the mango:EpochPosition roles + Place holder for the roles (attribute names) of the mango:EpochPosition class """ LONGITUDE = "longitude" LATITUDE = "latitude" @@ -42,7 +42,7 @@ class SkyCoordBuilder(object): Utility generating SkyCoord instances from MIVOT annotations - SkyCoord instances can only be built from model classes containing the minimal - set of required parameters (a position). + set of required parameters (a position). - In this implementation, only the mango:EpochPosition class is supported since it contains the information required to compute the epoch propagation which is a major use-case ''' @@ -53,41 +53,49 @@ def __init__(self, mivot_instance_dict): parameters ----------- - - mivot_instance_dict: viewer.MivotInstance.to_dict() - Internal dictionary of the dynamic Python object generated - from the MIVOT block + mivot_instance_dict: viewer.MivotInstance.to_dict() + Internal dictionary of the dynamic Python object generated from the MIVOT block ''' self._mivot_instance_dict = mivot_instance_dict self._map_coord_names = None def build_sky_coord(self): """ - Build a SkyCoord instance from the dynamic Python object attribute. + Build a SkyCoord instance from the MivotInstance dictionary. + The operation requires the dictionary to have ``mango:EpochPosition`` as dmtype + This is a public method which could be extended to support other dmtypes. returns ------- - - SkyCoord instance + SkyCoord + Instance built by the method raises ------ - - NoMatchingDMTypeError: if the SkyCoord instance cannot be built. + NoMatchingDMTypeError + if the SkyCoord instance cannot be built. """ if self._mivot_instance_dict and self._mivot_instance_dict["dmtype"] == "mango:EpochPosition": return self._build_sky_coord_from_mango() - raise NoMatchingDMTypeError("No INSTANCE with dmtype='mango:EpochPosition' has been found:" - " cannot build a SkyCoord from annotations") + raise NoMatchingDMTypeError( + "No INSTANCE with dmtype='mango:EpochPosition' has been found:" + " cannot build a SkyCoord from annotations") def _set_year_time_format(self, hk_field, besselian=False): """ - Format a date expressed in year as J-year + Format a date expressed in year as [scale]year parameters ---------- - - hk_field: MIVOT instance parameter as a dict + hk_field: dict + MIVOT instance attribute + besselian: boolean + besselian time scale is used if True, otherwise Julian (default) returns ------- - - formatted string + string + attribute value formatted as [scale]year """ scale = "J" if not besselian else "B" return (f"{scale}{hk_field['value']}" if hk_field["unit"] in ("yr", "year") @@ -97,17 +105,18 @@ def _get_space_frame(self, obstime=None): """ Build an astropy space frame instance from the MIVOT annotations. - - Equinox are supported fot FK4/5 - - Ref location are not supported + - Equinox are supported for FK4/5 + - Reference location is not supported parameters ---------- - - obstime: string - Observation time is given here because KF4 set an default value - if it is not given + obstime: str + Observation time is given to the space frame builder (this method) because + it must be set by the coordinate system constructor in case of FK4 frame. returns ------- - - Astropy space frame + FK2, FK5, ICRS or Galactic + Astropy space frame instance """ coo_sys = self._mivot_instance_dict["coordSys"] equinox = None @@ -136,18 +145,19 @@ def _get_space_frame(self, obstime=None): def _build_sky_coord_from_mango(self): """ - Build silently a SkyCoord instance from the mango:EpochPosition instance. - No error is trapped, unconsistencies in the mango:EpochPosition instance will + Build silently a SkyCoord instance from the ``mango:EpochPosition instance``. + No error is trapped, unconsistencies in the ``mango:EpochPosition`` instance will raise Astropy errors. - - The epoch (obstime) is meant to be given as J-year. + - The epoch (obstime) is meant to be given in year. - ICRS frame is taken by default - The cos-delta correction is meant to be applied. - The case mango:pmCosDeltApplied = False is not suppored yet + The case ``mango:pmCosDeltApplied = False`` is not supported yet returns ------- - - SkyCoord instance + SkyCoord + instance built by the method """ kwargs = {} kwargs["frame"] = self._get_space_frame() From ff0eeeaf585a1492ba3233816e1f8e61cc0e8233 Mon Sep 17 00:00:00 2001 From: lmichel Date: Tue, 1 Oct 2024 11:19:34 +0200 Subject: [PATCH 14/14] Rename Exceptions - MappingException renamed as MappingError and MivotException as MivotError - NoMatchingDMTypeError typed as child of TypeError. - Both MappingError and MivotError keep children of Exception. --- .../features/static_reference_resolver.py | 6 ++--- pyvo/mivot/seekers/annotation_seeker.py | 10 ++++----- pyvo/mivot/tests/test_mivot_viewer.py | 6 ++--- pyvo/mivot/tests/test_vizier_cs.py | 4 ++-- pyvo/mivot/tests/test_xml_viewer.py | 14 ++++++------ pyvo/mivot/utils/dict_utils.py | 4 ++-- pyvo/mivot/utils/exceptions.py | 10 ++++----- pyvo/mivot/utils/xml_utils.py | 4 ++-- pyvo/mivot/viewer/mivot_viewer.py | 22 +++++++++---------- pyvo/mivot/viewer/xml_viewer.py | 8 +++---- 10 files changed, 44 insertions(+), 44 deletions(-) diff --git a/pyvo/mivot/features/static_reference_resolver.py b/pyvo/mivot/features/static_reference_resolver.py index 8e194c5c8..fd376e924 100644 --- a/pyvo/mivot/features/static_reference_resolver.py +++ b/pyvo/mivot/features/static_reference_resolver.py @@ -2,7 +2,7 @@ Class used to resolve each static REFERENCE found in mivot_block. """ from copy import deepcopy -from pyvo.mivot.utils.exceptions import MivotException +from pyvo.mivot.utils.exceptions import MivotError from pyvo.mivot.utils.xpath_utils import XPath from pyvo.utils.prototype import prototype_feature @@ -33,7 +33,7 @@ def resolve(annotation_seeker, templates_ref, mivot_block): The number of references resolved. Raises ------ - MappingException + MappingError If the reference cannot be resolved. NotImplementedError If the reference is dynamic. @@ -50,7 +50,7 @@ def resolve(annotation_seeker, templates_ref, mivot_block): target = annotation_seeker.get_templates_instance_by_dmid(templates_ref, dmref) found_in_global = False if target is None: - raise MivotException(f"Cannot resolve reference={dmref}") + raise MivotError(f"Cannot resolve reference={dmref}") # Resolve static references recursively if not found_in_global: StaticReferenceResolver.resolve(annotation_seeker, templates_ref, ele) diff --git a/pyvo/mivot/seekers/annotation_seeker.py b/pyvo/mivot/seekers/annotation_seeker.py index 4f74eca5c..5c0d718a6 100644 --- a/pyvo/mivot/seekers/annotation_seeker.py +++ b/pyvo/mivot/seekers/annotation_seeker.py @@ -3,7 +3,7 @@ Utilities for extracting sub-blocks from a MIVOT mapping block. """ import logging -from pyvo.mivot.utils.exceptions import MivotException, MappingException +from pyvo.mivot.utils.exceptions import MivotError, MappingError from pyvo.mivot.utils.vocabulary import Att, Ele from pyvo.mivot.utils.vocabulary import Constant from pyvo.mivot.utils.xpath_utils import XPath @@ -73,7 +73,7 @@ def _find_templates_blocks(self): logging.debug("Found " + Ele.TEMPLATES + " without " + Att.tableref) self._templates_blocks["DEFAULT"] = child else: - raise MivotException(Ele.TEMPLATES + " without " + Att.tableref + " must be unique") + raise MivotError(Ele.TEMPLATES + " without " + Att.tableref + " must be unique") def _rename_ref_and_join(self): """ @@ -391,7 +391,7 @@ def get_collection_item_by_primarykey(self, coll_dmid, key_value): Raises ------ MivotElementNotFound: If no element matches the criteria. - MappingException: If more than one element matches the criteria. + MappingError: If more than one element matches the criteria. """ eset = XPath.x_path(self._globals_block, ".//" + Ele.COLLECTION + "[@" + Att.dmid + "='" + coll_dmid + "']/" + Ele.INSTANCE + "/" + Att.primarykey @@ -400,14 +400,14 @@ def get_collection_item_by_primarykey(self, coll_dmid, key_value): message = (f"{Ele.INSTANCE} with {Att.primarykey} = {key_value} in " f"{Ele.COLLECTION} {Att.dmid} {key_value} not found" ) - raise MivotException(message) + raise MivotError(message) if len(eset) > 1: message = ( f"More than one {Ele.INSTANCE} with {Att.primarykey}" f" = {key_value} found in {Ele.COLLECTION} " f"{Att.dmid} {key_value}" ) - raise MappingException(message) + raise MappingError(message) logging.debug(Ele.INSTANCE + " with " + Att.primarykey + "=%s found in " + Ele.COLLECTION + " " + Att.dmid + "=%s", key_value, coll_dmid) diff --git a/pyvo/mivot/tests/test_mivot_viewer.py b/pyvo/mivot/tests/test_mivot_viewer.py index 866839c8e..20fb63f85 100644 --- a/pyvo/mivot/tests/test_mivot_viewer.py +++ b/pyvo/mivot/tests/test_mivot_viewer.py @@ -8,7 +8,7 @@ from astropy.utils.data import get_pkg_data_filename from pyvo.mivot.utils.vocabulary import Constant from pyvo.mivot.utils.dict_utils import DictUtils -from pyvo.mivot.utils.exceptions import MappingException +from pyvo.mivot.utils.exceptions import MappingError from pyvo.mivot.version_checker import check_astropy_version from pyvo.mivot import MivotViewer from astropy import version as astropy_version @@ -84,9 +84,9 @@ def test_no_mivot(path_no_mivot): assert m_viewer.get_globals_models() is None assert m_viewer.get_templates_models() is None - with pytest.raises(MappingException): + with pytest.raises(MappingError): m_viewer._connect_table('_PKTable') - with pytest.raises(MappingException): + with pytest.raises(MappingError): m_viewer._connect_table() assert m_viewer.next_table_row() is None diff --git a/pyvo/mivot/tests/test_vizier_cs.py b/pyvo/mivot/tests/test_vizier_cs.py index 52c5ed696..573430cec 100644 --- a/pyvo/mivot/tests/test_vizier_cs.py +++ b/pyvo/mivot/tests/test_vizier_cs.py @@ -21,7 +21,7 @@ from urllib.request import urlretrieve from pyvo.mivot.version_checker import check_astropy_version from pyvo.mivot import MivotViewer -from pyvo.mivot.utils.exceptions import MivotException +from pyvo.mivot.utils.exceptions import MivotError @pytest.fixture @@ -125,5 +125,5 @@ def test_bad_ref(path_to_badref, delt_coo): """ Test that the epoch propagation works with all FIELDs referenced by name or by ID """ # Test with all FILELDs referenced by names - with (pytest.raises(MivotException, match="Attribute mango:EpochPosition.epoch can not be set.*")): + with (pytest.raises(MivotError, match="Attribute mango:EpochPosition.epoch can not be set.*")): MivotViewer(votable_path=path_to_badref) diff --git a/pyvo/mivot/tests/test_xml_viewer.py b/pyvo/mivot/tests/test_xml_viewer.py index 8431c500b..e0dde04ea 100644 --- a/pyvo/mivot/tests/test_xml_viewer.py +++ b/pyvo/mivot/tests/test_xml_viewer.py @@ -10,7 +10,7 @@ from astropy.utils.data import get_pkg_data_filename from pyvo.mivot.version_checker import check_astropy_version from pyvo.mivot import MivotViewer -from pyvo.mivot.utils.exceptions import MivotException +from pyvo.mivot.utils.exceptions import MivotError @pytest.mark.skipif(not check_astropy_version(), reason="need astropy 6+") @@ -18,27 +18,27 @@ def test_xml_viewer(m_viewer): m_viewer.next() xml_viewer = m_viewer.xml_viewer - with pytest.raises(MivotException, + with pytest.raises(MivotError, match="Cannot find dmrole wrong_role in any instances of the VOTable"): xml_viewer.get_instance_by_role("wrong_role") - with pytest.raises(MivotException, + with pytest.raises(MivotError, match="Cannot find dmrole wrong_role in any instances of the VOTable"): xml_viewer.get_instance_by_role("wrong_role", all_instances=True) - with pytest.raises(MivotException, + with pytest.raises(MivotError, match="Cannot find dmtype wrong_dmtype in any instances of the VOTable"): xml_viewer.get_instance_by_type("wrong_dmtype") - with pytest.raises(MivotException, + with pytest.raises(MivotError, match="Cannot find dmtype wrong_dmtype in any instances of the VOTable"): xml_viewer.get_instance_by_type("wrong_dmtype", all_instances=True) - with pytest.raises(MivotException, + with pytest.raises(MivotError, match="Cannot find dmrole wrong_role in any collections of the VOTable"): xml_viewer.get_collection_by_role("wrong_role") - with pytest.raises(MivotException, + with pytest.raises(MivotError, match="Cannot find dmrole wrong_role in any collections of the VOTable"): xml_viewer.get_collection_by_role("wrong_role", all_instances=True) diff --git a/pyvo/mivot/utils/dict_utils.py b/pyvo/mivot/utils/dict_utils.py index bd9d76e97..679c55a23 100644 --- a/pyvo/mivot/utils/dict_utils.py +++ b/pyvo/mivot/utils/dict_utils.py @@ -3,7 +3,7 @@ """ import json import logging -from pyvo.mivot.utils.exceptions import MivotException +from pyvo.mivot.utils.exceptions import MivotError from pyvo.mivot.utils.json_encoder import MivotJsonEncoder @@ -31,7 +31,7 @@ def read_dict_from_file(filename, fatal=False): return json.load(file, object_pairs_hook=OrderedDict) except Exception as exception: if fatal: - raise MivotException("reading {}".format(filename)) + raise MivotError("reading {}".format(filename)) else: logging.error("{} reading {}".format(exception, filename)) diff --git a/pyvo/mivot/utils/exceptions.py b/pyvo/mivot/utils/exceptions.py index 7e688b57c..16944d090 100644 --- a/pyvo/mivot/utils/exceptions.py +++ b/pyvo/mivot/utils/exceptions.py @@ -3,19 +3,19 @@ 3 exception classes - AstropyVersionException that prevent to use the package -- MappingException if the annotation cannot be processed (e.g. no MIVOT block) +- MappingError if the annotation cannot be processed (e.g. no MIVOT block) but the VOtable parsing can continue -- MivotException in any other case (block the processing) +- MivotError in any other case (block the processing) """ -class MivotException(Exception): +class MivotError(Exception): """ The annotation block is there but something went wrong with its processing """ -class MappingException(Exception): +class MappingError(Exception): """ Exception raised if a Resource or MIVOT element can't be mapped for one of these reasons: - It doesn't match with any Resource/MIVOT element expected. @@ -25,7 +25,7 @@ class MappingException(Exception): """ -class NoMatchingDMTypeError(Exception): +class NoMatchingDMTypeError(TypeError): """ Exception thrown when some PyVO code misses MIVOT element: - When trying to build a SkyCoord while there is no position in the annotations diff --git a/pyvo/mivot/utils/xml_utils.py b/pyvo/mivot/utils/xml_utils.py index 7c3f40cc9..83f15a108 100644 --- a/pyvo/mivot/utils/xml_utils.py +++ b/pyvo/mivot/utils/xml_utils.py @@ -5,7 +5,7 @@ import xml.etree.ElementTree as ET from pyvo.mivot.utils.vocabulary import Constant from pyvo.mivot.utils.vocabulary import Att -from pyvo.mivot.utils.exceptions import MivotException +from pyvo.mivot.utils.exceptions import MivotError class XmlUtils: @@ -95,7 +95,7 @@ def add_column_indices(mapping_block, index_map): break if not field_desc: if not ele.get(Att.value): - raise MivotException( + raise MivotError( f"Attribute {ele.get(Att.dmrole)} can not be set:" f" references a non existing column: {attr_ref} " f"and has no default value") diff --git a/pyvo/mivot/viewer/mivot_viewer.py b/pyvo/mivot/viewer/mivot_viewer.py index a6f1224f7..2521036d2 100644 --- a/pyvo/mivot/viewer/mivot_viewer.py +++ b/pyvo/mivot/viewer/mivot_viewer.py @@ -29,8 +29,8 @@ from pyvo.dal import DALResults from pyvo.mivot.utils.vocabulary import Ele, Att from pyvo.mivot.utils.vocabulary import Constant, NoMapping -from pyvo.mivot.utils.exceptions import (MappingException, - MivotException, +from pyvo.mivot.utils.exceptions import (MappingError, + MivotError, AstropyVersionException) from pyvo.mivot.utils.xml_utils import XmlUtils from pyvo.mivot.utils.xpath_utils import XPath @@ -100,7 +100,7 @@ def __init__(self, votable_path, tableref=None): self._set_mapped_tables() self._connect_table(tableref) self._init_instance() - except MappingException as mnf: + except MappingError as mnf: logging.error(str(mnf)) def __enter__(self): @@ -325,7 +325,7 @@ def get_first_instance_dmtype(self, tableref=None): elif child[0] in collection: return collection[0].get(Att.dmtype) else: - raise MivotException("Can't find the first " + Ele.INSTANCE + raise MivotError("Can't find the first " + Ele.INSTANCE + "/" + Ele.COLLECTION + " in " + Ele.TEMPLATES) def _connect_table(self, tableref=None): @@ -340,7 +340,7 @@ def _connect_table(self, tableref=None): Identifier of the table. If None, connects to the first table. """ if not self._resource_seeker: - raise MappingException("No mapping block found") + raise MappingError("No mapping block found") stableref = tableref if tableref is None: @@ -350,7 +350,7 @@ def _connect_table(self, tableref=None): "the mapping will be applied to the first table." ) elif tableref not in self._mapped_tables: - raise MappingException(f"The table {self._connected_tableref} doesn't match with any " + raise MappingError(f"The table {self._connected_tableref} doesn't match with any " f"mapped_table ({self._mapped_tables}) encountered in " + Ele.TEMPLATES ) @@ -359,11 +359,11 @@ def _connect_table(self, tableref=None): self._connected_table = self._resource_seeker.get_table(tableref) if self.connected_table is None: - raise MivotException(f"Cannot find table {stableref} in VOTable") + raise MivotError(f"Cannot find table {stableref} in VOTable") logging.debug("table %s found in VOTable", stableref) self._templates = deepcopy(self.annotation_seeker.get_templates_block(tableref)) if self._templates is None: - raise MivotException("Cannot find " + Ele.TEMPLATES + f" {stableref} ") + raise MivotError("Cannot find " + Ele.TEMPLATES + f" {stableref} ") logging.debug(Ele.TEMPLATES + " %s found ", stableref) self._table_iterator = TableIterator(self._connected_tableref, self.connected_table.to_table()) @@ -432,7 +432,7 @@ def _set_resource(self): """ if len(self._parsed_votable.resources) < 1: - raise MivotException("No resource detected in the VOTable") + raise MivotError("No resource detected in the VOTable") rnb = 0 for res in self._parsed_votable.resources: if res.type.lower() == "results": @@ -440,14 +440,14 @@ def _set_resource(self): self._resource = self._parsed_votable.resources[rnb] return rnb += 1 - raise MivotException("No resource @type='results'detected in the VOTable") + raise MivotError("No resource @type='results'detected in the VOTable") def _set_mapping_block(self): """ Set the mapping block found in the resource and set the annotation_seeker """ if NoMapping.search(self._resource.mivot_block.content): - raise MappingException("Mivot block is not found") + raise MappingError("Mivot block is not found") # The namespace should be removed self._mapping_block = ( etree.fromstring(self._resource.mivot_block.content diff --git a/pyvo/mivot/viewer/xml_viewer.py b/pyvo/mivot/viewer/xml_viewer.py index 75103a0ec..fd69e79ce 100644 --- a/pyvo/mivot/viewer/xml_viewer.py +++ b/pyvo/mivot/viewer/xml_viewer.py @@ -3,7 +3,7 @@ XMLViewer provides several getters on XML instances built by `pyvo.mivot.viewer.mivot_viewer`. """ -from pyvo.mivot.utils.exceptions import MivotException +from pyvo.mivot.utils.exceptions import MivotError from pyvo.mivot.utils.xpath_utils import XPath from pyvo.utils.prototype import prototype_feature @@ -60,7 +60,7 @@ def get_instance_by_role(self, dmrole, all_instances=False): dmrole) if len(instances) == 0: - raise MivotException( + raise MivotError( f"Cannot find dmrole {dmrole} in any instances of the VOTable") if all_instances is False: @@ -98,7 +98,7 @@ def get_instance_by_type(self, dmtype, all_instances=False): dmtype) if len(instances) == 0: - raise MivotException( + raise MivotError( f"Cannot find dmtype {dmtype} in any instances of the VOTable") if all_instances is False: @@ -136,7 +136,7 @@ def get_collection_by_role(self, dmrole, all_instances=False): dmrole) if len(collections) == 0: - raise MivotException( + raise MivotError( f"Cannot find dmrole {dmrole} in any collections of the VOTable") if all_instances is False: