1818
1919import astropy .units as u
2020import astropy .coordinates as coord
21- from botocore .exceptions import ClientError , BotoCoreError
2221
2322from astropy .table import Table , Row , vstack
2423from astroquery import log
2726
2827from ..utils import async_to_sync
2928from ..utils .class_or_instance import class_or_instance
30- from ..exceptions import (InvalidQueryError , RemoteServiceError , NoResultsWarning , InputWarning )
29+ from ..exceptions import (InvalidQueryError , RemoteServiceError , NoResultsWarning , InputWarning , CloudAccessWarning )
3130
32- from . import utils
31+ from . import utils , conf
3332from .core import MastQueryWithLogin
3433
35- __all__ = ['Observations' , 'ObservationsClass' ,
36- 'MastClass' , 'Mast' ]
34+ try :
35+ from botocore .exceptions import ClientError , BotoCoreError
36+ except ImportError :
37+ ClientError = BotoCoreError = ()
38+
39+ __all__ = ['Observations' , 'ObservationsClass' , 'MastClass' , 'Mast' ]
3740
3841
3942@async_to_sync
@@ -51,6 +54,24 @@ class ObservationsClass(MastQueryWithLogin):
5154 _caom_filtered = 'Mast.Caom.Filtered'
5255 _caom_products = 'Mast.Caom.Products'
5356
57+ def __init__ (self , mast_token = None ):
58+ super ().__init__ (mast_token )
59+ self ._cloud_enabled_explicitly = None # Track whether cloud access was explicitly enabled by the user
60+
61+ def _ensure_cloud_access (self ):
62+ """Ensure cloud access is initialized if appropriate."""
63+ # User explicitly disabled
64+ if self ._cloud_enabled_explicitly is False :
65+ return
66+
67+ # Already initialized
68+ if self ._cloud_connection is not None :
69+ return
70+
71+ # Default behavior is to enable cloud access if the config option is set, so we check that here
72+ if self ._cloud_enabled_explicitly is None and conf .enable_cloud_dataset :
73+ self .enable_cloud_dataset (_internal = True )
74+
5475 def _parse_result (self , responses , * , verbose = False ): # Used by the async_to_sync decorator functionality
5576 """
5677 Parse the results of a list of `~requests.Response` objects and returns an `~astropy.table.Table` of results.
@@ -180,7 +201,7 @@ def _parse_caom_criteria(self, *, resolver=None, **criteria):
180201
181202 return position , mashup_filters
182203
183- def enable_cloud_dataset (self , provider = "AWS" , profile = None , verbose = True ):
204+ def enable_cloud_dataset (self , provider = "AWS" , profile = None , verbose = True , * , _internal = False ):
184205 """
185206 Enable downloading public files from S3 instead of MAST.
186207 Requires the boto3 library to function.
@@ -196,13 +217,21 @@ def enable_cloud_dataset(self, provider="AWS", profile=None, verbose=True):
196217 Default True.
197218 Logger to display extra info and warning.
198219 """
199- self ._cloud_connection = CloudAccess (provider , profile , verbose )
220+ try :
221+ self ._cloud_connection = CloudAccess (provider , profile , verbose )
222+ if not _internal :
223+ self ._cloud_enabled_explicitly = True
224+ except ImportError as e :
225+ # boto3 or botocore is not installed
226+ self ._cloud_connection = None
227+ warnings .warn (e .msg , CloudAccessWarning )
200228
201229 def disable_cloud_dataset (self ):
202230 """
203231 Disables downloading public files from S3 instead of MAST.
204232 """
205233 self ._cloud_connection = None
234+ self ._cloud_enabled_explicitly = False
206235
207236 @class_or_instance
208237 def query_region_async (self , coordinates , * , radius = 0.2 * u .deg , pagesize = None , page = None ):
@@ -656,6 +685,9 @@ def download_file(self, uri, *, local_path=None, base_url=None, cache=True, clou
656685 url : str
657686 The full url download path
658687 """
688+ # Ensure cloud access is enabled
689+ self ._ensure_cloud_access ()
690+
659691 if not uri or not isinstance (uri , str ):
660692 raise InvalidQueryError ("A valid data product URI must be provided." )
661693
@@ -693,8 +725,9 @@ def download_file(self, uri, *, local_path=None, base_url=None, cache=True, clou
693725 NoResultsWarning )
694726 return 'SKIPPED' , None , None
695727
696- warnings .warn (f'The product { uri } was not found in the cloud. '
697- 'Falling back to MAST download.' , InputWarning )
728+ if self ._cloud_enabled_explicitly :
729+ warnings .warn (f'The product { uri } was not found in the cloud. '
730+ 'Falling back to MAST download.' , InputWarning )
698731 self ._download_file (escaped_url , local_path , cache = cache , head_safe = True , verbose = verbose )
699732 except (ClientError , BotoCoreError ) as ex :
700733 # Should be in cloud, but download failed
@@ -703,8 +736,9 @@ def download_file(self, uri, *, local_path=None, base_url=None, cache=True, clou
703736 NoResultsWarning )
704737 return 'SKIPPED' , None , None
705738
706- warnings .warn (f'Could not download { uri } from cloud: { ex } . Falling back to MAST download.' ,
707- InputWarning )
739+ if self ._cloud_enabled_explicitly :
740+ warnings .warn (f'Could not download { uri } from cloud: { ex } . Falling back to MAST download.' ,
741+ InputWarning )
708742 self ._download_file (escaped_url , local_path , cache = cache , head_safe = True , verbose = verbose )
709743 else :
710744 if cloud_only :
@@ -771,7 +805,6 @@ def _download_files(self, products, base_dir, *, flat=False, cache=True, cloud_o
771805 status , msg , url = 'ERROR' , None , None
772806
773807 cloud_uri = cloud_uri_map .get (mast_uri ) if cloud_uri_map else None
774-
775808 if cloud_uri :
776809 try :
777810 self ._cloud_connection .download_file_from_cloud (cloud_uri , local_path , cache , verbose )
@@ -784,8 +817,9 @@ def _download_files(self, products, base_dir, *, flat=False, cache=True, cloud_o
784817 status = 'SKIPPED'
785818 msg = str (ex )
786819 else :
787- warnings .warn (f'Could not download { cloud_uri } from cloud: { ex } . '
788- 'Falling back to MAST download.' , InputWarning )
820+ if self ._cloud_enabled_explicitly :
821+ warnings .warn (f'Could not download { cloud_uri } from cloud: { ex } . '
822+ 'Falling back to MAST download.' , InputWarning )
789823 status , msg , url = self .download_file (mast_uri , local_path = local_path , cache = cache ,
790824 force_on_prem = True , verbose = verbose )
791825 else :
@@ -797,8 +831,9 @@ def _download_files(self, products, base_dir, *, flat=False, cache=True, cloud_o
797831 status = 'SKIPPED'
798832 msg = 'Product not found in cloud'
799833 else :
800- warnings .warn (f'The product { mast_uri } was not found in the cloud. '
801- 'Falling back to MAST download.' , InputWarning )
834+ if self ._cloud_enabled_explicitly :
835+ warnings .warn (f'The product { mast_uri } was not found in the cloud. '
836+ 'Falling back to MAST download.' , InputWarning )
802837 status , msg , url = self .download_file (mast_uri , local_path = local_path , cache = cache ,
803838 force_on_prem = True , verbose = verbose )
804839 else :
@@ -899,6 +934,9 @@ def download_products(self, products, *, download_dir=None, flat=False,
899934 response : `~astropy.table.Table`
900935 The manifest of files downloaded, or status of files on disk if curl option chosen.
901936 """
937+ # Ensure cloud access is enabled
938+ self ._ensure_cloud_access ()
939+
902940 # If the products list is a row we need to cast it as a table
903941 if isinstance (products , Row ):
904942 products = Table (products , masked = True )
@@ -961,6 +999,9 @@ def list_cloud_datasets(self):
961999 response : list
9621000 List of dataset prefixes that support cloud data access.
9631001 """
1002+ # Ensure cloud access is enabled
1003+ self ._ensure_cloud_access ()
1004+
9641005 if self ._cloud_connection is None :
9651006 raise RemoteServiceError (
9661007 'Please enable anonymous cloud access by calling `enable_cloud_dataset` method. '
@@ -1027,6 +1068,8 @@ def get_cloud_uris(self, data_products=None, *, include_bucket=True, full_url=Fa
10271068 List of URIs generated from the data products. May contain entries that are None
10281069 if data_products includes products not found in the cloud.
10291070 """
1071+ # Ensure cloud access is enabled
1072+ self ._ensure_cloud_access ()
10301073
10311074 if self ._cloud_connection is None :
10321075 raise RemoteServiceError (
@@ -1110,6 +1153,8 @@ def get_cloud_uri(self, data_product, *, include_bucket=True, full_url=False):
11101153 Cloud URI generated from the data product. If the product cannot be
11111154 found in the cloud, None is returned.
11121155 """
1156+ # Ensure cloud access is enabled
1157+ self ._ensure_cloud_access ()
11131158
11141159 if self ._cloud_connection is None :
11151160 raise RemoteServiceError (
0 commit comments