1111import requests
1212import textwrap
1313
14+ from datetime import datetime , timedelta
15+ from pathlib import Path
16+
1417from astropy .config import paths
15- from astroquery import log
1618import astropy .units as u
1719from astropy .utils .console import ProgressBarOrSpinner
1820import astropy .utils .data
21+ from astropy .utils import deprecated
22+
23+ from astroquery import version , log , cache_conf
24+ from astroquery .utils import system_tools
1925
20- from . import version
21- from .utils import system_tools
2226
2327__all__ = ['BaseQuery' , 'QueryWithLogin' ]
2428
2529
2630def to_cache (response , cache_file ):
2731 log .debug ("Caching data to {0}" .format (cache_file ))
32+
2833 response = copy .deepcopy (response )
2934 if hasattr (response , 'request' ):
3035 for key in tuple (response .request .hooks .keys ()):
3136 del response .request .hooks [key ]
3237 with open (cache_file , "wb" ) as f :
33- pickle .dump (response , f )
38+ pickle .dump (response , f , protocol = 4 )
3439
3540
3641def _replace_none_iterable (iterable ):
@@ -102,20 +107,30 @@ def hash(self):
102107 return self ._hash
103108
104109 def request_file (self , cache_location ):
105- fn = os . path . join ( cache_location , self .hash () + ".pickle" )
110+ fn = cache_location . joinpath ( self .hash () + ".pickle" )
106111 return fn
107112
108- def from_cache (self , cache_location ):
113+ def from_cache (self , cache_location , cache_timeout ):
109114 request_file = self .request_file (cache_location )
110115 try :
111- with open (request_file , "rb" ) as f :
112- response = pickle .load (f )
113- if not isinstance (response , requests .Response ):
116+ if cache_timeout is None :
117+ expired = False
118+ else :
119+ current_time = datetime .utcnow ()
120+ cache_time = datetime .utcfromtimestamp (request_file .stat ().st_mtime )
121+ expired = current_time - cache_time > timedelta (seconds = cache_timeout )
122+ if not expired :
123+ with open (request_file , "rb" ) as f :
124+ response = pickle .load (f )
125+ if not isinstance (response , requests .Response ):
126+ response = None
127+ else :
128+ log .debug (f"Cache expired for { request_file } ..." )
114129 response = None
115130 except FileNotFoundError :
116131 response = None
117132 if response :
118- log .debug ("Retrieving data from {0}" .format (request_file ))
133+ log .debug ("Retrieved data from {0}" .format (request_file ))
119134 return response
120135
121136 def remove_cache_file (self , cache_location ):
@@ -125,8 +140,8 @@ def remove_cache_file(self, cache_location):
125140 """
126141 request_file = self .request_file (cache_location )
127142
128- if os . path . exists ( request_file ) :
129- os . remove ( request_file )
143+ if request_file . exists :
144+ request_file . unlink ( )
130145 else :
131146 raise FileNotFoundError (f"Tried to remove cache file { request_file } but "
132147 "it does not exist" )
@@ -173,11 +188,8 @@ def __init__(self):
173188 .format (vers = version .version ,
174189 olduseragent = S .headers ['User-Agent' ]))
175190
176- self .cache_location = os .path .join (
177- paths .get_cache_dir (), 'astroquery' ,
178- self .__class__ .__name__ .split ("Class" )[0 ])
179- os .makedirs (self .cache_location , exist_ok = True )
180- self ._cache_active = True
191+ self .name = self .__class__ .__name__ .split ("Class" )[0 ]
192+ self ._cache_location = None
181193
182194 def __call__ (self , * args , ** kwargs ):
183195 """ init a fresh copy of self """
@@ -217,9 +229,28 @@ def _response_hook(self, response, *args, **kwargs):
217229 f"-----------------------------------------" , '\t ' )
218230 log .log (5 , f"HTTP response\n { response_log } " )
219231
232+ @property
233+ def cache_location (self ):
234+ cl = self ._cache_location or Path (paths .get_cache_dir (), 'astroquery' , self .name )
235+ cl .mkdir (parents = True , exist_ok = True )
236+ return cl
237+
238+ @cache_location .setter
239+ def cache_location (self , loc ):
240+ self ._cache_location = Path (loc )
241+
242+ def reset_cache_location (self ):
243+ """Resets the cache location to the default astropy cache"""
244+ self ._cache_location = None
245+
246+ def clear_cache (self ):
247+ """Removes all cache files."""
248+ for fle in self .cache_location .glob ("*.pickle" ):
249+ fle .unlink ()
250+
220251 def _request (self , method , url ,
221252 params = None , data = None , headers = None ,
222- files = None , save = False , savedir = '' , timeout = None , cache = True ,
253+ files = None , save = False , savedir = '' , timeout = None , cache = None ,
223254 stream = False , auth = None , continuation = True , verify = True ,
224255 allow_redirects = True ,
225256 json = None , return_response_on_save = False ):
@@ -253,6 +284,7 @@ def _request(self, method, url,
253284 somewhere other than `BaseQuery.cache_location`
254285 timeout : int
255286 cache : bool
287+ Optional, if specified, overrides global cache settings.
256288 verify : bool
257289 Verify the server's TLS certificate?
258290 (see http://docs.python-requests.org/en/master/_modules/requests/sessions/?highlight=verify)
@@ -278,12 +310,16 @@ def _request(self, method, url,
278310 is True.
279311 """
280312
313+ if cache is None : # Global caching not overridden
314+ cache = cache_conf .cache_active
315+
281316 if save :
282317 local_filename = url .split ('/' )[- 1 ]
283318 if os .name == 'nt' :
284319 # Windows doesn't allow special characters in filenames like
285320 # ":" so replace them with an underscore
286321 local_filename = local_filename .replace (':' , '_' )
322+
287323 local_filepath = os .path .join (savedir or self .cache_location or '.' , local_filename )
288324
289325 response = self ._download_file (url , local_filepath , cache = cache , timeout = timeout ,
@@ -298,14 +334,14 @@ def _request(self, method, url,
298334 else :
299335 query = AstroQuery (method , url , params = params , data = data , headers = headers ,
300336 files = files , timeout = timeout , json = json )
301- if (( self . cache_location is None ) or ( not self . _cache_active ) or ( not cache )) :
302- with suspend_cache ( self ):
337+ if not cache :
338+ with cache_conf . set_temp ( "cache_active" , False ):
303339 response = query .request (self ._session , stream = stream ,
304340 auth = auth , verify = verify ,
305341 allow_redirects = allow_redirects ,
306342 json = json )
307343 else :
308- response = query .from_cache (self .cache_location )
344+ response = query .from_cache (self .cache_location , cache_conf . cache_timeout )
309345 if not response :
310346 response = query .request (self ._session ,
311347 self .cache_location ,
@@ -315,6 +351,7 @@ def _request(self, method, url,
315351 verify = verify ,
316352 json = json )
317353 to_cache (response , query .request_file (self .cache_location ))
354+
318355 self ._last_query = query
319356 return response
320357
@@ -336,6 +373,7 @@ def _download_file(self, url, local_filepath, timeout=None, auth=None,
336373 supports HTTP "range" requests, the download will be continued
337374 where it left off.
338375 cache : bool
376+ Cache downloaded file. Defaults to False.
339377 method : "GET" or "POST"
340378 head_safe : bool
341379 """
@@ -439,19 +477,21 @@ def _download_file(self, url, local_filepath, timeout=None, auth=None,
439477 return response
440478
441479
480+ @deprecated (since = "v0.4.7" , message = ("The suspend_cache function is deprecated,"
481+ "Use the conf set_temp function instead." ))
442482class suspend_cache :
443483 """
444484 A context manager that suspends caching.
445485 """
446486
447- def __init__ (self , obj ):
448- self .obj = obj
487+ def __init__ (self , obj = None ):
488+ self .original_cache_setting = cache_conf . cache_active
449489
450490 def __enter__ (self ):
451- self . obj . _cache_active = False
491+ cache_conf . cache_active = False
452492
453493 def __exit__ (self , exc_type , exc_value , traceback ):
454- self . obj . _cache_active = True
494+ cache_conf . cache_active = self . original_cache_setting
455495 return False
456496
457497
@@ -507,7 +547,7 @@ def _login(self, *args, **kwargs):
507547 pass
508548
509549 def login (self , * args , ** kwargs ):
510- with suspend_cache ( self ):
550+ with cache_conf . set_temp ( "cache_active" , False ):
511551 self ._authenticated = self ._login (* args , ** kwargs )
512552 return self ._authenticated
513553
0 commit comments