def generate_cfg_example(config_name, cfgpath='examples/configs', **kwargs): """Generate config file documentation for a given config name. If found, it will be a Python code block of the contents. If not found, it will have a generic message that the config is not available. Parameters ---------- config_name : str Config name that is attached to the configuration file. This is the same as input for ``prefs.createCategory()``. For example, ``'general'``, ``'channel_Image'``, or ``'plugin_Zoom'``. cfgpath : str Where it is within package data. kwargs : dict Optional keywords for :func:`~astropy.utils.data.get_pkg_data_contents`. Returns ------- docstr : str Docstring to be inserted into documentation. """ cfgname = config_name + '.cfg' try: cfgfile = _find_pkg_data_path(os.path.join(cfgpath, cfgname), **kwargs) with open(cfgfile) as f: cfgdata = f.readlines() except Exception as e: warnings.warn(str(e), AstropyUserWarning) return '' homepath = '~' # Symbol for HOME for doc only, not actual code userfile = os.path.join(homepath, '.ginga', cfgname) docstring = io.StringIO() docstring.write("""It is customizable using ``{0}``, where ``{1}`` is your HOME directory: .. code-block:: Python """.format(userfile, homepath)) for line in cfgdata: line = line.strip() if len(line) == 0: docstring.write('\n') # Prevent trailing spaces else: docstring.write(' {0}\n'.format(line)) return docstring.getvalue()
def test_download_mirror_cache(): import pathlib import shelve from astropy.utils.data import _find_pkg_data_path, download_file, get_cached_urls main_url = pathlib.Path( _find_pkg_data_path(os.path.join('data', 'dataurl'))).as_uri() + '/' mirror_url = pathlib.Path( _find_pkg_data_path(os.path.join( 'data', 'dataurl_mirror'))).as_uri() + '/' # noqa main_file = main_url + 'index.html' mirror_file = mirror_url + 'index.html' # Temporarily change data.conf. # This also test https://github.com/astropy/astropy/pull/8163 because # urlopen() on a local dir URI also gives URLError. with conf.set_temp('dataurl', main_url): with conf.set_temp('dataurl_mirror', mirror_url): # "Download" files by rerouting URLs to local URIs. download_file(main_file, cache=True) download_file(mirror_file, cache=True) # Now test that download_file looks in mirror's cache before # download. # https://github.com/astropy/astropy/issues/6982 dldir, urlmapfn = _get_download_cache_locs() with shelve.open(urlmapfn) as url2hash: del url2hash[main_file] # Comparing hash makes sure they download the same file # but does not guarantee they were downloaded from the same URL. assert (download_file(main_file, cache=True) == download_file(mirror_file, cache=True)) # This has to be called after the last download to obtain # an accurate view of cached URLs. # This is to ensure that main_file was not re-downloaded # unnecessarily. # This test also tests for "assert TESTURL in get_cached_urls()". c_urls = get_cached_urls() assert (mirror_file in c_urls) and (main_file not in c_urls)
def test_download_mirror_cache(): import pathlib import shelve from astropy.utils.data import _find_pkg_data_path, download_file, get_cached_urls main_url = pathlib.Path( _find_pkg_data_path(os.path.join('data', 'dataurl'))).as_uri() + '/' mirror_url = pathlib.Path( _find_pkg_data_path(os.path.join('data', 'dataurl_mirror'))).as_uri() + '/' # noqa main_file = main_url + 'index.html' mirror_file = mirror_url + 'index.html' # Temporarily change data.conf. # This also test https://github.com/astropy/astropy/pull/8163 because # urlopen() on a local dir URI also gives URLError. with conf.set_temp('dataurl', main_url): with conf.set_temp('dataurl_mirror', mirror_url): # "Download" files by rerouting URLs to local URIs. download_file(main_file, cache=True) download_file(mirror_file, cache=True) # Now test that download_file looks in mirror's cache before # download. # https://github.com/astropy/astropy/issues/6982 dldir, urlmapfn = _get_download_cache_locs() with shelve.open(urlmapfn) as url2hash: del url2hash[main_file] # Comparing hash makes sure they download the same file # but does not guarantee they were downloaded from the same URL. assert (download_file(main_file, cache=True) == download_file(mirror_file, cache=True)) # This has to be called after the last download to obtain # an accurate view of cached URLs. # This is to ensure that main_file was not re-downloaded # unnecessarily. # This test also tests for "assert TESTURL in get_cached_urls()". c_urls = get_cached_urls() assert (mirror_file in c_urls) and (main_file not in c_urls)
def setup_class(self): self.datadir = 'data' test_vos_path = _find_pkg_data_path(self.datadir) + os.sep # Convert to a proper file:// URL--on *NIXen this is not necessary but # Windows paths will blow up if we don't do this. test_vos_path = '/'.join(test_vos_path.split(os.sep)) if not test_vos_path.startswith('/'): test_vos_path = '/' + test_vos_path cs_conf.vos_baseurl = 'file://' + test_vos_path self.r = inspect.ConeSearchResults()
def test_skip_hidden(): path = data._find_pkg_data_path('data') for root, dirs, files in os.walk(path): assert '.hidden_file.txt' in files assert 'local.dat' in files # break after the first level since the data dir contains some other # subdirectories that don't have these files break for root, dirs, files in misc.walk_skip_hidden(path): assert '.hidden_file.txt' not in files assert 'local.dat' in files break
def setup_class(self): # (earthshine.fits * 0.5) + # rn(spec(Zodi.fits), band(V), 22.7, vegamag) + # (el1215a.fits * 0.5) + # (el1302a.fits * 0.5) + # (el1356a.fits * 0.5) + # (el2471a.fits * 0.5) path = _find_pkg_data_path(os.path.join('data', 'generic'), package='pysynphot') spz = FileSourceSpectrum(os.path.join(path, 'Zodi.fits')).renorm( 22.7, 'vegamag', ObsBandpass('V')) self.sp = ((FileSourceSpectrum(os.path.join(path, 'earthshine.fits')) + FileSourceSpectrum(os.path.join(path, 'el1215a.fits')) + FileSourceSpectrum(os.path.join(path, 'el1302a.fits')) + FileSourceSpectrum(os.path.join(path, 'el1356a.fits')) + FileSourceSpectrum(os.path.join(path, 'el2471a.fits'))) * 0.5 + spz) self.bp = ObsBandpass('acs,sbc,F140LP')
def _iraf_decode(irafdir): """Decode IRAF dir shortcut.""" from .config import conf # Put here to avoid circular import error global _irafconvdata irafdir = irafdir.lower() if irafdir == 'synphot': # Local data path = _find_pkg_data_path('data') elif irafdir == 'crrefer': # Root dir path = conf.rootdir else: # Read from file # Avoid repeated I/O but do not load if not used if _irafconvdata is None: _irafconvdata = ascii.read(irafconvert(conf.irafshortcutfile)) mask = _irafconvdata['IRAFNAME'] == irafdir if not np.any(mask): raise KeyError('IRAF shortcut {0} not found in ' '{1}.'.format(irafdir, conf.irafshortcutfile)) relpath = os.path.normpath(_irafconvdata['RELPATH'][mask][0]) path = os.path.join(conf.rootdir, relpath) return path
def get_isochrone_orbits(n_orbits=100, seed=42): np.random.seed(seed) cache_file = os.path.abspath(_find_pkg_data_path("isochrone_orbits.h5")) if os.path.exists(cache_file): return cache_file # integration parameters nperiods = 50 nsteps_per_period = 512 nsteps = nperiods * nsteps_per_period # --------------------------------------------------------------- # first data set are orbits integrated in an Isochrone potential # potential parameters m = 1E11 b = 5. pot = gp.IsochronePotential(m=m, b=b, units=galactic) # distance r = 10. # MAGIC NUMBER # velocity magnitude menc = pot.mass_enclosed([r, 0., 0.]) vc = np.sqrt(pot.G * menc.value / r) vmag = np.random.normal(vc - 0.01, vc * 0.01, size=n_orbits) # for position phi = np.random.uniform(0, 2 * np.pi, size=n_orbits) x = r * np.cos(phi) y = r * np.sin(phi) z = np.zeros_like(x) vx = -vmag * np.sin(phi) vy = vmag * np.cos(phi) vz = np.zeros_like(x) pos = np.vstack((x, y, z)) * galactic['length'] vel = np.vstack((vx, vy, vz)) * galactic['length'] / galactic['time'] # compute true actions, true frequencies w0 = gd.CartesianPhaseSpacePosition(pos=pos, vel=vel) act, ang, frq = pot.action_angle(w0) # reshape down to 2d act = act[:2] ang = ang[:2] frq = frq[:2] true_periods = (2 * np.pi / frq).max(axis=0) # write to file f = h5py.File(cache_file, "w") truth = f.create_group("initial") truth.create_dataset("actions", act.shape, dtype='f8', data=act.value) truth["actions"].attrs['unit'] = str(act.unit) truth.create_dataset("angles", ang.shape, dtype='f8', data=ang.value) truth["angles"].attrs['unit'] = str(ang.unit) truth.create_dataset("freqs", frq.shape, dtype='f8', data=frq.value) truth["freqs"].attrs['unit'] = str(frq.unit) # integrate them orbits -- have to do it this way to make sure # dt is right all_x = np.zeros((2, nsteps + 1, n_orbits)) all_v = np.zeros((2, nsteps + 1, n_orbits)) all_t = np.zeros((nsteps + 1, n_orbits)) for i, period in enumerate(true_periods): print("Orbit {0}".format(i)) dt = period / nsteps_per_period orbit = pot.integrate_orbit(w0[i], dt=dt, nsteps=nsteps, Integrator=DOPRI853Integrator) all_x[..., i] = orbit.pos.xyz.decompose(pot.units).value[:2] all_v[..., i] = orbit.vel.d_xyz.decompose(pot.units).value[:2] all_t[..., i] = orbit.t.decompose(pot.units).value orb = f.create_group("orbits") orb.create_dataset("t", all_t.shape, dtype='f8', data=all_t) orb['t'].attrs['unit'] = str(pot.units['time']) orb.create_dataset("x", all_x.shape, dtype='f8', data=all_x) orb['x'].attrs['unit'] = str(pot.units['length']) orb.create_dataset("v", all_v.shape, dtype='f8', data=all_v) orb['v'].attrs['unit'] = str(pot.units['length'] / pot.units['time']) f.flush() f.close() return cache_file
def get_harmonic_oscillator_orbits(n_orbits=100, seed=42): np.random.seed(seed) cache_file = os.path.abspath(_find_pkg_data_path("ho_orbits.h5")) if os.path.exists(cache_file): return cache_file # integration parameters nperiods = 50 nsteps_per_period = 256 nsteps = nperiods * nsteps_per_period # --------------------------------------------------------------- # first data set are orbits integrated in an Isochrone potential # potential parameters omegas = 2 * np.pi / np.array([150., 71., 201.]) pot = gp.HarmonicOscillatorPotential(omega=omegas, units=galactic) pos = np.zeros((3, n_orbits)) pos[:2] = np.random.uniform(-10, 10, size=(2, n_orbits)) pos = pos * galactic['length'] vel = np.zeros((3, n_orbits)) * galactic['length'] / galactic['time'] # compute true actions, true frequencies w0 = gd.CartesianPhaseSpacePosition(pos=pos, vel=vel) act, ang, frq = pot.action_angle(w0) frq = np.repeat(frq[:, np.newaxis], axis=1, repeats=n_orbits) # reshape down to 2d act = act[:2] ang = ang[:2] frq = frq[:2] true_periods = (2 * np.pi / frq).max(axis=0) # write to file f = h5py.File(cache_file, "w") truth = f.create_group("initial") truth.create_dataset("actions", act.shape, dtype='f8', data=act.value) truth["actions"].attrs['unit'] = str(act.unit) truth.create_dataset("angles", ang.shape, dtype='f8', data=ang.value) truth["angles"].attrs['unit'] = str(ang.unit) truth.create_dataset("freqs", frq.shape, dtype='f8', data=frq.value) truth["freqs"].attrs['unit'] = str(frq.unit) # integrate them orbits -- have to do it this way to make sure # dt is right all_x = np.zeros((2, nsteps + 1, n_orbits)) all_v = np.zeros((2, nsteps + 1, n_orbits)) all_t = np.zeros((nsteps + 1, n_orbits)) for i, period in enumerate(true_periods): print("Orbit {0}".format(i)) dt = period / nsteps_per_period orbit = pot.integrate_orbit(w0[i], dt=dt, nsteps=nsteps, Integrator=DOPRI853Integrator) all_x[..., i] = orbit.pos.xyz.decompose(pot.units).value[:2] all_v[..., i] = orbit.vel.d_xyz.decompose(pot.units).value[:2] all_t[..., i] = orbit.t.decompose(pot.units).value orb = f.create_group("orbits") orb.create_dataset("t", all_t.shape, dtype='f8', data=all_t) orb['t'].attrs['unit'] = str(pot.units['time']) orb.create_dataset("x", all_x.shape, dtype='f8', data=all_x) orb['x'].attrs['unit'] = str(pot.units['length']) orb.create_dataset("v", all_v.shape, dtype='f8', data=all_v) orb['v'].attrs['unit'] = str(pot.units['length'] / pot.units['time']) f.flush() f.close() return cache_file
def get_harmonic_oscillator_orbits(n_orbits=100, seed=42): np.random.seed(seed) cache_file = os.path.abspath(_find_pkg_data_path("ho_orbits.h5")) if os.path.exists(cache_file): return cache_file # integration parameters nperiods = 50 nsteps_per_period = 256 nsteps = nperiods * nsteps_per_period # --------------------------------------------------------------- # first data set are orbits integrated in an Isochrone potential # potential parameters omegas = 2*np.pi / np.array([150., 71., 201.]) pot = gp.HarmonicOscillatorPotential(omega=omegas, units=galactic) pos = np.zeros((3, n_orbits)) pos[:2] = np.random.uniform(-10, 10, size=(2, n_orbits)) pos = pos*galactic['length'] vel = np.zeros((3, n_orbits))*galactic['length']/galactic['time'] # compute true actions, true frequencies w0 = gd.CartesianPhaseSpacePosition(pos=pos, vel=vel) act, ang, frq = pot.action_angle(w0) frq = np.repeat(frq[:,np.newaxis], axis=1, repeats=n_orbits) # reshape down to 2d act = act[:2] ang = ang[:2] frq = frq[:2] true_periods = (2*np.pi / frq).max(axis=0) # write to file f = h5py.File(cache_file, "w") truth = f.create_group("initial") truth.create_dataset("actions", act.shape, dtype='f8', data=act.value) truth["actions"].attrs['unit'] = str(act.unit) truth.create_dataset("angles", ang.shape, dtype='f8', data=ang.value) truth["angles"].attrs['unit'] = str(ang.unit) truth.create_dataset("freqs", frq.shape, dtype='f8', data=frq.value) truth["freqs"].attrs['unit'] = str(frq.unit) # integrate them orbits -- have to do it this way to make sure # dt is right all_x = np.zeros((2, nsteps+1, n_orbits)) all_v = np.zeros((2, nsteps+1, n_orbits)) all_t = np.zeros((nsteps+1, n_orbits)) for i, period in enumerate(true_periods): print("Orbit {0}".format(i)) dt = period / nsteps_per_period orbit = pot.integrate_orbit(w0[i], dt=dt, nsteps=nsteps, Integrator=DOPRI853Integrator) all_x[..., i] = orbit.pos.xyz.decompose(pot.units).value[:2] all_v[..., i] = orbit.vel.d_xyz.decompose(pot.units).value[:2] all_t[..., i] = orbit.t.decompose(pot.units).value orb = f.create_group("orbits") orb.create_dataset("t", all_t.shape, dtype='f8', data=all_t) orb['t'].attrs['unit'] = str(pot.units['time']) orb.create_dataset("x", all_x.shape, dtype='f8', data=all_x) orb['x'].attrs['unit'] = str(pot.units['length']) orb.create_dataset("v", all_v.shape, dtype='f8', data=all_v) orb['v'].attrs['unit'] = str(pot.units['length']/pot.units['time']) f.flush() f.close() return cache_file
def get_isochrone_orbits(n_orbits=100, seed=42): np.random.seed(seed) cache_file = os.path.abspath(_find_pkg_data_path("isochrone_orbits.h5")) if os.path.exists(cache_file): return cache_file # integration parameters nperiods = 50 nsteps_per_period = 512 nsteps = nperiods * nsteps_per_period # --------------------------------------------------------------- # first data set are orbits integrated in an Isochrone potential # potential parameters m = 1E11 b = 5. pot = gp.IsochronePotential(m=m, b=b, units=galactic) # distance r = 10. # MAGIC NUMBER # velocity magnitude menc = pot.mass_enclosed([r, 0., 0.]) vc = np.sqrt(pot.G * menc.value / r) vmag = np.random.normal(vc-0.01, vc*0.01, size=n_orbits) # for position phi = np.random.uniform(0, 2*np.pi, size=n_orbits) x = r * np.cos(phi) y = r * np.sin(phi) z = np.zeros_like(x) vx = -vmag * np.sin(phi) vy = vmag * np.cos(phi) vz = np.zeros_like(x) pos = np.vstack((x, y, z))*galactic['length'] vel = np.vstack((vx, vy, vz))*galactic['length']/galactic['time'] # compute true actions, true frequencies w0 = gd.CartesianPhaseSpacePosition(pos=pos, vel=vel) act, ang, frq = pot.action_angle(w0) # reshape down to 2d act = act[:2] ang = ang[:2] frq = frq[:2] true_periods = (2*np.pi / frq).max(axis=0) # write to file f = h5py.File(cache_file, "w") truth = f.create_group("initial") truth.create_dataset("actions", act.shape, dtype='f8', data=act.value) truth["actions"].attrs['unit'] = str(act.unit) truth.create_dataset("angles", ang.shape, dtype='f8', data=ang.value) truth["angles"].attrs['unit'] = str(ang.unit) truth.create_dataset("freqs", frq.shape, dtype='f8', data=frq.value) truth["freqs"].attrs['unit'] = str(frq.unit) # integrate them orbits -- have to do it this way to make sure # dt is right all_x = np.zeros((2, nsteps+1, n_orbits)) all_v = np.zeros((2, nsteps+1, n_orbits)) all_t = np.zeros((nsteps+1,n_orbits)) for i, period in enumerate(true_periods): print("Orbit {0}".format(i)) dt = period / nsteps_per_period orbit = pot.integrate_orbit(w0[i], dt=dt, nsteps=nsteps, Integrator=DOPRI853Integrator) all_x[..., i] = orbit.pos.xyz.decompose(pot.units).value[:2] all_v[..., i] = orbit.vel.d_xyz.decompose(pot.units).value[:2] all_t[..., i] = orbit.t.decompose(pot.units).value orb = f.create_group("orbits") orb.create_dataset("t", all_t.shape, dtype='f8', data=all_t) orb['t'].attrs['unit'] = str(pot.units['time']) orb.create_dataset("x", all_x.shape, dtype='f8', data=all_x) orb['x'].attrs['unit'] = str(pot.units['length']) orb.create_dataset("v", all_v.shape, dtype='f8', data=all_v) orb['v'].attrs['unit'] = str(pot.units['length']/pot.units['time']) f.flush() f.close() return cache_file
def setup_class(self): self.old_cdbs = os.environ['PYSYN_CDBS'] locations.rootdir = _find_pkg_data_path(os.path.join('data', 'cdbs')) locations._get_RedLaws()
def setup_class(self): self.datadir = _find_pkg_data_path('data')
def _download_rtd_zip(rtd_version=None, **kwargs): """ Download and extract HTML ZIP from RTD to installed doc data path. Download is skipped if content already exists. Parameters ---------- rtd_version : str or `None` RTD version to download; e.g., "latest", "stable", or "v2.6.0". If not given, download closest match to software version. kwargs : dict Keywords for ``urlretrieve()``. Returns ------- index_html : str Path to local "index.html". """ # https://github.com/ejeschke/ginga/pull/451#issuecomment-298403134 if not toolkit.family.startswith('qt'): raise ValueError('Downloaded documentation not compatible with {} ' 'UI toolkit browser'.format(toolkit.family)) if rtd_version is None: rtd_version = _find_rtd_version() data_path = os.path.dirname( _find_pkg_data_path('help.html', package='ginga.doc')) index_html = os.path.join(data_path, 'index.html') # There is a previous download of documentation; Do nothing. # There is no check if downloaded version is outdated; The idea is that # this folder would be empty again when installing new version. if os.path.isfile(index_html): return index_html url = ('https://readthedocs.org/projects/ginga/downloads/htmlzip/' '{}/'.format(rtd_version)) local_path = urllib.request.urlretrieve(url, **kwargs)[0] with zipfile.ZipFile(local_path, 'r') as zf: zf.extractall(data_path) # RTD makes an undesirable sub-directory, so move everything there # up one level and delete it. subdir = os.path.join(data_path, 'ginga-{}'.format(rtd_version)) for s in os.listdir(subdir): src = os.path.join(subdir, s) if os.path.isfile(src): shutil.copy(src, data_path) else: # directory shutil.copytree(src, os.path.join(data_path, s)) shutil.rmtree(subdir) if not os.path.isfile(index_html): raise OSError( '{} is missing; Ginga doc download failed'.format(index_html)) return index_html
def freeze_iers(name='iers_frozen.ecsv', ignore_warnings=True): """Use a frozen IERS table saved with this package. This should be called at the beginning of a script that calls astropy time and coordinates functions which refer to the UT1-UTC and polar motions tabulated by IERS. The purpose is to ensure identical results across systems and astropy releases, to avoid a potential network download, and to eliminate some astropy warnings. After this call, the loaded table will be returned by :func:`astropy.utils.iers.IERS_Auto.open()` and treated like a a normal IERS table by all astropy code. Specifically, this method registers an instance of a custom IERS_Frozen class that inherits from IERS_B and overrides :meth:`astropy.utils.iers.IERS._check_interpolate_indices` to prevent any IERSRangeError being raised. See http://docs.astropy.org/en/stable/utils/iers.html for details. This function returns immediately after the first time it is called, so it it safe to insert anywhere that consistent IERS models are required, and subsequent calls with different args will have no effect. The :func:`desiutil.plots.plot_iers` function is useful for inspecting IERS tables and how they are extrapolated to DESI survey dates. Parameters ---------- name : :class:`str`, optional Name of the file to load the frozen IERS table from. Should normally be relative and then refers to this package's data/ directory. Must end with the .ecsv extension. ignore_warnings : :class:`bool`, optional Ignore ERFA and IERS warnings about future dates generated by astropy time and coordinates functions. Specifically, ERFA warnings containing the string "dubious year" are filtered out, as well as AstropyWarnings related to IERS table extrapolation. """ global _iers_is_frozen log = get_logger() if _iers_is_frozen: log.debug('IERS table already frozen.') return log.info('Freezing IERS table used by astropy time, coordinates.') # Validate the save_name extension. _, ext = os.path.splitext(name) if ext != '.ecsv': raise ValueError('Expected .ecsv extension for {0}.'.format(name)) # Locate the file in our package data/ directory. if not os.path.isabs(name): name = _find_pkg_data_path(os.path.join('data', name)) if not os.path.exists(name): raise ValueError('No such IERS file: {0}.'.format(name)) # Clear any current IERS table. astropy.utils.iers.IERS.close() # Initialize the global IERS table. We load the table by # hand since the IERS open() method hardcodes format='cds'. try: table = Table.read(name, format='ascii.ecsv').filled() except IOError: raise RuntimeError('Unable to load IERS table from {0}.'.format(name)) # Define a subclass of IERS_B that overrides _check_interpolate_indices # to prevent any IERSRangeError being raised. class IERS_Frozen(astropy.utils.iers.IERS_B): def _check_interpolate_indices(self, indices_orig, indices_clipped, max_input_mjd): pass # Create and register an instance of this class from the table. iers = IERS_Frozen(table) astropy.utils.iers.IERS.iers_table = iers astropy.utils.iers.IERS_B.iers_table = iers # Prevent any attempts to automatically download updated IERS-A tables. astropy.utils.iers.conf.auto_download = False astropy.utils.iers.conf.auto_max_age = None astropy.utils.iers.conf.iers_auto_url = 'frozen' astropy.utils.iers.conf.iers_auto_url_mirror = 'frozen' # Sanity check. auto_class = astropy.utils.iers.IERS_Auto.open() if auto_class is not iers: raise RuntimeError( 'Frozen IERS is not installed as the default ({0} v. {1}).'.format( auto_class.__class__, iers.__class__)) if ignore_warnings: warnings.filterwarnings( 'ignore', category=astropy._erfa.core.ErfaWarning, message= r'ERFA function \"[a-z0-9_]+\" yielded [0-9]+ of \"dubious year') warnings.filterwarnings( 'ignore', category=astropy.utils.exceptions.AstropyWarning, message=r'Tried to get polar motions for times after IERS data') warnings.filterwarnings( 'ignore', category=astropy.utils.exceptions.AstropyWarning, message=r'\(some\) times are outside of range covered by IERS') # Shortcircuit any subsequent calls to this function. _iers_is_frozen = True