casaconfig 1.3.1__py3-none-any.whl → 1.4.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -15,11 +15,12 @@
15
15
  this module will be included in the api
16
16
  """
17
17
 
18
- def measures_update(path=None, version=None, force=False, logger=None, auto_update_rules=False, use_astron_obs_table=False, verbose=None):
18
+ def measures_update(path=None, version=None, force=False, measures_site=None, logger=None, auto_update_rules=False, use_astron_obs_table=False, verbose=None):
19
19
  """
20
- Update or install the IERS data used for measures calculations from ASTRON into path.
20
+ Update or install the IERS data used for measures calculations from measures_site into path.
21
21
 
22
- Original data source used by ASTRON is here: https://www.iers.org/IERS/EN/DataProducts/data.html
22
+ Original IERS data source used by each of the recommended measures sites
23
+ is here: https://www.iers.org/IERS/EN/DataProducts/data.html
23
24
 
24
25
  If no update is necessary then this function will silently return.
25
26
 
@@ -27,16 +28,16 @@ def measures_update(path=None, version=None, force=False, logger=None, auto_upda
27
28
  are unchanged for expected reasons. A level of 0 outputs nothing. A
28
29
  value of 1 sends any output to the logger and a value of 2 logs and prints the information.
29
30
  The default value of the verbose argument is taken from the casaconfig_verbose config
30
- value (defaults to 1).
31
+ value (defaults to 1). Error messages are always logged and printed.
31
32
 
32
33
  CASA maintains a separate Observatories table which is available in the casarundata
33
- collection through pull_data and data_update. The Observatories table found at ASTRON
34
+ collection through pull_data and data_update. The Observatories table found at measures_site
34
35
  is not installed by measures_update and any Observatories file at path will not be changed
35
36
  by using this function. This behavior can be changed by setting force and use_astron_obs_table
36
37
  both to True (use_astron_obs_table is ignored when force is False).
37
38
 
38
- A text file (readme.txt in the geodetic directory in path) records the measures version string
39
- and the date when that version was installed in path.
39
+ A text file (readme.txt in the geodetic directory in path) records the measures version string,
40
+ the measures site that was used and the date when that version was installed in path.
40
41
 
41
42
  If path is None then config.measurespath is used.
42
43
 
@@ -44,12 +45,26 @@ def measures_update(path=None, version=None, force=False, logger=None, auto_upda
44
45
  nothing unless force is True.
45
46
 
46
47
  If a specific version is not requested (the default) and the modification time of that text
47
- file is less than 24 hrs before now then this function does nothing unless force is True. When this
48
- function checks for a more recent version and finds that the installed version is the most recent
49
- then the modification time of that text file is changed to the current time even though nothing has
50
- changed in path. This limits the number of attempts to update the measures data (including checking
51
- for more recent data) to once per day. When the force argument is True and a specific version is
52
- not requested then this function always checks for the latest version.
48
+ file is less than the measures_update_interval config value (days) before now then this function
49
+ does nothing unless force is True. When this function checks for a more recent version and finds
50
+ that the installed version is the most recent one then the modification time of that text file
51
+ is changed to the current time even though nothing has changed in path. This limits the
52
+ number of attempts to update the measures data (including checking
53
+ for more recent data) to once every measures_update_interval days. When the force argument
54
+ is True and a specific version is not requested then this function always checks for the
55
+ latest version. The measures_update_interval is always used as an int type (including
56
+ any truncation of the actual value in config if not an integer).
57
+
58
+ The measures_site is a single URL or a list of URLs to use to find the measures tar file
59
+ to use in the update. See measures_available for more details on how that parameter
60
+ is used.
61
+
62
+ If a specific version is requested then that must match a file in the list returned
63
+ by measures_available. The version is usually unique at each measures_site and so care
64
+ may need to be taken when requesting a specific version to ensure that it's available at
65
+ measures_site. If measures_site is a list and a specific version is requested then
66
+ measures_update will try and find that version in each measures_site in that list, using
67
+ the first site that has that version.
53
68
 
54
69
  When auto_update_rules is True then path must exist and contain the expected readme.txt file.
55
70
  Path must be owned by the user, force must be False, and the version must be None. This
@@ -107,12 +122,13 @@ def measures_update(path=None, version=None, force=False, logger=None, auto_upda
107
122
 
108
123
  Parameters
109
124
  - path (str=None) - Folder path to place updated measures data. Must contain a valid geodetic/readme.txt. If not set then config.measurespath is used.
110
- - version (str=None) - Version of measures data to retrieve (usually in the form of WSRT_Measures_yyyymmdd-160001.ztar, see measures_available()). Default None retrieves the latest.
125
+ - version (str=None) - Version of measures data to retrieve (usually in the form of WSRT_Measures_yyyymmdd-160001.ztar, see measures_available). Default None retrieves the latest.
111
126
  - force (bool=False) - If True, always re-download the measures data. Default False will not download measures data if updated within the past day unless the version parameter is specified and different from what was last downloaded.
127
+ - measures_site(str or list of str = None) - Each value is a URL where measures tar files are found. If measures_site is a list then the elements are used in order until a list can be assembled. Default None uses config.measures_site.
112
128
  - logger (casatools.logsink=None) - Instance of the casalogger to use for writing messages. Default None writes messages to the terminal
113
129
  - auto_update_rules (bool=False) - If True then the user must be the owner of path, version must be None, and force must be False.
114
130
  - use_astron_obs_table (bool=False) - If True and force is also True then keep the Observatories table found in the Measures tar tarball (possibly overwriting the Observatories table from casarundata).
115
- - verbose (int=None) - Level of output, 0 is none, 1 is to logger, 2 is to logger and terminal, defaults to casaconfig_verbose in config dictionary.
131
+ - verbose (int=None) - Level of output, 0 is none, 1 is to logger, 2 is to logger and terminal, defaults to config.casaconfig_verbose.
116
132
 
117
133
  Returns
118
134
  None
@@ -130,16 +146,11 @@ def measures_update(path=None, version=None, force=False, logger=None, auto_upda
130
146
 
131
147
  """
132
148
  import os
133
- import pkg_resources
134
149
  from datetime import datetime
135
150
  import sys
136
151
 
137
152
  import tarfile
138
153
  import re
139
- import ssl
140
- import urllib.request
141
- import certifi
142
- import shutil
143
154
 
144
155
  from casaconfig import measures_available
145
156
  from casaconfig import AutoUpdatesNotAllowed, UnsetMeasurespath, RemoteError, NotWritable, BadReadme, BadLock, NoReadme, NoNetwork
@@ -148,16 +159,20 @@ def measures_update(path=None, version=None, force=False, logger=None, auto_upda
148
159
  from .get_data_lock import get_data_lock
149
160
  from .get_data_info import get_data_info
150
161
  from .measures_available import measures_available
151
-
162
+ from .do_untar_url import do_untar_url
163
+
164
+ from .. import config as _config
165
+
152
166
  if path is None:
153
- from .. import config as _config
154
167
  path = _config.measurespath
155
168
 
156
169
  if path is None:
157
170
  raise UnsetMeasurespath('measures_update: path is None and has not been set in config.measurespath. Provide a valid path and retry.')
158
171
 
172
+ if measures_site is None:
173
+ measures_site = _config.measures_site
174
+
159
175
  if verbose is None:
160
- from .. import config as _config
161
176
  verbose = _config.casaconfig_verbose
162
177
 
163
178
  path = os.path.expanduser(path)
@@ -176,42 +191,52 @@ def measures_update(path=None, version=None, force=False, logger=None, auto_upda
176
191
  # make dirs all the way down, if possible
177
192
  os.makedirs(path)
178
193
 
179
- current = None
194
+ currentVersion = None
180
195
  ageRecent = False
196
+ currentSite = None
181
197
 
182
198
  # first, does this look like it needs to be updated
183
199
 
184
200
  # get any existing measures readme information
185
201
  readmeInfo = get_data_info(path, logger, type='measures')
186
202
  if readmeInfo is not None:
187
- current = readmeInfo['version']
203
+ currentVersion = readmeInfo['version']
204
+ currentSite = readmeInfo['site']
188
205
  if readmeInfo['age'] is not None:
189
- ageRecent = readmeInfo['age'] < 1.0
206
+ ageRecent = readmeInfo['age'] < int(_config.measures_update_interval)
190
207
 
191
208
  if not force:
192
- # don't check for new version if the age is less than 1 day
209
+ # don't check for new version if the age is less than measures_update_interval days
193
210
  if version is None and ageRecent:
194
- if verbose > 0:
195
- print_log_messages('measures_update: version installed or checked less than 1 day ago, nothing updated or checked', logger, verbose=verbose)
211
+ print_log_messages('measures_update: version installed or checked less than %s day(s) ago, nothing updated or checked' % int(_config.measures_update_interval), logger, verbose=verbose)
196
212
  return
197
213
 
198
- # don't overwrite something that looks bad unless forced to do so
199
- if current == 'invalid':
200
- raise NoReadme('measures_update: no measures readme.txt file found at %s. Nothing updated or checked.' % path)
201
-
202
- if current == 'error':
203
- raise BadReadme('measures_update: the measures readme.txt file at %s could not be read as expected, an update can not proceed unless force is True' % path)
204
-
205
- # don't overwrite something that looks like valid measures data unless forced to do so
206
- if current == 'unknown':
207
- print_log_messages('measures_update: the measures data at %s is not maintained by casaconfig and so it can not be updated unless force is True' % path, logger)
208
- return
214
+ if currentVersion is not None:
215
+ # don't overwrite something that looks bad unless forced to do so
216
+ if 'invalid' in currentVersion:
217
+ raise NoReadme('measures_update: no measures readme.txt file found at %s. Nothing updated or checked.' % path)
218
+
219
+ if 'error' in currentVersion:
220
+ msg = "measures_update: the measures readme.txt file at %s could not be read as expected, an update can not proceed unless force is True" % path
221
+ # add any additional error message, anything after the first ":" if is found
222
+ colonIndex = currentVersion.find(":")
223
+ if colonIndex != -1:
224
+ msg += "; " + currentVersion[colonIndex+1:]
225
+ raise BadReadme(msg)
226
+
227
+ # don't overwrite something that looks like valid measures data unless forced to do so
228
+ if 'unknown' in currentVersion:
229
+ print_log_messages('measures_update: the measures data at %s is not maintained by casaconfig and so it can not be updated unless force is True' % path, logger, True)
230
+ return
209
231
 
210
232
  checkVersion = version
211
233
  if checkVersion is None:
212
234
  # get the current most recent version
213
235
  try:
214
- checkVersion = measures_available()[-1]
236
+ # if measures_site is a list, measures_available will return the list from the appropriate site in that list
237
+ # that might not be the same site recorded with currentVersion, but that's OK for this update step
238
+ # the currentVersion is already old enough to trigger this check for a more recent version
239
+ checkVersion = measures_available(measures_site=measures_site)[-1]
215
240
  except NoNetwork as exc:
216
241
  # no network, no point in continuing, just reraise
217
242
  raise exc
@@ -220,12 +245,14 @@ def measures_update(path=None, version=None, force=False, logger=None, auto_upda
220
245
  raise exc
221
246
  except:
222
247
  # unsure what happened, leave it at none, which will trigger an update attempt, which might work
248
+ print("unexpected excption in measuress_available")
249
+ import traceback
250
+ traceback.print_exc()
223
251
  pass
224
252
 
225
253
  # don't re-download the same data
226
- if (checkVersion is not None) and (checkVersion == current):
227
- if verbose > 0:
228
- print_log_messages('measures_update: requested version already installed in %s' % path, logger, verbose=verbose)
254
+ if (checkVersion is not None) and (checkVersion == currentVersion):
255
+ print_log_messages('measures_update: requested version already installed in %s' % path, logger, verbose=verbose)
229
256
  # update the age of the readme to now
230
257
  readme_path = os.path.join(path,'geodetic/readme.txt')
231
258
  # readme_path should already exist if it's here
@@ -239,7 +266,7 @@ def measures_update(path=None, version=None, force=False, logger=None, auto_upda
239
266
  msgs = []
240
267
  msgs.append("Error: the Observatories table was not found as expected in %s" % path)
241
268
  msgs.append("Either install casarundata first or set use_astron_obs_table and force to be True when using measures_update.")
242
- msgs.append("Note that the Observatories table provided in the Astron measures tarfile is not the same as that maintained by CASA")
269
+ msgs.append("Note that the Observatories table provided in the measures tarfile is not the same as that maintained by CASA")
243
270
  print_log_messages(msgs, logger, True)
244
271
  return
245
272
 
@@ -253,7 +280,7 @@ def measures_update(path=None, version=None, force=False, logger=None, auto_upda
253
280
  lock_fd = None
254
281
  clean_lock = True # set to false if the contents are actively being update and the lock file should not be cleaned one exception
255
282
  try:
256
- print_log_messages('measures_update ... acquiring the lock ... ', logger)
283
+ print_log_messages('measures_update ... acquiring the lock ... ', logger, verbose=verbose)
257
284
 
258
285
  # the BadLock exception that may happen here is caught below
259
286
  lock_fd = get_data_lock(path, 'measures_update')
@@ -262,21 +289,19 @@ def measures_update(path=None, version=None, force=False, logger=None, auto_upda
262
289
 
263
290
  if not do_update:
264
291
  # recheck the readme file, another update may have already happened before the lock was obtained
265
- current = None
292
+ currentVersion = None
266
293
  ageRecent = False
267
294
 
268
295
  readmeInfo = get_data_info(path, logger, type='measures')
269
296
  if readmeInfo is not None:
270
- current = readmeInfo['version']
297
+ currentVersion = readmeInfo['version']
271
298
  if readmeInfo['age'] is not None:
272
- ageRecent = readmeInfo['age'] < 1.0
299
+ ageRecent = readmeInfo['age'] < int(_config.measures_update_interval)
273
300
 
274
- if (version is not None) and (version == current):
275
- # no update will be done, version is as requested - always verbose here because the lock is in use
276
- print_log_messages('The requested measures version is already installed in %s, using version %s' % (path, current), logger)
301
+ if (version is not None) and (version == currentVersion):
302
+ print_log_messages('The requested measures version is already installed in %s, using version %s' % (path, currentVersion), logger, verbose=verbose)
277
303
  elif (version is None) and ageRecent:
278
- # no update will be done, it's already been checked or updated recently - always verbose here because the lock is in use
279
- print_log_messages('The latest measures version was checked recently in %s, using version %s' % (path, current), logger)
304
+ print_log_messages('The latest measures version was checked recently in %s, using version %s' % (path, currentVersion), logger, verbose=verbose)
280
305
  else:
281
306
  # final check for problems before updating
282
307
  if not force and readmeInfo is not None and (version=='invalid' or version=='unknown'):
@@ -291,39 +316,62 @@ def measures_update(path=None, version=None, force=False, logger=None, auto_upda
291
316
 
292
317
  if do_update:
293
318
  if force:
294
- print_log_messages('A measures update has been requested by the force argument', logger)
295
-
296
- print_log_messages(' ... finding available measures at www.astron.nl ...', logger)
297
-
298
- files = measures_available()
299
-
300
- # target filename to download
301
- # for the non-force unspecified version case this can only get here if the age is > 1 day so there should be a newer version
302
- # but that isn't checked - this could install a version that's already installed
303
- target = files[-1] if version is None else version
304
- if target not in files:
305
- print_log_messages("measures_update can't find specified version %s" % target, logger, True)
306
-
319
+ print_log_messages('A measures update has been requested by the force argument', logger, verbose=verbose)
320
+
321
+ print_log_messages(' ... finding available measures ...', logger, verbose=verbose)
322
+
323
+ target = None
324
+ site = None
325
+
326
+ # if a specific version has been request AND measures_site is a list with more than one value
327
+ # then find the site with that version in it. Turn off logging in measures_available calls
328
+ # here as the age of the site doesn't matter
329
+ if version is not None and isinstance(measures_site, list) and len(measures_site) > 1:
330
+ target = version
331
+ for this_site in measures_site:
332
+ files = measures_available(measures_site=this_site, logger=None)
333
+ # make sure that site is excluded from files before target is checked
334
+ files = files[1:]
335
+ if target in files:
336
+ site = this_site
337
+ break
338
+ if site is None:
339
+ print_log_messages("measures_update can't find specfied version %s at any of the sites in the measures_site list" % target, logger, True)
340
+ # else site and target are set
307
341
  else:
342
+ # if measure_available sorts out what site to use if it's a list with more than one element
343
+ # a specific version probably needs to use a specific site to make it work
344
+ # if a specific version has been requested then the age of the site isn't an issue, turn logging off
345
+ this_logger = logger if version is None else None
346
+ files = measures_available(measures_site=measures_site, logger=this_logger)
347
+ site = files[0]
348
+ # this makes sure that the site is exclude from files before target is checked.
349
+ files = files[1:]
350
+
351
+ # target filename to download
352
+ # for the non-force unspecified version case this can only get here if the age is > 1 day so there should be a newer version
353
+ # but that isn't checked - this could install a version that's already installed
354
+ target = files[-1] if version is None else version
355
+ if target not in files:
356
+ print_log_messages("measures_update can't find specified version %s at site %s" % (target,site), logger, True)
357
+ # unset site here to signal nothing to extract
358
+ site = None
359
+
360
+ if site is not None:
308
361
  # there are files to extract
309
- print_log_messages(' ... downloading %s from ASTRON server to %s ...' % (target, path), logger)
362
+ print_log_messages(' ... downloading %s from %s to %s ...' % (target, site, path), logger, verbose=verbose)
310
363
 
311
- astronURL = 'https://www.astron.nl/iers'
312
- context = ssl.create_default_context(cafile=certifi.where())
313
- # just in case there's a redirect at astron the way there is for the go.nrao.edu site and casarundata
314
- measuresURLroot = urllib.request.urlopen(astronURL, context=context).url
315
- measuresURL = os.path.join(measuresURLroot, target)
316
-
317
364
  # it's at this point that this code starts modifying what's there so the lock file should
318
- # not be removed on failure after this although it may leave that temp tar file around, but that's OK
365
+ # not be removed on failure from here on (until it succeeds)
319
366
  clean_lock = False
367
+
320
368
  # remove any existing measures readme.txt now in case something goes wrong during extraction
321
369
  readme_path = os.path.join(path,'geodetic/readme.txt')
322
370
  if os.path.exists(readme_path):
323
371
  os.remove(readme_path)
324
372
 
325
- # custom filter that incorporates data_filter to watch for dangerous members of the tar file and
326
- # add filtering to remove the Observatories table (unless use_astron_obs_table is True) and
373
+ # custom filter that incorporates 'data_filter' to watch for dangerous members of the tar file
374
+ # this adds filtering to remove the Observatories table (unless use_astron_obs_table is True) and
327
375
  # the *.old tables that may be in the geodetic tree
328
376
  def custom_filter(member, extractionPath):
329
377
  # member is a TarInfo instance and extractionPath is the destination path
@@ -337,18 +385,16 @@ def measures_update(path=None, version=None, force=False, logger=None, auto_upda
337
385
  member = None
338
386
  return member
339
387
 
340
- with urllib.request.urlopen(measuresURL, context=context, timeout=400) as tstream, tarfile.open(fileobj=tstream, mode='r|*') as tar :
341
- # use the 'data' filter if available, revert to previous 'fully_trusted' behavior of not available
342
- tar.extraction_filter = custom_filter
343
- tar.extractall(path=path)
344
- tar.close()
388
+ # untar the target at site to path using the custom filter, do not be verbose
389
+ do_untar_url(site, target, path, custom_filter)
345
390
 
346
391
  # create a new readme.txt file
347
392
  with open(readme_path,'w') as fid:
348
- fid.write("# measures data populated by casaconfig\nversion : %s\ndate : %s" % (target, datetime.today().strftime('%Y-%m-%d')))
393
+ # site added last to not break past readers of the measures readme
394
+ fid.write("# measures data populated by casaconfig\nversion : %s\ndate : %s\nsite : %s" % (target, datetime.today().strftime('%Y-%m-%d'),site))
349
395
 
350
396
  clean_lock = True
351
- print_log_messages(' ... measures data updated at %s' % path, logger)
397
+ print_log_messages(' ... measures data updated at %s' % path, logger, verbose=verbose)
352
398
 
353
399
  # closing out the do_update
354
400
 
@@ -372,6 +418,14 @@ def measures_update(path=None, version=None, force=False, logger=None, auto_upda
372
418
  msgs.append('Check for other updates in progress or choose a different path or clear out this path and reinstall the casarundata as well as the measures data')
373
419
  print_log_messages(msgs, logger, True)
374
420
  raise
421
+
422
+ except RemoteError as exc:
423
+ # probably an empty site
424
+ msgs = [str(exc)]
425
+ msgs.append('There was a remote error while attempting to update measures at %s' % path)
426
+ msgs.append('Check the value of measures_site and try again')
427
+ print_log_messages(msgs, logger, True)
428
+ raise
375
429
 
376
430
  except Exception as exc:
377
431
  msgs = []
@@ -30,13 +30,17 @@ def print_log_messages(msg, logger, is_err=False, verbose=2):
30
30
  - msg (str) - The message to print and optionally log.
31
31
  - logger (casatools.logsink) - Instance of the casalogger to use. Not used if None.
32
32
  - is_err (bool=False) - When False, output goes to sys.stdout and logged as INFO level. When True, output goes to sys.stderr and logged as SEVERE
33
- - verbose (int=2) - When < 2 then msg is only printed if there is no logger, otherwise it's just logged
33
+ - verbose (int=2) - 0 - do nothing, 1 log unless there is no logger, then print, 2 log and print
34
34
 
35
35
  Returns
36
36
  None
37
37
 
38
38
  """
39
39
 
40
+ if verbose == 0:
41
+ # do nothing
42
+ return
43
+
40
44
  import sys
41
45
  fileout = sys.stdout
42
46
  loglevel = 'INFO'
@@ -48,8 +52,8 @@ def print_log_messages(msg, logger, is_err=False, verbose=2):
48
52
  if type(msg) is not list:
49
53
  msg = [msg]
50
54
 
51
- # always print if there is no logger, if there is a logger and verbose is < 2 then do not print
52
- if (logger is None) or (not verbose < 2):
55
+ # print if there is no logger or verbose >= 2
56
+ if (logger is None) or (verbose >= 2):
53
57
  for m_msg in msg:
54
58
  print(m_msg,file=fileout)
55
59
 
@@ -22,6 +22,7 @@ def pull_data(path=None, version=None, force=False, logger=None, verbose=None):
22
22
  The verbose argument controls the level of information provided when this function when the data
23
23
  are unchanged for expected reasons. A level of 0 prints and logs nothing. A
24
24
  value of 1 logs the information and a value of 2 logs and prints the information.
25
+ Error messages are always logged and printed.
25
26
 
26
27
  The path must either contain a previously installed version of casarundata
27
28
  or it must not exist or be empty.
@@ -107,19 +108,23 @@ def pull_data(path=None, version=None, force=False, logger=None, verbose=None):
107
108
 
108
109
  from casaconfig import data_available
109
110
  from casaconfig import get_data_info
110
- from casaconfig import UnsetMeasurespath, BadLock, BadReadme, NotWritable
111
+ from casaconfig import UnsetMeasurespath, BadLock, BadReadme, NoNetwork, NotWritable, RemoteError
111
112
 
112
113
  from .print_log_messages import print_log_messages
113
114
  from .get_data_lock import get_data_lock
114
115
  from .do_pull_data import do_pull_data
115
116
 
117
+ from .. import config as _config
118
+
116
119
  if path is None:
117
- from .. import config as _config
118
120
  path = _config.measurespath
119
121
 
120
122
  if path is None:
121
123
  raise UnsetMeasurespath('path is None and has not been set in config.measurespath. Provide a valid path and retry.')
122
124
 
125
+ if verbose is None:
126
+ verbose = _config.casaconfig_verbose
127
+
123
128
  # when a specific version is requested then the measures readme.txt that is part of that version
124
129
  # will get a timestamp of now so that default measures updates won't happen for a day unless the
125
130
  # force argument is used for measures_update
@@ -223,7 +228,7 @@ def pull_data(path=None, version=None, force=False, logger=None, verbose=None):
223
228
  lock_fd = None
224
229
  clean_lock = True # set to False if the contents are actively being update and the lock file should not be cleaned on exception
225
230
  try:
226
- print_log_messages('pull_data using version %s, acquiring the lock ... ' % version, logger)
231
+ print_log_messages('pull_data using version %s, acquiring the lock ... ' % version, logger, verbose=verbose)
227
232
 
228
233
  # attempting to get the lock will raise NoNetwork if there is no network and the lock will not be set, catch and reemit that in this try block
229
234
  lock_fd = get_data_lock(path, 'pull_data')
@@ -248,13 +253,13 @@ def pull_data(path=None, version=None, force=False, logger=None, verbose=None):
248
253
  if measuresVersion == expectedMeasuresVersion:
249
254
  # no pull is necessary
250
255
  do_pull = False
251
- print_log_messages('pull_data requested "release" versions of casarundata and measures are already installed.', logger)
256
+ print_log_messages('pull_data requested "release" versions of casarundata and measures are already installed.', logger, verbose=verbose)
252
257
  # otherwise this is a release pull and even if the measuresVersion is 'unknown' or 'invalid' we should proceed with a pull at this point
253
258
  # problems with casarundata path will have been caught before
254
259
  else:
255
260
  # nothing to do here, already at the expected casarundata version and a pull is not being forced and this is not a release pull
256
261
  do_pull = False
257
- print_log_messages('pull_data requested version is already installed.', logger)
262
+ print_log_messages('pull_data requested version is already installed.', logger, verbose=verbose)
258
263
 
259
264
  # a version of 'invalid', 'error', or 'unknown' is a surprise here, likely caused by something else doing something
260
265
  # incompatible with this attempt
@@ -274,7 +279,7 @@ def pull_data(path=None, version=None, force=False, logger=None, verbose=None):
274
279
  if do_pull:
275
280
  # do not clean the lock file contents at this point unless do_pull_data returns normally
276
281
  clean_lock = False
277
- do_pull_data(path, version, installed_files, currentVersion, currentDate, logger)
282
+ do_pull_data(path, version, installed_files, currentVersion, currentDate, logger, verbose)
278
283
  clean_lock = True
279
284
  if namedVersion:
280
285
  # a specific version has been requested, set the times on the measures readme.txt to now to avoid
@@ -16,45 +16,89 @@ def read_readme(path):
16
16
  """
17
17
  Read and parse the contents of a readme.txt file used by casaconfig.
18
18
 
19
- A dictionary containing the 'version', 'date', and 'extra' (containing
19
+ A dictionary containing the 'version', 'date', 'site', and 'extra' (containing
20
20
  a list of optional extra lines found). On error, the return values is None.
21
21
 
22
22
  The extra lines are stripped and do not include lines begining with '#'
23
23
 
24
24
  The format is assumed to be:
25
25
  a line begining with #, which is ignored.
26
+ a line "site: the site url string"
26
27
  a line "version : the versions string"
27
28
  a line "date : the date"
28
29
  optional extra lines (the manifest of installed files for the main readme)
29
30
 
30
- The version string and date are stripped of leading and trailing whitespace.
31
+ The version, site and date strings are stripped of leading and trailing whitespace.
32
+
33
+ The site string only appears in measures data and is missing is early readme
34
+ files for measures data. If no site string is seen the value in the dictionary
35
+ is None.
36
+
37
+ The version and date strings are required. If they are not found a BadReadme
38
+ exception is raised.
31
39
 
32
40
  Parameters
33
41
  - path (str) - the path to the file to be read
34
42
 
35
43
  Returns
36
- Dictionary of 'version' (the version string), 'date' (the date string),
37
- 'extra' (a list of any extra lines found). The return value is None on error.
44
+ Dictionary of 'site' ( the site URL or None), 'version' (the version string),
45
+ 'date' (the date string), 'extra' (a list of any extra lines found).
46
+ The return value is None on error except that format errors raise
47
+ a BadReadme exception.
48
+
49
+ Raises
50
+ - casaconfig.BadReadme - raised when there is a format error in the file at path
38
51
  """
39
52
 
40
53
  import os
54
+ from casaconfig import BadReadme
41
55
 
56
+ site = None
42
57
  version = ""
43
58
  date = ""
44
59
  extra = []
45
60
  result = None
46
-
61
+
47
62
  try:
48
63
  with open(path, 'r') as fid:
49
64
  readmeLines = fid.readlines()
50
- version = readmeLines[1].split(':')[1].strip()
51
- date = readmeLines[2].split(':')[1].strip()
52
- if len(readmeLines) > 3:
53
- for extraLine in readmeLines[3:]:
54
- if (extraLine[0] != '#'):
55
- extra.append(extraLine.strip())
56
- result = {'version':version, 'date':date, 'extra':extra}
57
- except:
65
+ # order is unimportant except the first line must start with
66
+ # an "#" and the second line starting with "#" indicates the
67
+ # start of the "extra" lines
68
+ # duplicate instances of the same key in key : value lines are accepted
69
+ # the last value associated with key is used, that should never happen.
70
+ inExtraLines = False
71
+ if readmeLines[0][0] != '#':
72
+ raise BadReadme('Readme file missing required "#" at start of first line, %s' % path)
73
+ for line in readmeLines[1:]:
74
+ if inExtraLines and not (line[0] == '#'):
75
+ extra.append(line.strip())
76
+ elif line[0] == '#':
77
+ inExtraLines = True
78
+ else:
79
+ splitLine = line.split(':')
80
+ if len(splitLine) < 2:
81
+ raise BadReadme('A line did not have an expected ":" separating into at least two parts, %s' % path)
82
+ key = splitLine[0].strip()
83
+ if key == 'site':
84
+ # the URL likely also has a ":" so rejoin the second part and then strip that
85
+ siteURL = ":".join(splitLine[1:])
86
+ site = siteURL.strip()
87
+ elif key == 'version':
88
+ version = splitLine[1].strip()
89
+ elif key == 'date':
90
+ date = splitLine[1].strip()
91
+ # anything else is OK and silently ignored
92
+ result = {'site':site, 'version':version, 'date':date, 'extra':extra}
93
+ except BadReadme as exc:
94
+ # reraise it
95
+ raise
96
+ except Exception as exc:
97
+ # silently return None, handled elsewhere
58
98
  result = None
59
99
 
100
+ # require that date and version are now set in result
101
+ if result is not None and ((len(result['version'])==0) or (len(result['date'])==0)):
102
+ raise BadReadme('Readme file missing required version or date fields, %s' % path)
103
+
60
104
  return result
@@ -32,12 +32,12 @@ def set_casacore_path(path=None):
32
32
  None
33
33
 
34
34
  """
35
- import pkg_resources
35
+ import importlib.resources
36
36
  import os
37
37
  import re
38
38
  import sys
39
39
 
40
- if path is None: path = pkg_resources.resource_filename('casaconfig', 'data/')
40
+ if path is None: path = str(importlib.resources.files('casaconfig').joinpath('data/'))
41
41
  path = os.path.abspath(os.path.expanduser(path))
42
42
 
43
43
  rctext = 'measures.directory: %s\n' % path
@@ -80,6 +80,9 @@ def summary(configDict = None):
80
80
  msg += "; legacy data not maintained by casaconfig"
81
81
  elif measVers == "error":
82
82
  msg += "; unexpected readme.txt file, measures should be reinstalled"
83
+ measSite = dataInfo['measures']['site']
84
+ if measSite is not None and len(measSite) > 0:
85
+ msg = msg + " site : " + measSite
83
86
  print(msg)
84
87
 
85
88
  if (dataInfo['release'] is None):