Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #3647 +/- ##
==========================================
+ Coverage 73.45% 73.87% +0.41%
==========================================
Files 230 230
Lines 21424 21639 +215
==========================================
+ Hits 15738 15986 +248
+ Misses 5686 5653 -33 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
Note on CI: the devdeps job failure is unrelated to this PR |
|
Friendly ping @bsipocz — this PR is ready for review whenever someone has time. Since opening it I've synced with current main and all CI checks pass. I work on the Fermi data-server side, I'm happy to answer any questions about the API itself, and glad to adjust anything about the module's approach. |
|
@PazSheimy - yes, it's somewhere at the top of my review queue, but there are a few other big PRs that preceed it. |
bsipocz
left a comment
There was a problem hiding this comment.
Thanks for the PR!
I have some API related comments, mostly about aiming for API consistency with the other modules. I would think at least some of them need to be resolved before we merge this.
| ] | ||
|
|
||
| import warnings | ||
| warnings.warn("Experimental: Fermi-LAT has not yet been refactored to have " |
| # ------------------------------------------------------------------ | ||
| # submission | ||
| # ------------------------------------------------------------------ | ||
| def query_object_async(self, *args, **kwargs): |
There was a problem hiding this comment.
have we make args and kwargs explicit? Only list the required positional argument and accepted keyword arguments in the signature and not have a signature that swallows everything
There was a problem hiding this comment.
Also, this is a good spot to say we are moving away from the duplicated method and method_async approach, so I would say a substantial refactor like this PR can do that work and cleanup the API to only keep the non async methods around.
There was a problem hiding this comment.
Happy to do this cleanup
I'll make query_object() an explicitly defined method with the full signature and move submission/polling to private helpers.
One question: should query_object_async be removed outright, or kept for one release as a deprecated shim? (Worth noting the old query_object_async returned an HTML results-page URL that won't exist once the CGI retires so pre-existing callers break either way; the shim would just make it a deprecation warning instead of an AttributeError.)
| _COORD_PAIR_RE = re.compile(r'^\s*[-+]?\d+(\.\d*)?\s*,\s*[-+]?\d+(\.\d*)?\s*$') | ||
|
|
||
|
|
||
| @async_to_sync |
There was a problem hiding this comment.
Done. @async_to_sync is removed; query_object() is now an explicitly-defined method and submission/polling are private helpers. I kept query_object_async as a deprecated shim for now, matching the PR's existing deprecation pattern — happy to remove it outright instead if you prefer, it's a two-line change.
|
|
||
| Notes | ||
| ----- | ||
| Prior to the REST API migration this method returned the URL of an |
There was a problem hiding this comment.
No need to keep any mention of the old API here. If you want to you can keep some note boxes in the narrative documentation though and formal deprecations in the code itself.
| _raise_for_status(response, context=f"Fermi LAT results ({query_id})") | ||
| return response.json().get('files', []) | ||
|
|
||
| def wait_for_completion(self, query_id, *, check_frequency=None, |
There was a problem hiding this comment.
does this have to be user facing/public API?
There was a problem hiding this comment.
Made it private (_wait_for_completion) — users still control polling via the check_frequency/max_wait keywords on the public methods.
| time.sleep(check_frequency * 60) | ||
| elapsed_time += check_frequency | ||
|
|
||
| def get_file_urls(self, query_id, *, check_frequency=None, max_wait=None, |
There was a problem hiding this comment.
The method name here may need some coordination -- there are a couple other PRs in the works that deal with datalink urls and uris, but getting a conclusion on that should not be a blocker for this PR, it's just a heads up
There was a problem hiding this comment.
Understood
happy to rename to match whatever convention comes out of the datalink discussions, here or in a follow-up PR.
| return _fermi_format_coords(c) | ||
| except (u.UnitsError, TypeError): | ||
| raise Exception("Coordinates not specified correctly") | ||
| def _raise_for_status(response, *, context): |
There was a problem hiding this comment.
everywhere else we just use response.raise_for_status(), I would do that here, too rather than override it.
There was a problem hiding this comment.
Done — error detection now goes through response.raise_for_status(); the helper only catches the HTTPError and re-raises as RemoteServiceError with the server's JSON error message attached (that message is the #1849 fix, so I kept it). Happy to simplify to a bare raise_for_status() if you'd prefer.
|
|
||
| def __call__(self, result_url, *, check_frequency=1, verbose=False): | ||
| self.result_url = result_url | ||
| def _fermi_format_coords(c, *, coordsystem='J2000'): |
There was a problem hiding this comment.
do we really need this as a separate method, or could be merged into the previous one?
There was a problem hiding this comment.
Merged into _parse_coordinates.
fermi: fix docstring markup for _parse_args reference Co-authored-by: Brigitta Sipőcz <b.sipocz@gmail.com>
Fixes #3646
Closes #1849
Background
The Fermi LAT data server is replacing the LATDataQuery.cgi form endpoint
with a JSON REST API. Both will run in parallel for some time, but the REST
API will eventually replace LATDataQuery.cgi entirely, so this PR migrates
the module in advance for astroquery users.
https://fermi.gsfc.nasa.gov/ssc/data/access/lat/query/api/v1(docs). I work on the
Fermi data-server side and wrote this migration against the new API.
What changed
query_object_async()POSTs a JSON payload to{base}/queryand returnsthe server-assigned
query_id. Previously it returned the URL of an HTMLresults page
_parse_result()polls{base}/query/{id}/statusand reads{base}/query/{id}/results, so the synchronousquery_object()keeps its exact previous signature and return type(a list of FITS file URLs) — existing user scripts are unaffected.
requestsusage (everything now goes throughBaseQuery._request).get_status(),list_results(), andget_file_urls()(which accepts an optional
max_waitto bound the wait).zenithangle,coordsystem(J2000/B1950/Galactic- previously only J2000), and all-sky queries(radius > 60 deg with an observation window <= 24 h); literal
"RA,Dec"strings bypass name resolution.
RemoteServiceError;a query that never completes can be bounded with
max_wait(raises
TimeoutError).a JSON body like
{"error": "Invalid energy range ..."}is raised as aRemoteServiceErrorcarrying that message (closes the old TODO from Fail with useful failure messages on genuine failures. #1849).GetFermilatDatafile/get_fermilat_datafilekept as deprecated shimsdelegating to
get_file_urls().standard astroquery async/sync pattern.
docs/fermi/fermi.rst) and changelog updated.Testing
running/complete/failed, results), including payload-shape tests, error
paths, and a forward-compatibility test for absolute file URLs:
15 pass offline.
4/4 pass (basic query, async + status, zenith angle, all-sky).