Skip to content

Commit 0430d22

Browse files
committed
now that the server is working again, we don't want xfails
1 parent ea7af08 commit 0430d22

3 files changed

Lines changed: 61 additions & 31 deletions

File tree

astroquery/linelists/jplspec/core.py

Lines changed: 50 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -156,9 +156,19 @@ def query_lines(self, min_frequency, max_frequency, *,
156156
by the query service. This workaround is needed while JPLSpec's query
157157
tool is broken.
158158
159-
use_getmolecule is an option to force the query to use get_molecule.
160-
It is needed if the JPL server is completely unresponsive.
159+
use_getmolecule is an option to skip the query service entirely and
160+
retrieve full molecule catalogs via `get_molecule`. It is needed when
161+
the JPL query server is unresponsive. Frequency and strength limits
162+
are not applied in this mode.
161163
"""
164+
if use_getmolecule:
165+
if get_query_payload:
166+
return [('Mol', tuple(self._resolve_molecules(molecule, flags=flags,
167+
parse_name_locally=parse_name_locally)))]
168+
mols = self._resolve_molecules(molecule, flags=flags,
169+
parse_name_locally=parse_name_locally)
170+
return self._build_table_from_get_molecule(mols)
171+
162172
response = self.query_lines_async(min_frequency=min_frequency,
163173
max_frequency=max_frequency,
164174
min_strength=min_strength,
@@ -171,13 +181,44 @@ def query_lines(self, min_frequency, max_frequency, *,
171181
if get_query_payload:
172182
return response
173183
else:
174-
return self._parse_result(response, fallback_to_getmolecule=fallback_to_getmolecule,
175-
use_getmolecule=use_getmolecule)
184+
return self._parse_result(response, fallback_to_getmolecule=fallback_to_getmolecule)
176185

177186
query_lines.__doc__ = process_asyncs.async_to_sync_docstr(query_lines_async.__doc__)
178187

179-
def _parse_result(self, response, *, verbose=False, fallback_to_getmolecule=False,
180-
use_getmolecule=True):
188+
def _resolve_molecules(self, molecule, *, flags=0, parse_name_locally=False):
189+
"""Return a list of molecule identifiers to feed to get_molecule."""
190+
if molecule is None or molecule == 'All':
191+
raise InvalidQueryError("use_getmolecule requires an explicit molecule "
192+
"or regex; 'All' is not supported.")
193+
if parse_name_locally:
194+
self.lookup_ids = build_lookup()
195+
mols = list(self.lookup_ids.find(molecule, flags).values())
196+
if len(mols) == 0:
197+
raise InvalidQueryError('No matching species found.')
198+
return mols
199+
if isinstance(molecule, (list, tuple)):
200+
return list(molecule)
201+
return [molecule]
202+
203+
def _build_table_from_get_molecule(self, mols):
204+
"""Fetch full catalog tables for each molecule and combine them."""
205+
self.lookup_ids = build_lookup()
206+
tbs = [self.get_molecule(mol) for mol in mols]
207+
if len(tbs) > 1:
208+
for tb, mol in zip(tbs, mols):
209+
tb['Name'] = self.lookup_ids.find(str(mol), flags=0)
210+
for key in list(tb.meta.keys()):
211+
tb.meta[f'{mol}_{key}'] = tb.meta.pop(key)
212+
tb = table.vstack(tbs)
213+
tb.meta['molecule_list'] = list(mols)
214+
return tb
215+
else:
216+
tb = tbs[0]
217+
tb.meta['molecule_id'] = mols[0]
218+
tb.meta['molecule_name'] = self.lookup_ids.find(str(mols[0]), flags=0)
219+
return tb
220+
221+
def _parse_result(self, response, *, verbose=False, fallback_to_getmolecule=False):
181222
"""
182223
Parse a response into an `~astropy.table.Table`
183224
@@ -208,28 +249,11 @@ def _parse_result(self, response, *, verbose=False, fallback_to_getmolecule=Fals
208249
QN": Quantum numbers for the lower state.
209250
"""
210251

211-
if 'Zero lines were found' in response.text or use_getmolecule:
252+
if 'Zero lines were found' in response.text:
212253
if fallback_to_getmolecule:
213-
self.lookup_ids = build_lookup()
214254
payload = parse_qs(response.request.body)
215-
tbs = [self.get_molecule(mol) for mol in payload['Mol']]
216-
if len(tbs) > 1:
217-
mols = []
218-
for tb, mol in zip(tbs, payload['Mol']):
219-
tb['Name'] = self.lookup_ids.find(mol, flags=0)
220-
for key in list(tb.meta.keys()):
221-
tb.meta[f'{mol}_{key}'] = tb.meta.pop(key)
222-
mols.append(mol)
223-
tb = table.vstack(tbs)
224-
tb.meta['molecule_list'] = mols
225-
else:
226-
tb = tbs[0]
227-
tb.meta['molecule_id'] = payload['Mol'][0]
228-
tb.meta['molecule_name'] = self.lookup_ids.find(payload['Mol'][0], flags=0)
229-
230-
return tb
231-
else:
232-
raise EmptyResponseError(f"Response was empty; message was '{response.text}'.")
255+
return self._build_table_from_get_molecule(payload['Mol'])
256+
raise EmptyResponseError(f"Response was empty; message was '{response.text}'.")
233257

234258
# data starts at 0 since regex was applied
235259
# Warning for a result with more than 1000 lines:

astroquery/linelists/jplspec/tests/test_jplspec.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,7 @@ def test_query_lines_with_fallback():
290290
max_frequency=200 * u.GHz,
291291
min_strength=-500,
292292
molecule="28001 CO",
293+
use_getmolecule=False,
293294
fallback_to_getmolecule=False)
294295

295296
# Test with fallback enabled - should call get_molecule
@@ -320,6 +321,7 @@ def test_query_lines_with_fallback():
320321
max_frequency=200 * u.GHz,
321322
min_strength=-500,
322323
molecule="28001 CO",
324+
use_getmolecule=False,
323325
fallback_to_getmolecule=True)
324326

325327
mock_get_molecule.assert_called_once_with('28001')

astroquery/linelists/jplspec/tests/test_jplspec_remote.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,18 @@
66
from astroquery.exceptions import EmptyResponseError
77

88

9-
@pytest.mark.xfail(reason="2025 server problems", raises=EmptyResponseError)
109
@pytest.mark.remote_data
1110
def test_remote():
11+
"""
12+
In 2025-2026, the JPLSpec server went down and this was marked 'xfail' for a while.
13+
14+
As of late April 2026, it's back online again.
15+
"""
1216
tbl = JPLSpec.query_lines(min_frequency=500 * u.GHz,
1317
max_frequency=1000 * u.GHz,
1418
min_strength=-500,
1519
molecule="18003 H2O",
20+
use_getmolecule=False,
1621
fallback_to_getmolecule=False)
1722
assert isinstance(tbl, Table)
1823
assert len(tbl) == 36
@@ -36,7 +41,7 @@ def test_remote_regex_fallback():
3641
max_frequency=1000 * u.GHz,
3742
min_strength=-500,
3843
molecule=("28001", "28002", "28003"),
39-
fallback_to_getmolecule=True)
44+
use_getmolecule=True)
4045
assert isinstance(tbl, Table)
4146
tbl = tbl[((tbl['FREQ'].quantity > 500*u.GHz) & (tbl['FREQ'].quantity < 1*u.THz))]
4247
assert len(tbl) == 16
@@ -54,14 +59,13 @@ def test_remote_regex_fallback():
5459
assert tbl['FREQ'][15] == 946175.3151
5560

5661

57-
# Starting in 2025, the JPL CGI server that did search queries broke totally. See #3363
58-
@pytest.mark.xfail(reason="2025 server problems", raises=EmptyResponseError)
5962
@pytest.mark.remote_data
6063
def test_remote_regex():
6164
tbl = JPLSpec.query_lines(min_frequency=500 * u.GHz,
6265
max_frequency=1000 * u.GHz,
6366
min_strength=-500,
6467
molecule=("28001", "28002", "28003"),
68+
use_getmolecule=False,
6569
fallback_to_getmolecule=False)
6670
assert isinstance(tbl, Table)
6771
assert len(tbl) == 16
@@ -130,7 +134,7 @@ def test_remote_fallback():
130134
max_frequency=1000 * u.GHz,
131135
min_strength=-500,
132136
molecule="18003 H2O",
133-
fallback_to_getmolecule=True)
137+
use_getmolecule=True)
134138
assert isinstance(tbl, Table)
135139
tbl = tbl[((tbl['FREQ'].quantity > 500*u.GHz) & (tbl['FREQ'].quantity < 1*u.THz))]
136140
assert len(tbl) == 36

0 commit comments

Comments
 (0)