1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
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)
|