Skip to content

Commit

Permalink
Basic dynamic module loading
Browse files Browse the repository at this point in the history
  • Loading branch information
parente committed Dec 10, 2010
1 parent 8efc7c2 commit 608866e
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 17 deletions.
65 changes: 48 additions & 17 deletions handlerbag.py
Expand Up @@ -14,42 +14,43 @@
import os.path import os.path
import shelve import shelve
import sys import sys
import re
# handlerbag # handlerbag
import hbag import hbag


class HandlerBag(tornado.web.Application): class HandlerBag(tornado.web.Application):
def __init__(self, **kwargs): def __init__(self, **kwargs):
# always enabled debug for auto reload super(HandlerBag, self).__init__([], **kwargs)
kwargs['debug'] = False
# load the bag db # load the bag db
self.db = shelve.open('hbdata') self.db = shelve.open('hbdata')
# import dynamic module # import dynamic module
self.modules = {} self.modules = {}


# always load the admin handler
import hbag.admin as admin
handlers = hbag.admin.get_handler_map(options.webroot)
self.modules['admin'] = admin

# store paths # store paths
self.appPath = os.path.dirname(os.path.abspath(__file__)) self.appPath = os.path.dirname(os.path.abspath(__file__))
self.bagPath = os.path.dirname(hbag.__file__) self.bagPath = os.path.dirname(hbag.__file__)


# set the initial handlers and options # update db to reflect available handlers
super(HandlerBag, self).__init__(handlers, **kwargs) self.refresh_handlers_in_db()

# load all enabled handlers
for name in self.db:
if self.db[name]['enabled'] or name == 'admin':
self.set_handler_status(name, True)


def shutdown(self): def shutdown(self):
# close the db cleanly # close the db cleanly
self.db.close() self.db.close()


def add_dynamic_handlers(self, host, handlers, pos=0): def add_dynamic_handlers(self, host_pattern, host_handlers, pos=0):
handlers = None handlers = None
for regex, h in self.handlers: for regex, h in self.handlers:
if regex.pattern == host_pattern: if regex.pattern == host_pattern:
handlers = h handlers = h
break break
if handlers is None: if handlers is None:
raise ValueError('cannot extend handlers for unknown host') handlers = []
self.handlers.append((re.compile(host_pattern), handlers))
for spec in host_handlers: for spec in host_handlers:
if type(spec) is type(()): if type(spec) is type(()):
assert len(spec) in (2, 3) assert len(spec) in (2, 3)
Expand All @@ -62,7 +63,7 @@ def add_dynamic_handlers(self, host, handlers, pos=0):
spec = tornado.web.URLSpec(pattern, handler, kwargs) spec = tornado.web.URLSpec(pattern, handler, kwargs)
handlers.insert(pos, spec) handlers.insert(pos, spec)


def remove_dynamic_handler(self, host, url): def remove_dynamic_handler(self, host_pattern, url):
handlers = None handlers = None
for regex, h in self.handlers: for regex, h in self.handlers:
if regex.pattern == host_pattern: if regex.pattern == host_pattern:
Expand All @@ -81,8 +82,14 @@ def remove_dynamic_handler(self, host, url):
i += 1 i += 1


def refresh_handlers_in_db(self): def refresh_handlers_in_db(self):
# grab packages
g = glob.glob(os.path.join(self.bagPath, '*')) g = glob.glob(os.path.join(self.bagPath, '*'))
avail = set((os.path.basename(d) for d in g if os.path.isdir(d))) avail = set((os.path.basename(d) for d in g if os.path.isdir(d)))
# grab modules
g = glob.glob(os.path.join(self.bagPath, '*.py'))
avail.update((os.path.basename(d).split('.')[0]
for d in g if not d.endswith('__init__.py')))

known = set(self.db.keys()) known = set(self.db.keys())
new = avail - known new = avail - known
for name in new: for name in new:
Expand All @@ -96,15 +103,39 @@ def set_handler_status(self, name, enable):
# not a known handler # not a known handler
if not self.db.has_key(name): return if not self.db.has_key(name): return
info = self.db[name] info = self.db[name]
# no change
if info['enabled'] == enable: return


# set the state in the db # set the state in the db
info = self.db[name] info = self.db[name]
info['enabled'] = enable info['enabled'] = enable
self.db[name] = info self.db[name] = info

if enable:
# check if module already loaded
try:
mod = self.modules[name]
except KeyError:
# load the module for the first time
mod = __import__('hbag.'+name, fromlist=[name])
self.modules[name] = mod
else:
# reload the module if loaded
mod = reload(mod)
self.modules[name] = mod


# check if module already loaded # register the handlers
handlers = mod.get_handler_map(options.webroot)
self.add_dynamic_handlers('.*$', handlers)
else:
# unregister the handlers
try:
mod = self.modules[name]
except KeyError:
# nothing to do if never loaded
return
handlers = mod.get_handler_map(options.webroot)
for url, cls in handlers:
self.remove_dynamic_handler('.*$', url)
# keep tracking module for later reload


if __name__ == '__main__': if __name__ == '__main__':
define('webroot', default='/', help='absolute root url of all handlers (default: /)') define('webroot', default='/', help='absolute root url of all handlers (default: /)')
Expand Down
9 changes: 9 additions & 0 deletions hbag/hello.py
@@ -0,0 +1,9 @@
# tornado
import tornado.web

class HelloHandler(tornado.web.RequestHandler):
def get(self, *args, **kwargs):
self.write('Hello world!')

def get_handler_map(webroot):
return [(webroot+'hello/?', HelloHandler)]

0 comments on commit 608866e

Please sign in to comment.