Skip to content

Commit eb5a1b2

Browse files
author
alex
committed
Adding read_product function to mast.observations to stream asdf and fits data products from s3
1 parent 25c4840 commit eb5a1b2

2 files changed

Lines changed: 66 additions & 0 deletions

File tree

CHANGES.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,8 @@ mast
147147
- The cloud dataset in ``Observations`` is now enabled by default if the ``boto3`` and ``botocore`` packages are installed. This
148148
default can be overridden by setting the ``enable_cloud_dataset`` configuration option to False. [#3534]
149149

150+
- Adding in ability to read FITS and ASDF dataproducts to memory from s3:// using ``Observations.read_product()`` function. [#3561]
151+
150152

151153
jplspec
152154
^^^^^^^

astroquery/mast/observations.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@
1111
import time
1212
import os
1313
from urllib.parse import quote
14+
from importlib.metadata import version
1415

1516
import numpy as np
1617
import astropy.units as u
18+
from astropy.io import fits
1719
import astropy.coordinates as coord
1820
from requests import HTTPError
1921
from astropy.table import Table, Row, vstack
@@ -44,6 +46,15 @@
4446
'`~astroquery.mast.ObservationsClass.enable_cloud_dataset` method.'
4547
)
4648

49+
asdf_packages = ["asdf", "s3fs", "fsspec", "lz4", "gwcs"]
50+
fits_packages = ['s3fs', 'fsspec']
51+
52+
try:
53+
import asdf
54+
import s3fs
55+
except ImportError:
56+
pass
57+
4758

4859
@async_to_sync
4960
class ObservationsClass(MastQueryWithLogin):
@@ -1203,6 +1214,59 @@ def get_unique_product_list(self, observations, *, batch_size=500):
12031214
log.info("To return all products, use `Observations.get_product_list`")
12041215
return unique_products
12051216

1217+
# TODO: Need to inlcude way to parse if it is a MAST on prem URL and handle the streaming of that
1218+
def read_product(self, product_path, read_as="auto", ignore_unrecognized=False):
1219+
"""
1220+
Read a product from Open S3 bucket to memory. Currently can handle FITS and ASDF product types.
1221+
1222+
Parameters
1223+
----------
1224+
product_path: str
1225+
URI to the product in open bucket.
1226+
read_as: str, optional
1227+
How to read the file. Currently only .fits and .asdf is supported by "auto". Defaults to "auto".
1228+
ignore_unrecognized: bool
1229+
Tells asdf.open() to include or ignore warnings from unrecognized asdf tags. Defaults to False
1230+
1231+
Returns
1232+
-------
1233+
object
1234+
FITS or ASDF object.
1235+
"""
1236+
if read_as == "auto":
1237+
if product_path.endswith(".fits"):
1238+
for package in fits_packages:
1239+
try:
1240+
version(package)
1241+
except ModuleNotFoundError:
1242+
log.debug(f"Missing Required Package: {package}")
1243+
return
1244+
try:
1245+
return fits.open(product_path, fsspec_kwargs={"anon": True})
1246+
except Exception as e:
1247+
log.exception(f"Failed to open FITS File: {product_path} {e}")
1248+
1249+
# Read logic for ASDF
1250+
elif product_path.endswith(".asdf"):
1251+
# Check all required modules are available
1252+
for package in asdf_packages:
1253+
try:
1254+
version(package)
1255+
except ModuleNotFoundError:
1256+
log.debug(f"Missing Required Package: {package}")
1257+
return
1258+
1259+
try:
1260+
fs = s3fs.S3FileSystem(anon=True)
1261+
with fs.open(product_path, 'rb') as s3_file:
1262+
af = asdf.open(s3_file, ignore_unrecognized_tag=ignore_unrecognized)
1263+
return af
1264+
except Exception as e:
1265+
log.exception(f"Failed to open ASD File: {product_path} {e}")
1266+
else:
1267+
log.error("Unsupported extension type")
1268+
return
1269+
12061270

12071271
@async_to_sync
12081272
class MastClass(MastQueryWithLogin):

0 commit comments

Comments
 (0)