From 47a33ff5e5b0ffffd0b289e7f4149f464dbd2f44 Mon Sep 17 00:00:00 2001 From: "Adam Ginsburg (keflavich)" Date: Wed, 4 Mar 2026 15:37:02 -0500 Subject: [PATCH 1/6] use get_molecule by default in jplspec - server is completely down now, may never come back. wayback machine URL added to config --- astroquery/linelists/jplspec/__init__.py | 6 ++++++ astroquery/linelists/jplspec/core.py | 15 +++++++++++---- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/astroquery/linelists/jplspec/__init__.py b/astroquery/linelists/jplspec/__init__.py index 5b03985e7f..1f7b76ad4c 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..547d135044 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,7 +208,7 @@ 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 'Zero lines were found' in response.text or use_getmolecule: if fallback_to_getmolecule: self.lookup_ids = build_lookup() payload = parse_qs(response.request.body) @@ -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, From 3f36ddaa3c66cc6e679ee23d327e99f815444ce0 Mon Sep 17 00:00:00 2001 From: "Adam Ginsburg (keflavich)" Date: Thu, 9 Apr 2026 10:20:48 -0400 Subject: [PATCH 2/6] whitespace --- astroquery/linelists/jplspec/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/astroquery/linelists/jplspec/__init__.py b/astroquery/linelists/jplspec/__init__.py index 1f7b76ad4c..49d6018ce5 100644 --- a/astroquery/linelists/jplspec/__init__.py +++ b/astroquery/linelists/jplspec/__init__.py @@ -18,7 +18,7 @@ class Conf(_config.ConfigNamespace): 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' + 'JPL FTP Catalog URL' ) timeout = _config.ConfigItem( From 20d6912dd13384b15f506b08a331dc8c7a4b930d Mon Sep 17 00:00:00 2001 From: "Adam Ginsburg (keflavich)" Date: Thu, 9 Apr 2026 22:16:46 -0400 Subject: [PATCH 3/6] test fixes --- astroquery/linelists/jplspec/core.py | 2 +- .../linelists/jplspec/tests/test_jplspec.py | 18 ++++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/astroquery/linelists/jplspec/core.py b/astroquery/linelists/jplspec/core.py index 547d135044..30ac489c39 100644 --- a/astroquery/linelists/jplspec/core.py +++ b/astroquery/linelists/jplspec/core.py @@ -209,7 +209,7 @@ def _parse_result(self, response, *, verbose=False, fallback_to_getmolecule=Fals """ if 'Zero lines were found' in response.text or use_getmolecule: - if fallback_to_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']] 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]})} From 6f4f6c4dc47e7dddd945a2b5e34b1c62fb96848f Mon Sep 17 00:00:00 2001 From: "Adam Ginsburg (keflavich)" Date: Fri, 10 Apr 2026 14:31:02 -0400 Subject: [PATCH 4/6] fix some more tests --- astroquery/linelists/jplspec/tests/test_jplspec_remote.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) 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', From 1d8d9bf9afedeb15cdd726677b7893ccd41d863e Mon Sep 17 00:00:00 2001 From: "Adam Ginsburg (keflavich)" Date: Fri, 10 Apr 2026 19:55:33 -0400 Subject: [PATCH 5/6] add changelog --- CHANGES.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.rst b/CHANGES.rst index eabaaef26d..433f20c637 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 [#3510] linelists.jplspec From a94b6ec48ae94978cbae8da879521f6bd2f41ed3 Mon Sep 17 00:00:00 2001 From: "Adam Ginsburg (keflavich)" Date: Fri, 10 Apr 2026 21:05:47 -0400 Subject: [PATCH 6/6] fix changelog entry ID --- CHANGES.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 433f20c637..4d4a9782b4 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -103,7 +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 [#3510] +- Fallback added to go to the wayback machine [#3547] linelists.jplspec