Skip to content

Commit

Permalink
Merge pull request #2954 from cds-astro/refactor_Simbad
Browse files Browse the repository at this point in the history
Simbad: refactor to use TAP
  • Loading branch information
bsipocz authored Jun 20, 2024
2 parents f64b997 + 6594ebb commit 091e6b3
Show file tree
Hide file tree
Showing 39 changed files with 4,680 additions and 3,241 deletions.
45 changes: 45 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,51 @@ vizier
- Change the type of raised error when the catalog is not found in ``Vizier.get_catalog_metadata``
from ``IndexError`` to ``EmptyResponseError`` [#2980]

simbad
^^^^^^

- The ``ROW_LIMIT`` value to have the maximum number of rows is now -1.
Use ``ROW_LIMIT = 0`` to retrieve the output's meta-data. [#2954]

- ``ROW_LIMIT`` can now be set at instantiation
(e.g.: ``simbad = Simbad(ROW_LIMIT=10))``). [#2954]

- ``list_votable_fields`` now return an astropy Table with added fields
information instead of a list of strings. [#2954]

- ``list_votable_fields`` is now queried directly from SIMBAD instead of reading
a file in astroquery. This prevents it from being outdated. [#2954]

- ``get_votable_fields`` now prints the table name and column name instead of
just the column name. [#2954]

- The ``verbose`` and ``cache`` kwargs have been deprecated from all methods
as they have no effect with with the new query interface. [#2954]

- ``get_adql`` is deprecated and replaced by ``get_query_payload`` in
``list_columns`` and ``list_table``.
The payload output contains the ADQL under the ``QUERY`` key. [#2954]

- all query methods except ``query_tap`` and ``query_criteria`` now accept a
``criteria`` argument to restrict the results with custom criteria. [#2954]

- ``query_objects`` outputs now have an additional column ``user_specified_id``
containing the objects' name as specified by the user.
The ``votable_field`` option ``typed_id`` is removed. [#2954]

- The ``equinox`` and ``epoch`` kwargs are deprecated in ``query_region``,
use astropy.coordinates.SkyCoord directly instead. [#2954]

- ``query_bibcode`` has a new option ``abstract`` that allows to also
retrieve the article's abstract. [#2954]

- ``query_bibcode`` output is now in an astropy Table with distinct columns
instead of a single one in which all the information was a string. [#2954]

- ``query_criteria`` is now deprecated and should be replaced by either custom
TAP queries or by the ``criteria`` argument added in the other query methods.
A helper method was added ``astroquery.simbad.utils.CriteriaTranslator`` to
translate between the sim-script syntax and the TAP/ADQL syntax. [#2954]

skyview
^^^^^^^
Expand Down
7 changes: 4 additions & 3 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,10 @@ website <https://simbad.cds.unistra.fr/simbad/>`_, use the ``simbad`` sub-packag
>>> from astroquery.simbad import Simbad
>>> theta1c = Simbad.query_object('tet01 Ori C')
>>> theta1c.pprint()
MAIN_ID RA DEC ... COO_QUAL COO_WAVELENGTH COO_BIBCODE
------------- ------------- ------------- ... -------- -------------- -------------------
* tet01 Ori C 05 35 16.4637 -05 23 22.848 ... A O 2007A&A...474..653V
main_id ra dec ... coo_wavelength coo_bibcode matched_id
deg deg ...
------------- ------------- ------------- ... -------------- ------------------- -------------
* tet01 Ori C 83.8186095697 -5.3897005033 ... O 2020yCat.1350....0G * tet01 Ori C
Installation and Requirements
-----------------------------
Expand Down
9 changes: 6 additions & 3 deletions astroquery/esa/jwst/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -571,9 +571,12 @@ def resolve_target_coordinates(self, target_name, target_resolver):
if target_resolver == "ALL" or target_resolver == "SIMBAD":
try:
result_table = Simbad.query_object(target_name)
return SkyCoord((f'{result_table["RA"][0]} '
f'{result_table["DEC"][0]}'),
unit=(units.hourangle,
# new simbad behavior does not return None but an empty table
if len(result_table) == 0:
result_table = None
return SkyCoord((f'{result_table["ra"][0]} '
f'{result_table["dec"][0]}'),
unit=(units.deg,
units.deg), frame="icrs")
except (KeyError, TypeError, ConnectionError):
log.info("SIMBAD could not resolve this target")
Expand Down
73 changes: 73 additions & 0 deletions astroquery/esa/jwst/tests/data/simbad_M1.vot
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Produced with astropy.io.votable version 6.0.0
http://www.astropy.org/ -->
<VOTABLE version="1.3" xmlns="http://www.ivoa.net/xml/VOTable/v1.3" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.ivoa.net/xml/VOTable/v1.3 http://www.ivoa.net/xml/VOTable/VOTable-1.3.xsd">
<RESOURCE type="results">
<INFO ID="QUERY_STATUS" name="QUERY_STATUS" value="OK"/>
<INFO ID="PROVIDER" name="PROVIDER" value="CDS">SIMBAD TAP Service</INFO>
<INFO ID="QUERY" name="QUERY" value="SELECT basic.&quot;main_id&quot;, basic.&quot;ra&quot;, basic.&quot;dec&quot;, basic.&quot;coo_err_maj&quot;, basic.&quot;coo_err_min&quot;, basic.&quot;coo_err_angle&quot;, basic.&quot;coo_wavelength&quot;, basic.&quot;coo_bibcode&quot;, ident.&quot;id&quot; AS matched_id FROM basic JOIN ident ON basic.&quot;oid&quot; = ident.&quot;oidref&quot; WHERE id = &apos;M1&apos;"/>
<TABLE ID="result_S1708521282485" name="result_S1708521282485">
<FIELD ID="main_id" arraysize="*" datatype="char" name="main_id" ucd="meta.id;meta.main">
<DESCRIPTION>
Main identifier for an object
</DESCRIPTION>
</FIELD>
<FIELD ID="ra" datatype="double" name="ra" ucd="pos.eq.ra;meta.main" unit="deg">
<DESCRIPTION>
Right ascension
</DESCRIPTION>
</FIELD>
<FIELD ID="dec" datatype="double" name="dec" ucd="pos.eq.dec;meta.main" unit="deg">
<DESCRIPTION>
Declination
</DESCRIPTION>
</FIELD>
<FIELD ID="coo_err_maj" datatype="float" name="coo_err_maj" ucd="phys.angSize.smajAxis;pos.errorEllipse;pos.eq" unit="mas">
<DESCRIPTION>
Coordinate error major axis
</DESCRIPTION>
</FIELD>
<FIELD ID="coo_err_min" datatype="float" name="coo_err_min" ucd="phys.angSize.sminAxis;pos.errorEllipse;pos.eq" unit="mas">
<DESCRIPTION>
Coordinate error minor axis
</DESCRIPTION>
</FIELD>
<FIELD ID="coo_err_angle" datatype="short" name="coo_err_angle" ucd="pos.posAng;pos.errorEllipse;pos.eq" unit="deg">
<DESCRIPTION>
Coordinate error angle
</DESCRIPTION>
<VALUES null="-32768"/>
</FIELD>
<FIELD ID="coo_wavelength" arraysize="1" datatype="char" name="coo_wavelength" ucd="instr.bandpass;pos.eq">
<DESCRIPTION>
Wavelength class for the origin of the coordinates (R,I,V,U,X,G)
</DESCRIPTION>
</FIELD>
<FIELD ID="coo_bibcode" arraysize="*" datatype="char" name="coo_bibcode" ucd="meta.bib.bibcode;pos.eq">
<DESCRIPTION>
Coordinate reference
</DESCRIPTION>
</FIELD>
<FIELD ID="matched_id" arraysize="*" datatype="char" name="matched_id" ucd="meta.id">
<DESCRIPTION>
Identifier
</DESCRIPTION>
</FIELD>
<DATA>
<TABLEDATA>
<TR>
<TD>M 1</TD>
<TD>83.6287</TD>
<TD>22.0147</TD>
<TD>18500</TD>
<TD>18500</TD>
<TD>0</TD>
<TD>R</TD>
<TD>1995AuJPh..48..143S</TD>
<TD>M 1</TD>
</TR>
</TABLEDATA>
</DATA>
</TABLE>
</RESOURCE>
</VOTABLE>
58 changes: 58 additions & 0 deletions astroquery/esa/jwst/tests/data/simbad_TEST.vot
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Produced with astropy.io.votable version 6.0.0
http://www.astropy.org/ -->
<VOTABLE version="1.3" xmlns="http://www.ivoa.net/xml/VOTable/v1.3" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.ivoa.net/xml/VOTable/v1.3 http://www.ivoa.net/xml/VOTable/VOTable-1.3.xsd">
<RESOURCE type="results">
<INFO ID="QUERY_STATUS" name="QUERY_STATUS" value="OK"/>
<INFO ID="PROVIDER" name="PROVIDER" value="CDS">SIMBAD TAP Service</INFO>
<INFO ID="QUERY" name="QUERY" value="SELECT basic.&quot;main_id&quot;, basic.&quot;ra&quot;, basic.&quot;dec&quot;, basic.&quot;coo_err_maj&quot;, basic.&quot;coo_err_min&quot;, basic.&quot;coo_err_angle&quot;, basic.&quot;coo_wavelength&quot;, basic.&quot;coo_bibcode&quot;, ident.&quot;id&quot; AS matched_id FROM basic JOIN ident ON basic.&quot;oid&quot; = ident.&quot;oidref&quot; WHERE id = &apos;TEST&apos;"/>
<TABLE ID="result_S1708521454265" name="result_S1708521454265">
<FIELD ID="main_id" arraysize="*" datatype="char" name="main_id" ucd="meta.id;meta.main">
<DESCRIPTION>
Main identifier for an object
</DESCRIPTION>
</FIELD>
<FIELD ID="ra" datatype="double" name="ra" ucd="pos.eq.ra;meta.main" unit="deg">
<DESCRIPTION>
Right ascension
</DESCRIPTION>
</FIELD>
<FIELD ID="dec" datatype="double" name="dec" ucd="pos.eq.dec;meta.main" unit="deg">
<DESCRIPTION>
Declination
</DESCRIPTION>
</FIELD>
<FIELD ID="coo_err_maj" datatype="float" name="coo_err_maj" ucd="phys.angSize.smajAxis;pos.errorEllipse;pos.eq" unit="mas">
<DESCRIPTION>
Coordinate error major axis
</DESCRIPTION>
</FIELD>
<FIELD ID="coo_err_min" datatype="float" name="coo_err_min" ucd="phys.angSize.sminAxis;pos.errorEllipse;pos.eq" unit="mas">
<DESCRIPTION>
Coordinate error minor axis
</DESCRIPTION>
</FIELD>
<FIELD ID="coo_err_angle" datatype="short" name="coo_err_angle" ucd="pos.posAng;pos.errorEllipse;pos.eq" unit="deg">
<DESCRIPTION>
Coordinate error angle
</DESCRIPTION>
<VALUES null="-32768"/>
</FIELD>
<FIELD ID="coo_wavelength" arraysize="1" datatype="char" name="coo_wavelength" ucd="instr.bandpass;pos.eq">
<DESCRIPTION>
Wavelength class for the origin of the coordinates (R,I,V,U,X,G)
</DESCRIPTION>
</FIELD>
<FIELD ID="coo_bibcode" arraysize="*" datatype="char" name="coo_bibcode" ucd="meta.bib.bibcode;pos.eq">
<DESCRIPTION>
Coordinate reference
</DESCRIPTION>
</FIELD>
<FIELD ID="matched_id" arraysize="*" datatype="char" name="matched_id" ucd="meta.id">
<DESCRIPTION>
Identifier
</DESCRIPTION>
</FIELD>
</TABLE>
</RESOURCE>
</VOTABLE>
104 changes: 55 additions & 49 deletions astroquery/esa/jwst/tests/test_jwsttap.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import os
import shutil
from pathlib import Path
from unittest.mock import MagicMock
from unittest.mock import MagicMock, patch
import sys
import io

Expand All @@ -21,14 +21,15 @@
from astropy import units
from astropy.coordinates.name_resolve import NameResolveError
from astropy.coordinates.sky_coordinate import SkyCoord
from astropy.io.votable import parse_single_table
from astropy.table import Table
from astropy.units import Quantity
from astroquery.exceptions import TableParseError

from astroquery.esa.jwst import JwstClass
from astroquery.esa.jwst.tests.DummyTapHandler import DummyTapHandler
from astroquery.ipac.ned import Ned
from astroquery.simbad import Simbad
from astroquery.simbad import SimbadClass
from astroquery.utils.tap.conn.tests.DummyConnHandler import DummyConnHandler
from astroquery.utils.tap.conn.tests.DummyResponse import DummyResponse
from astroquery.utils.tap.core import TapPlus
Expand Down Expand Up @@ -914,53 +915,58 @@ def __check_extracted_files(self, files_expected, files_returned):
raise ValueError(f"Not found expected file: {f}")

def test_query_target_error(self):
jwst = JwstClass(show_messages=False)
simbad = Simbad()
ned = Ned()
vizier = Vizier()
# Testing default parameters
with pytest.raises((ValueError, TableParseError)) as err:
jwst.query_target(target_name="M1", target_resolver="")
assert "This target resolver is not allowed" in err.value.args[0]
with pytest.raises((ValueError, TableParseError)) as err:
jwst.query_target("TEST")
assert ('This target name cannot be determined with this '
'resolver: ALL' in err.value.args[0] or 'Failed to parse' in err.value.args[0])
with pytest.raises((ValueError, TableParseError)) as err:
jwst.query_target(target_name="M1", target_resolver="ALL")
assert err.value.args[0] in ["This target name cannot be determined "
"with this resolver: ALL", "Missing "
"required argument: 'width'"]

# Testing no valid coordinates from resolvers
simbad_file = data_path('test_query_by_target_name_simbad_ned_error.vot')
simbad_table = Table.read(simbad_file)
simbad.query_object = MagicMock(return_value=simbad_table)
ned_file = data_path('test_query_by_target_name_simbad_ned_error.vot')
ned_table = Table.read(ned_file)
ned.query_object = MagicMock(return_value=ned_table)
vizier_file = data_path('test_query_by_target_name_vizier_error.vot')
vizier_table = Table.read(vizier_file)
vizier.query_object = MagicMock(return_value=vizier_table)

# coordinate_error = 'coordinate must be either a string or astropy.coordinates'
with pytest.raises((ValueError, TableParseError)) as err:
jwst.query_target(target_name="test", target_resolver="SIMBAD",
radius=units.Quantity(5, units.deg))
assert ('This target name cannot be determined with this '
'resolver: SIMBAD' in err.value.args[0] or 'Failed to parse' in err.value.args[0])

with pytest.raises((ValueError, TableParseError)) as err:
jwst.query_target(target_name="test", target_resolver="NED",
radius=units.Quantity(5, units.deg))
assert ('This target name cannot be determined with this '
'resolver: NED' in err.value.args[0] or 'Failed to parse' in err.value.args[0])

with pytest.raises((ValueError, TableParseError)) as err:
jwst.query_target(target_name="test", target_resolver="VIZIER",
radius=units.Quantity(5, units.deg))
assert ('This target name cannot be determined with this resolver: '
'VIZIER' in err.value.args[0] or 'Failed to parse' in err.value.args[0])
# need to patch simbad query object here
with patch("astroquery.simbad.SimbadClass.query_object",
side_effect=lambda object_name: parse_single_table(
Path(__file__).parent / "data" / f"simbad_{object_name}.vot"
).to_table()):
jwst = JwstClass(show_messages=False)
simbad = SimbadClass()
ned = Ned()
vizier = Vizier()
# Testing default parameters
with pytest.raises((ValueError, TableParseError)) as err:
jwst.query_target(target_name="M1", target_resolver="")
assert "This target resolver is not allowed" in err.value.args[0]
with pytest.raises((ValueError, TableParseError)) as err:
jwst.query_target("TEST")
assert ('This target name cannot be determined with this '
'resolver: ALL' in err.value.args[0] or 'Failed to parse' in err.value.args[0])
with pytest.raises((ValueError, TableParseError)) as err:
jwst.query_target(target_name="M1", target_resolver="ALL")
assert err.value.args[0] in ["This target name cannot be determined "
"with this resolver: ALL", "Missing "
"required argument: 'width'"]

# Testing no valid coordinates from resolvers
simbad_file = data_path('test_query_by_target_name_simbad_ned_error.vot')
simbad_table = Table.read(simbad_file)
simbad.query_object = MagicMock(return_value=simbad_table)
ned_file = data_path('test_query_by_target_name_simbad_ned_error.vot')
ned_table = Table.read(ned_file)
ned.query_object = MagicMock(return_value=ned_table)
vizier_file = data_path('test_query_by_target_name_vizier_error.vot')
vizier_table = Table.read(vizier_file)
vizier.query_object = MagicMock(return_value=vizier_table)

# coordinate_error = 'coordinate must be either a string or astropy.coordinates'
with pytest.raises((ValueError, TableParseError)) as err:
jwst.query_target(target_name="TEST", target_resolver="SIMBAD",
radius=units.Quantity(5, units.deg))
assert ('This target name cannot be determined with this '
'resolver: SIMBAD' in err.value.args[0] or 'Failed to parse' in err.value.args[0])

with pytest.raises((ValueError, TableParseError)) as err:
jwst.query_target(target_name="TEST", target_resolver="NED",
radius=units.Quantity(5, units.deg))
assert ('This target name cannot be determined with this '
'resolver: NED' in err.value.args[0] or 'Failed to parse' in err.value.args[0])

with pytest.raises((ValueError, TableParseError)) as err:
jwst.query_target(target_name="TEST", target_resolver="VIZIER",
radius=units.Quantity(5, units.deg))
assert ('This target name cannot be determined with this resolver: '
'VIZIER' in err.value.args[0] or 'Failed to parse' in err.value.args[0])

def test_remove_jobs(self):
dummyTapHandler = DummyTapHandler()
Expand Down
4 changes: 4 additions & 0 deletions astroquery/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,10 @@ def __init__(self):

self.name = self.__class__.__name__.split("Class")[0]

def __call__(self, *args, **kwargs):
""" init a fresh copy of self """
return self.__class__(*args, **kwargs)


class BaseQuery(metaclass=LoginABCMeta):
"""
Expand Down
22 changes: 11 additions & 11 deletions astroquery/simbad/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@
SIMBAD Query Tool
=================
The SIMBAD query tool creates a `script query
<https://simbad.cds.unistra.fr/simbad/sim-fscript>`__ that returns VOtable XML
data that is then parsed into a SimbadResult object. This object then
parses the data and returns a table parsed with `astropy.io.votable.parse`.
The SIMBAD query tool creates `TAP ADQL queries
<https://cds.unistra.fr/help/documentation/simbad-more/adql-simbad/>`__ that return VOtable XML
data. This is then parsed into a `~astropy.table.Table` object.
"""
from astropy import config as _config

Expand All @@ -27,16 +26,17 @@ class Conf(_config.ConfigNamespace):
'Time limit for connecting to Simbad server.')

row_limit = _config.ConfigItem(
# O defaults to the maximum limit
0,
# defaults to the maximum limit
-1,
'Maximum number of rows that will be fetched from the result.')

# should be columns of 'basic'
default_columns = ["main_id", "ra", "dec", "coo_err_maj", "coo_err_min",
"coo_err_angle", "coo_wavelength", "coo_bibcode"]


conf = Conf()

from .core import Simbad, SimbadClass, SimbadBaseQuery
from .core import Simbad, SimbadClass

__all__ = ['Simbad', 'SimbadClass',
'SimbadBaseQuery',
'Conf', 'conf',
]
__all__ = ['Simbad', 'SimbadClass', 'Conf', 'conf']
Loading

0 comments on commit 091e6b3

Please sign in to comment.