Add 'apps/arv-web/' from commit 'f9732ad8460d013c2f28363655d0d1b91894dca5'
[arvados.git] / crunch_scripts / crunchutil / subst.py
1 import os
2 import glob
3 import stat
4
5 class SubstitutionError(Exception):
6     pass
7
8 def search(c):
9     DEFAULT = 0
10     DOLLAR = 1
11
12     i = 0
13     state = DEFAULT
14     start = None
15     depth = 0
16     while i < len(c):
17         if c[i] == '\\':
18             i += 1
19         elif state == DEFAULT:
20             if c[i] == '$':
21                 state = DOLLAR
22                 if depth == 0:
23                     start = i
24             elif c[i] == ')':
25                 if depth == 1:
26                     return [start, i]
27                 if depth > 0:
28                     depth -= 1
29         elif state == DOLLAR:
30             if c[i] == '(':
31                 depth += 1
32             state = DEFAULT
33         i += 1
34     if depth != 0:
35         raise SubstitutionError("Substitution error, mismatched parentheses {}".format(c))
36     return None
37
38 def sub_file(v):
39     path = os.path.join(os.environ['TASK_KEEPMOUNT'], v)
40     st = os.stat(path)
41     if st and stat.S_ISREG(st.st_mode):
42         return path
43     else:
44         raise SubstitutionError("$(file {}) is not accessible or is not a regular file".format(path))
45
46 def sub_dir(v):
47     d = os.path.dirname(v)
48     if d == '':
49         d = v
50     path = os.path.join(os.environ['TASK_KEEPMOUNT'], d)
51     st = os.stat(path)
52     if st and stat.S_ISDIR(st.st_mode):
53         return path
54     else:
55         raise SubstitutionError("$(dir {}) is not accessible or is not a directory".format(path))
56
57 def sub_basename(v):
58     return os.path.splitext(os.path.basename(v))[0]
59
60 def sub_glob(v):
61     l = glob.glob(v)
62     if len(l) == 0:
63         raise SubstitutionError("$(glob {}) no match fonud".format(v))
64     else:
65         return l[0]
66
67 default_subs = {"file ": sub_file,
68                 "dir ": sub_dir,
69                 "basename ": sub_basename,
70                 "glob ": sub_glob}
71
72 def do_substitution(p, c, subs=default_subs):
73     while True:
74         m = search(c)
75         if m is None:
76             return c
77
78         v = do_substitution(p, c[m[0]+2 : m[1]])
79         var = True
80         for sub in subs:
81             if v.startswith(sub):
82                 r = subs[sub](v[len(sub):])
83                 var = False
84                 break
85         if var:
86             if v in p:
87                 r = p[v]
88             else:
89                 raise SubstitutionError("Unknown variable or function '%s' while performing substitution on '%s'" % (v, c))
90             if r is None:
91                 raise SubstitutionError("Substitution for '%s' is null while performing substitution on '%s'" % (v, c))
92             if not isinstance(r, basestring):
93                 raise SubstitutionError("Substitution for '%s' must be a string while performing substitution on '%s'" % (v, c))
94
95         c = c[:m[0]] + r + c[m[1]+1:]