import webob.exc from repoze.bfg.configuration import Configurator from repoze.bfg.url import route_url from repoze.bfg.threadlocal import get_current_request from repoze.bfg.exceptions import NotFound from repoze.bfg.exceptions import Forbidden from repoze.bfg.events import NewRequest from repoze.bfg.view import AppendSlashNotFoundViewFactory from repoze.bfg.view import append_slash_notfound_view from repoze.bfg.renderers import render from beaker.middleware import SessionMiddleware from repoze.bfg.jinja2.bindings import renderer_factory as jinja2_renderer from paste.httpserver import serve from paste.registry import StackedObjectProxy from paste.registry import RegistryManager from weberror.evalexception import EvalException request = StackedObjectProxy() application = StackedObjectProxy() g = StackedObjectProxy() class Namespace(object): pass class Groundhog(object): def __init__(self, package, session_key): self.package = package self.session_key = session_key self.g = Namespace() self.config = Configurator() self.config.begin() self.config.add_renderer('groundhog', self.renderer_factory) self.config.add_renderer('.html', jinja2_renderer) self.config.set_renderer_globals_factory(self.renderer_globals_factory) self.config.add_view(append_slash_notfound_view, context=NotFound) self.config.add_view(self.handle_webob_exc, context=webob.exc.WSGIHTTPException) self.config.add_static_view('static', '%s:static' % self.package) self.config.add_subscriber(self.start_request, NewRequest) def start_request(self, event): req = event.request environ = req.environ reg = environ.get('paste.registry') if reg is not None: reg.register(request, req) reg.register(application, self) reg.register(g, self.g) def handle_webob_exc(self, exc, request): return request.get_response(exc) def renderer_factory(self, name): def render_(value, system): request = system.get('request') if request is not None: request.response_content_type = 'text/html' return value return render_ def renderer_globals_factory(self, system): return {'g':self.g, 'app':self} def mapply(self, func): def mapplied(request): return func(**request.matchdict) return mapplied def route(self, pattern, methods=('GET', 'HEAD')): def decorator(func): endpoint = func.__name__ self.config.add_route(endpoint, pattern) for method in methods: view = self.mapply(func) self.config.add_view(view, route_name=endpoint, request_method=method, renderer='groundhog') return func return decorator def run(self, host=None, port=8080, debug=False): self.config.end() app = self.config.make_wsgi_app() if debug: app = EvalException(app) session_options = { 'session.type':'cookie', 'session.cookie_expires':True, 'session.auto':True, 'session.validate_key':self.session_key, } app = SessionMiddleware(app, session_options) app = RegistryManager(app) serve(app, host, port) def url_for(self, endpoint, *arg, **options): if not isinstance(endpoint, basestring): endpoint = endpoint.__name__ request = self.request return route_url(endpoint, request, *arg, **options) @property def request(self): return get_current_request() @property def session(self): return self.request.environ['beaker.session'] def flash(self, message): session = self.session fq = session.setdefault('flash_queue', []) fq.append(message) session.save() def get_flashed_messages(self): session = self.session result = session.pop('flash_queue', []) session.save() return result def render_template(self, name, **kw): request = self.request return render('%s:templates/%s' % (self.package, name), request=request, **kw) def abort(self, code, message=''): raise webob.exc.status_map[code](message) def redirect(self, url): raise webob.exc.HTTPFound(location=url) def errorhandler(self, code): bfg_status_map = {404:NotFound, 401:Forbidden} def decorator(func): def errfunc(exc, request): request.response_status = exc.status return func(exc) webob_exc = webob.exc.status_map.get(code) bfg_exc = bfg_status_map.get(code) for exc in (webob_exc, bfg_exc): if exc is not None: view = errfunc if exc is NotFound: view = AppendSlashNotFoundViewFactory(errfunc) self.config.add_view(view, context=exc, renderer='groundhog') return decorator def listen(self, *event_types): def decorator(func): if event_types: for typ in event_types: self.config.add_subscriber(func, typ) else: self.config.add_subscriber(func, None) return decorator def notify(self, event): self.config.registry.notify(event) # application #from groundhog import request app = Groundhog(__name__, 'seekrit') @app.route('/') def root(): return 'root' @app.route('/exc/:code') def exc(code=404): code = int(code) app.abort(code, 'this is the message') @app.route('/redir') def redir(): app.redirect('/') class MyEvent(object): def __init__(self, param): self.param = param @app.route('/notify/:param') def notify(param=None): app.notify(MyEvent(param)) return 'notified' @app.listen() def listen(event): print event, event.__dict__ @app.route('/showglobals') def showglobals(): import cgi print request print application print g return cgi.escape('%s %s %s' % (request, application, g)) if __name__ == '__main__': app.run(debug=True)