]>
Commit | Line | Data |
---|---|---|
5cb62ba9 JP |
1 | # Joe Presbrey <presbrey@mit.edu> |
2 | ||
3 | import os, sys, types, time | |
4 | import threading | |
5 | from pyinotify import WatchManager, Notifier, EventsCodes | |
6 | from Cheetah.Template import Template as _Template | |
7 | ||
8 | __all__ = ['Templates'] | |
9 | ||
10 | class Template(object): | |
11 | def __init__(self, name, ns={}, path=['templates']): | |
12 | self._name, self._ns, self._path = name, ns, path+[name] | |
13 | assert self._template() | |
14 | ||
15 | def _template(self): | |
16 | return sys.modules['.'.join(self._path)] | |
17 | ||
18 | def __getattr__(self, name): | |
19 | return Template(name, self._ns, self._path) | |
20 | ||
21 | def __call__(self, **kw): | |
22 | namespaces = self._ns.copy() | |
23 | namespaces.update(kw) | |
24 | return getattr(self._template(), | |
25 | self._name)(namespaces=namespaces).respond() | |
26 | ||
27 | class Templates(object): | |
28 | _base = '' | |
29 | _mask = EventsCodes.ALL_FLAGS['IN_CREATE'] \ | |
30 | | EventsCodes.ALL_FLAGS['IN_MODIFY'] \ | |
31 | | EventsCodes.ALL_FLAGS['IN_DELETE'] \ | |
32 | | EventsCodes.ALL_FLAGS['IN_ATTRIB'] | |
33 | _watch_manager = None | |
34 | _notifier = None | |
35 | _thread = None | |
36 | ||
37 | _loaded = False | |
38 | _files = {} | |
39 | _modules = {} | |
40 | ||
41 | def __init__(self, template_path, global_namespace={}): | |
42 | self._base = os.path.abspath(template_path) | |
43 | self._event_init() | |
44 | self._assert_module('templates', [template_path]) | |
45 | self._globals = global_namespace | |
46 | self.load() | |
47 | self._thread = threading.Thread(target=self.loop) | |
48 | self._thread.setDaemon(True) | |
49 | self._thread.start() | |
50 | ||
51 | def _tree_walk(self, tree, path, func): | |
52 | path = list(path) | |
53 | if len(path) > 1: | |
54 | x = path.pop(0) | |
55 | if not x in tree: | |
56 | tree[x] = {} | |
57 | return self._tree_walk(tree[x], path, func) | |
58 | elif callable(func): | |
59 | return func(tree, path[0]) | |
60 | ||
61 | def _tree_add(self, tree, path, value): | |
62 | def _add(tree, key): | |
63 | tree[key] = value | |
64 | return value | |
65 | print self._tree_walk(tree, path, _add) | |
66 | ||
67 | def _assert_module(self, name, path=None): | |
68 | if not name in self._modules: | |
69 | self._modules[name] = types.ModuleType(name) | |
70 | if path: | |
71 | self._modules[name].__path__ = path | |
72 | sys.modules[name] = self._modules[name] | |
73 | ||
74 | def _get_template_path(self, template_file): | |
75 | r = template_file.startswith(self._base) \ | |
76 | and template_file[len(self._base)+1:].split(os.path.sep) or [] | |
77 | if r[-1].endswith('.tmpl'): | |
78 | r[-1] = r[-1][:-5] | |
79 | return tuple(r) | |
80 | ||
81 | def _compile(self, template_path): | |
82 | template = _Template.compile(file=self._files[template_path], | |
83 | returnAClass=False, | |
84 | moduleName='.'.join(['templates']+list(template_path)), | |
85 | className=template_path[-1], | |
86 | useCache=False, | |
87 | compilerSettings={'useStackFrames':False}) | |
88 | return template | |
89 | ||
90 | def add(self, template_file): | |
91 | template_path = self._get_template_path(template_file) | |
92 | template_name = '.'.join(['templates']+list(template_path)) | |
93 | template_pkg = '.'.join(['templates']+list(template_path[:-1])) | |
94 | template_py = os.path.join(os.path.dirname(template_file), '%s.py' % template_path[-1]) | |
95 | self._assert_module(template_pkg, [os.path.dirname(template_file)]) | |
96 | self._files[template_path] = template_file | |
97 | code = self._compile(template_path) | |
98 | f_code = file(template_py, 'w') | |
99 | f_code.write(code) | |
100 | f_code.close() | |
101 | if self._loaded and \ | |
102 | template_name in sys.modules: | |
103 | reload(sys.modules[template_name]) | |
104 | ||
105 | def remove(self, template_file): | |
106 | #print 'removing', template_file | |
107 | pass | |
108 | ||
109 | def _walk_load(self, args, dname, fnames): | |
110 | for fname in fnames: | |
111 | if fname.endswith('.tmpl'): | |
112 | template_file = os.path.join(dname, fname) | |
113 | self.add(template_file) | |
114 | ||
115 | def load(self): | |
116 | if not self._loaded: | |
117 | os.path.walk(self._base, self._walk_load, None) | |
118 | for path, filename in self._files.items(): | |
119 | __import__('.'.join(['templates']+list(path))) | |
120 | self._loaded = True | |
121 | ||
122 | def _event_handler(self, event): | |
123 | if event.name.endswith('.tmpl') and not event.name.startswith('.'): | |
124 | if event.maskname == 'IN_DELETE': | |
125 | self.remove(event.pathname) | |
126 | else: | |
127 | time.sleep(0.5) | |
128 | self.add(event.pathname) | |
129 | ||
130 | ||
131 | def _event_init(self): | |
132 | self._watch_manager = WatchManager() | |
133 | self._watch_manager.add_watch(self._base, | |
134 | self._mask, | |
135 | self._event_handler, | |
136 | rec=True, | |
137 | auto_add=True) | |
138 | self._notifier = Notifier(self._watch_manager) | |
139 | ||
140 | def loop(self): | |
141 | self.load() | |
142 | while True: | |
143 | try: | |
144 | self._notifier.process_events() | |
145 | if self._notifier.check_events(): | |
146 | self._notifier.read_events() | |
147 | except KeyboardInterrupt: | |
148 | self._notifier.stop() | |
149 | break | |
150 | ||
151 | def __getattr__(self, key): | |
152 | return Template(key, self._globals) | |
153 | ||
154 | if __name__ == '__main__': | |
155 | t = Templates('/home/joe/templates') |