diff --git a/CHANGES.rst b/CHANGES.rst index eabaaef26d..4d4a9782b4 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -103,6 +103,7 @@ jplspec - Refactored to use linelists.core. Added new ``get_molecule`` method [#3456] - Moved to linelists/. astroquery.jplspec is now deprecated in favor of astroquery.linelists.jplspec [#3455] +- Fallback added to go to the wayback machine [#3547] linelists.jplspec diff --git a/astroquery/linelists/jplspec/__init__.py b/astroquery/linelists/jplspec/__init__.py index 5b03985e7f..49d6018ce5 100644 --- a/astroquery/linelists/jplspec/__init__.py +++ b/astroquery/linelists/jplspec/__init__.py @@ -15,6 +15,12 @@ class Conf(_config.ConfigNamespace): 'https://spec.jpl.nasa.gov/cgi-bin/catform', 'JPL Spectral Catalog URL.') + ftp_cat_server = _config.ConfigItem( + ['https://spec.jpl.nasa.gov/ftp/pub/catalog/', + 'https://web.archive.org/web/20250630185813/https://spec.jpl.nasa.gov/ftp/pub/catalog/'], + 'JPL FTP Catalog URL' + ) + timeout = _config.ConfigItem( 60, 'Time limit for connecting to JPL server.') diff --git a/astroquery/linelists/jplspec/core.py b/astroquery/linelists/jplspec/core.py index fd2648dcf5..30ac489c39 100644 --- a/astroquery/linelists/jplspec/core.py +++ b/astroquery/linelists/jplspec/core.py @@ -27,6 +27,7 @@ class JPLSpecClass(BaseQuery): # use the Configuration Items imported from __init__.py URL = conf.server + FTP_CAT_URL = conf.ftp_cat_server TIMEOUT = conf.timeout def __init__(self): @@ -142,6 +143,7 @@ def query_lines(self, min_frequency, max_frequency, *, parse_name_locally=False, get_query_payload=False, fallback_to_getmolecule=True, + use_getmolecule=True, cache=True): """ Query the JPLSpec service for spectral lines. @@ -153,6 +155,9 @@ def query_lines(self, min_frequency, max_frequency, *, governs whether `get_molecule` will be used when no results are returned by the query service. This workaround is needed while JPLSpec's query tool is broken. + + use_getmolecule is an option to force the query to use get_molecule. + It is needed if the JPL server is completely unresponsive. """ response = self.query_lines_async(min_frequency=min_frequency, max_frequency=max_frequency, @@ -166,11 +171,13 @@ def query_lines(self, min_frequency, max_frequency, *, if get_query_payload: return response else: - return self._parse_result(response, fallback_to_getmolecule=fallback_to_getmolecule) + return self._parse_result(response, fallback_to_getmolecule=fallback_to_getmolecule, + use_getmolecule=use_getmolecule) query_lines.__doc__ = process_asyncs.async_to_sync_docstr(query_lines_async.__doc__) - def _parse_result(self, response, *, verbose=False, fallback_to_getmolecule=False): + def _parse_result(self, response, *, verbose=False, fallback_to_getmolecule=False, + use_getmolecule=True): """ Parse a response into an `~astropy.table.Table` @@ -201,8 +208,8 @@ def _parse_result(self, response, *, verbose=False, fallback_to_getmolecule=Fals QN": Quantum numbers for the lower state. """ - if 'Zero lines were found' in response.text: - if fallback_to_getmolecule: + if 'Zero lines were found' in response.text or use_getmolecule: + if fallback_to_getmolecule or use_getmolecule: self.lookup_ids = build_lookup() payload = parse_qs(response.request.body) tbs = [self.get_molecule(mol) for mol in payload['Mol']] @@ -320,7 +327,7 @@ def get_molecule(self, molecule_id, *, cache=True): molecule_str = parse_molid(molecule_id) # Construct the URL to the catalog file - url = f'https://spec.jpl.nasa.gov/ftp/pub/catalog/c{molecule_str}.cat' + url = f'{self.FTP_CAT_URL}/c{molecule_str}.cat' # Request the catalog file response = self._request(method='GET', url=url, diff --git a/astroquery/linelists/jplspec/tests/test_jplspec.py b/astroquery/linelists/jplspec/tests/test_jplspec.py index b933b60cb2..91119e7d57 100644 --- a/astroquery/linelists/jplspec/tests/test_jplspec.py +++ b/astroquery/linelists/jplspec/tests/test_jplspec.py @@ -23,8 +23,10 @@ def data_path(filename): class MockResponseSpec: - def __init__(self, filename): + def __init__(self, filename, body=''): self.filename = data_path(filename) + self.request = Mock() + self.request.body = body @property def text(self): @@ -76,8 +78,8 @@ def test_input_multi(): def test_query(): - response = MockResponseSpec(file1) - tbl = JPLSpec._parse_result(response) + response = MockResponseSpec(file1, body='Mol=28001 CO') + tbl = JPLSpec._parse_result(response, fallback_to_getmolecule=False, use_getmolecule=False) assert isinstance(tbl, Table) assert len(tbl) == 8 assert set(tbl.keys()) == set(['FREQ', 'ERR', 'LGINT', 'DR', 'ELO', 'GUP', @@ -94,8 +96,8 @@ def test_query(): def test_query_truncated(): - response = MockResponseSpec(file2) - tbl = JPLSpec._parse_result(response) + response = MockResponseSpec(file2, body='Mol=28001 CO') + tbl = JPLSpec._parse_result(response, fallback_to_getmolecule=False, use_getmolecule=False) assert isinstance(tbl, Table) assert len(tbl) == 6 assert set(tbl.keys()) == set(['FREQ', 'ERR', 'LGINT', 'DR', 'ELO', 'GUP', @@ -109,8 +111,8 @@ def test_query_truncated(): def test_query_multi(): - response = MockResponseSpec(file3) - tbl = JPLSpec._parse_result(response) + response = MockResponseSpec(file3, body='Mol=28001 CO') + tbl = JPLSpec._parse_result(response, fallback_to_getmolecule=False, use_getmolecule=False) assert isinstance(tbl, Table) assert len(tbl) == 208 assert set(tbl.keys()) == set(['FREQ', 'ERR', 'LGINT', 'DR', 'ELO', 'GUP', @@ -230,7 +232,7 @@ def test_fallback_to_getmolecule_with_empty_response(): # Test with fallback disabled - should raise EmptyResponseError with pytest.raises(EmptyResponseError, match="Response was empty"): - JPLSpec._parse_result(mock_response, fallback_to_getmolecule=False) + JPLSpec._parse_result(mock_response, fallback_to_getmolecule=False, use_getmolecule=False) # Test with fallback enabled - should call get_molecule molecules = {'18003': ('H2O', {'FREQ': [100.0, 200.0]})} diff --git a/astroquery/linelists/jplspec/tests/test_jplspec_remote.py b/astroquery/linelists/jplspec/tests/test_jplspec_remote.py index db0df501ce..074fb598c0 100644 --- a/astroquery/linelists/jplspec/tests/test_jplspec_remote.py +++ b/astroquery/linelists/jplspec/tests/test_jplspec_remote.py @@ -13,7 +13,8 @@ def test_remote(): max_frequency=1000 * u.GHz, min_strength=-500, molecule="18003 H2O", - fallback_to_getmolecule=False) + fallback_to_getmolecule=False, + use_getmolecule=False) assert isinstance(tbl, Table) assert len(tbl) == 36 assert set(tbl.keys()) == set(['FREQ', 'ERR', 'LGINT', 'DR', 'ELO', 'GUP', @@ -62,7 +63,8 @@ def test_remote_regex(): max_frequency=1000 * u.GHz, min_strength=-500, molecule=("28001", "28002", "28003"), - fallback_to_getmolecule=False) + fallback_to_getmolecule=False, + use_getmolecule=False) assert isinstance(tbl, Table) assert len(tbl) == 16 assert set(tbl.keys()) == set(['FREQ', 'ERR', 'LGINT', 'DR', 'ELO', 'GUP',