diff options
Diffstat (limited to 'scripts/update_extern_pkg')
-rw-r--r-- | scripts/update_extern_pkg | 144 |
1 files changed, 144 insertions, 0 deletions
diff --git a/scripts/update_extern_pkg b/scripts/update_extern_pkg new file mode 100644 index 0000000..536ca38 --- /dev/null +++ b/scripts/update_extern_pkg @@ -0,0 +1,144 @@ +#!/usr/bin/env python +# +# Copyright(c) 2016 Association of Universities for Research in Astronomy, Inc. + +import argparse +import os, os.path +import shutil +import re +from collections import OrderedDict + + +# AstroConda naming conventions: +extern_pkg = 'extern.pkg' +backup = 'extern.pkg.ac_bak' # avoid possible collision with user .bak +extern_dir = 'iraf_extern' +template = 'ur_extern.pkg' +path_sub = 'UR_VDIR' # a regexp + + +# Get command-line user arguments: +parser = argparse.ArgumentParser() + +parser.add_argument('path', type=str, + help='path to {0} and {1}/'.format(extern_pkg, extern_dir)) + +parser.add_argument('name', type=str, + help='name of IRAF package (and its directory)') + +args = parser.parse_args() + +# Convert to canonical path and ensure it exists, with an extern.pkg file and +# an iraf_extern/package/ur_extern.pkg (ie. files already installed): +path = os.path.abspath(args.path) # env dir with extern.pkg +extpkg_path = os.path.join(path, extern_pkg) # extern.pkg path +pkg_path = os.path.join(path, extern_dir, args.name) # path to this IRAF pkg +template_path = os.path.join(pkg_path, template) # this ur_extern.pkg + +if not os.access(extpkg_path, os.R_OK | os.W_OK): + raise ValueError("cannot access {0}".format(extpkg_path)) + +if not os.access(template_path, os.R_OK): + raise ValueError("cannot access {0}".format(template_path)) + +# Read extern.pkg: +with open(extpkg_path, 'r+') as extpkg_file: + buffer = extpkg_file.read() + +# Read the package's template ur_extern.pkg, removing any outer blank lines: +with open(template_path, 'r') as template_file: + tempbuf = template_file.read().strip() + +# Here we convert the template entry to a regex for finding and removing old, +# previously-generated entries for the same package. This mainly consists of +# escaping special characters to match literally and allowing for a new path, +# but we also allow for ignoring some trivial whitespace differences. Any other +# user-modified entries are deliberately preserved, but still get overridden +# when adding the new package entry at the end. We don't use re.escape() here +# because it's easier & more understandable to do those subs in concert with +# the others. The template ur_extern.pkg files must not contain backslashes. + +replacements = OrderedDict(( + ('spec_nospace', (r'([][.^${}\|*?])', r'\\\1')), + ('spec_space', (r'[ \t]*([=+()])[ \t]*', r'[ \t]*\\\1[ \t]*')), + ('extra_space', (r'[ \t]+', r'[ \t]+')), + ('end_space', (r'(^|$)', r'[ \t]*')), + ('path_sub', (path_sub, r'.*')), +)) + +# Concatenate individual expressions for replacement, so they can be done +# simultaneously on the original buffer (otherwise they trip each other up): +cat_re = '|'.join(['(?P<{0}>{1})'.format(key, regex) \ + for key, (regex, sub) in replacements.items()]) # py 3 way + +# Determine the replacement for a given match to the concatenated regex: +def match_fn(match): + sub_re, sub = replacements[match.lastgroup] # look up replacement tuple + return re.sub(sub_re, sub, match.group()) # do & return the substitution + +# Perform the substitutions to turn the template string into a regex itself: +# template_re = re.escape(tempbuf) # if using built-in escape for special chars +template_re = re.sub(cat_re, match_fn, tempbuf, flags=re.M) + +# Update the original template entry with our new path: +tempbuf = re.sub(path_sub, os.path.join(pkg_path, ''), tempbuf) # trailing / + +# print(template_re) # debug + +# Define expected "keep" line(s) at the end of extern.pkg: +keep_re = '((?:^[ \t]*keep\s*)+\Z)' +keep_str = '\nkeep\n' + +# Find the last entry in extern.pkg (if any) that matches the template: +match = None +for match in re.finditer(template_re, buffer, flags=re.M): + pass + +# Check for any other definition(s) of this package after the last one based +# on the template, requiring that our new definition be moved to the end of the +# file, to override the other defs. without actually losing the user's edits: +if match: + # The name of the path variable in our extern.pkg template entries must be + # the name of the IRAF package, otherwise we would have to parse the actual + # variable name from the template using these commented regexes and search + # for that, instead of the package name itself, to find redefinitions: + # pkgdef_re = '^[ \t]*task[ \t]+{0}[.]pkg[ \t]*=[ \t]*(.*)$'.format(name) + # pathvar_re = '["\']?(.*)[$].*[.]cl' # find path variable name in pkg def. + + # Match any non-commented instances of the package name, in the remainder + # of the buffer, that don't look like a substring of a longer name: + pkgname_re = '^[^\n#]*(?<![A-z0-9_-]){0}(?![A-z0-9_-])'.format(args.name) + later_match = re.search(pkgname_re, buffer[match.end():], flags=re.M) + + # Where a later (user) definition is found, we still remove the last + # template-based definition but then continue as if there had been no + # match, so a new definition will get placed at the end of the file below: + if later_match: + buffer = '{0}\n\n{1}'.format(buffer[:match.start()].rstrip(), + buffer[match.end():].lstrip()) + match = None + +# Replace the applicable entry with the template-based version (and make sure +# there's a "keep" at the end of the file): +if match: + buffer = buffer[:match.start()] + tempbuf + buffer[match.end():] + if not re.search(keep_re, buffer, flags=re.M): + buffer += keep_str # add missing "keep" + +# If there wasn't an existing entry, or the last one wasn't based on the +# template, put the new entry at the end (before "keep"): +else: + match = re.search(keep_re, buffer, flags=re.M) + if match: + pos = match.start() + buffer = buffer[:pos] + '{0}\n\n'.format(tempbuf) + buffer[pos:] + else: + buffer += '{0}\n{1}'.format(tempbuf, keep_str) # add missing "keep" + +# Back up the original extern.pkg: +shutil.copy2(extpkg_path, os.path.join(path, backup)) + +# Write the modified extern.pkg: +with open(extpkg_path, 'w') as extpkg_file: + extpkg_file.write(buffer) + |