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