]> mj.ucw.cz Git - moe.git/blob - t/moe/config.py
3280283e0e3c63add243ee591e463ae935e94185
[moe.git] / t / moe / config.py
1 #!/usr/bin/env python
2
3 import re
4 import sys
5
6 key_pattern = re.compile('^[A-Za-z0-9_-]+$')
7 ref_pattern = re.compile('^[A-Za-z0-9_-]+')
8
9 class MoeConfigInvalid(Exception):
10     pass
11
12 class MoeConfigEvalErr(Exception):
13     pass
14
15 class MoeConfig:
16     """Moe configuration file."""
17
18     def __init__(self, file=None, name=None):
19         self.cfg = {}
20         if file is not None:
21             self.load(file)
22         elif name is not None:
23             self.load(open(name, 'r'))
24
25     def load(self, file):
26         for x in file.readlines():
27             x = x.rstrip("\n").lstrip(" \t")
28             if x=='' or x.startswith('#'):
29                 pass
30             else:
31                 sep = x.find('=')
32                 if sep >= 0:
33                     k = x[:sep]
34                     v = x[sep+1:]
35                     if k.endswith("+"):
36                         k = k[:-1]
37                         if not self.cfg.has_key(k):
38                             self.cfg[k] = [('a','')];
39                     else:
40                         self.cfg[k] = []
41                     if not key_pattern.match(k):
42                         raise MoeConfigInvalid, "Malformed name of configuration variable"
43                     if v.startswith("'"):
44                         v=v[1:]
45                         if not v.endswith("'"):
46                             raise MoeConfigInvalid, "Misquoted string"
47                         self.cfg[k].append(('s', v[:-1]))
48                     elif v.startswith('"'):
49                         v=v[1:]
50                         if not v.endswith('"'):
51                             raise MoeConfigInvalid, "Misquoted string"
52                         self.parse_interpolated(self.cfg[k], v[:-1])
53                     else:
54                         self.cfg[k].append(('s', v))
55                 else:
56                     ## FIXME: Report line numbers
57                     raise MoeConfigInvalid, "Parse error"
58
59     def parse_interpolated(self, list, s):
60         while s<>'':
61             if s.startswith('$'):
62                 s = s[1:]
63                 if s.startswith('{'):
64                     p = s.find('}')
65                     if not p:
66                         raise MoeConfigInvalid, "Unbalanced braces"
67                     k, s = s[1:p], s[p+1:]
68                     if not key_pattern.match(k):
69                         raise MoeConfigInvalid, "Invalid variable name"
70                 else:
71                     m = ref_pattern.match(s)
72                     if m:
73                         k, s = s[:m.end()], s[m.end():]
74                     else:
75                         raise MoeConfigInvalid, "Invalid variable reference"
76                 list.append(('i', k))
77             else:
78                 p = s.find('$')
79                 if p < 0:
80                     p = len(s)
81                 list.append(('s', s[:p]))
82                 s = s[p:]
83
84     def dump(self, file=sys.stdout):
85         for k,v in self.cfg.items():
86             file.write(k)
87             if len(v) > 0 and v[0][0] == 'a':
88                 file.write('+')
89                 v = v[1:]
90             file.write('=')
91             for t,w in v:
92                 if t == 's':
93                     file.write("'" + w + "'")
94                 elif t == 'i':
95                     file.write('"$' + w + '"')
96             file.write("\n")
97
98 class MoeConfigStack:
99     """Stack of configuration files."""
100
101     def __init__(self, base=None):
102         ## FIXME: Do we need to duplicate the config files themselves?
103         if base:
104             self.stk = base.stk[:]
105         else:
106             self.stk = []
107         self.in_progress = {}
108
109     def push(self, cfg):
110         self.stk.append(cfg)
111
112     def __getitem__(self, k):
113         if self.in_progress.has_key(k):
114             raise MoeConfigEvalErr, "Definition of $%s is recursive" % k;
115         self.in_progress[k] = 1;
116         v = self.do_get(k, len(self.stk)-1)
117         del self.in_progress[k]
118         return v
119
120     def do_get(self, k, pos):
121         while pos >= 0:
122             cfg = self.stk[pos]
123             if cfg.cfg.has_key(k):
124                 new = cfg.cfg[k]
125                 if new[0][0] == 'a':
126                     v = self.do_get(k, pos-1)
127                 else:
128                     v = ''
129                 for op,arg in new:
130                     if op == 's':
131                         v = v + arg
132                     elif op == 'i':
133                         v = v + self[arg]
134                 return v
135             pos -= 1
136         return ''