--- /dev/null
+# Joe Presbrey <presbrey@mit.edu>
+
+import os, sys, types, time
+import threading
+from pyinotify import WatchManager, Notifier, EventsCodes
+from Cheetah.Template import Template as _Template
+
+__all__ = ['Templates']
+
+class Template(object):
+ def __init__(self, name, ns={}, path=['templates']):
+ self._name, self._ns, self._path = name, ns, path+[name]
+ assert self._template()
+
+ def _template(self):
+ return sys.modules['.'.join(self._path)]
+
+ def __getattr__(self, name):
+ return Template(name, self._ns, self._path)
+
+ def __call__(self, **kw):
+ namespaces = self._ns.copy()
+ namespaces.update(kw)
+ return getattr(self._template(),
+ self._name)(namespaces=namespaces).respond()
+
+class Templates(object):
+ _base = ''
+ _mask = EventsCodes.ALL_FLAGS['IN_CREATE'] \
+ | EventsCodes.ALL_FLAGS['IN_MODIFY'] \
+ | EventsCodes.ALL_FLAGS['IN_DELETE'] \
+ | EventsCodes.ALL_FLAGS['IN_ATTRIB']
+ _watch_manager = None
+ _notifier = None
+ _thread = None
+
+ _loaded = False
+ _files = {}
+ _modules = {}
+
+ def __init__(self, template_path, global_namespace={}):
+ self._base = os.path.abspath(template_path)
+ self._event_init()
+ self._assert_module('templates', [template_path])
+ self._globals = global_namespace
+ self.load()
+ self._thread = threading.Thread(target=self.loop)
+ self._thread.setDaemon(True)
+ self._thread.start()
+
+ def _tree_walk(self, tree, path, func):
+ path = list(path)
+ if len(path) > 1:
+ x = path.pop(0)
+ if not x in tree:
+ tree[x] = {}
+ return self._tree_walk(tree[x], path, func)
+ elif callable(func):
+ return func(tree, path[0])
+
+ def _tree_add(self, tree, path, value):
+ def _add(tree, key):
+ tree[key] = value
+ return value
+ print self._tree_walk(tree, path, _add)
+
+ def _assert_module(self, name, path=None):
+ if not name in self._modules:
+ self._modules[name] = types.ModuleType(name)
+ if path:
+ self._modules[name].__path__ = path
+ sys.modules[name] = self._modules[name]
+
+ def _get_template_path(self, template_file):
+ r = template_file.startswith(self._base) \
+ and template_file[len(self._base)+1:].split(os.path.sep) or []
+ if r[-1].endswith('.tmpl'):
+ r[-1] = r[-1][:-5]
+ return tuple(r)
+
+ def _compile(self, template_path):
+ template = _Template.compile(file=self._files[template_path],
+ returnAClass=False,
+ moduleName='.'.join(['templates']+list(template_path)),
+ className=template_path[-1],
+ useCache=False,
+ compilerSettings={'useStackFrames':False})
+ return template
+
+ def add(self, template_file):
+ template_path = self._get_template_path(template_file)
+ template_name = '.'.join(['templates']+list(template_path))
+ template_pkg = '.'.join(['templates']+list(template_path[:-1]))
+ template_py = os.path.join(os.path.dirname(template_file), '%s.py' % template_path[-1])
+ self._assert_module(template_pkg, [os.path.dirname(template_file)])
+ self._files[template_path] = template_file
+ code = self._compile(template_path)
+ f_code = file(template_py, 'w')
+ f_code.write(code)
+ f_code.close()
+ if self._loaded and \
+ template_name in sys.modules:
+ reload(sys.modules[template_name])
+
+ def remove(self, template_file):
+ #print 'removing', template_file
+ pass
+
+ def _walk_load(self, args, dname, fnames):
+ for fname in fnames:
+ if fname.endswith('.tmpl'):
+ template_file = os.path.join(dname, fname)
+ self.add(template_file)
+
+ def load(self):
+ if not self._loaded:
+ os.path.walk(self._base, self._walk_load, None)
+ for path, filename in self._files.items():
+ __import__('.'.join(['templates']+list(path)))
+ self._loaded = True
+
+ def _event_handler(self, event):
+ if event.name.endswith('.tmpl') and not event.name.startswith('.'):
+ if event.maskname == 'IN_DELETE':
+ self.remove(event.pathname)
+ else:
+ time.sleep(0.5)
+ self.add(event.pathname)
+
+
+ def _event_init(self):
+ self._watch_manager = WatchManager()
+ self._watch_manager.add_watch(self._base,
+ self._mask,
+ self._event_handler,
+ rec=True,
+ auto_add=True)
+ self._notifier = Notifier(self._watch_manager)
+
+ def loop(self):
+ self.load()
+ while True:
+ try:
+ self._notifier.process_events()
+ if self._notifier.check_events():
+ self._notifier.read_events()
+ except KeyboardInterrupt:
+ self._notifier.stop()
+ break
+
+ def __getattr__(self, key):
+ return Template(key, self._globals)
+
+if __name__ == '__main__':
+ t = Templates('/home/joe/templates')