Create an application abstraction.

Groundwork for providing a post-fork app import option.
Fixes paster's ini not being able to register changes.
This commit is contained in:
Paul J. Davis 2010-05-13 23:58:55 -04:00
parent 63e39e1232
commit 5268b8fbbb
3 changed files with 123 additions and 56 deletions

94
gunicorn/app.py Normal file
View File

@ -0,0 +1,94 @@
# -*- coding: utf-8 -
#
# This file is part of gunicorn released under the MIT license.
# See the NOTICE for more information.
import os
import sys
from gunicorn import util
class Application(object):
"""\
An application interface for configuring and loading
the various necessities for any given web framework.
"""
def get_config(self):
return {}
def load(self):
raise NotImplementedError
class WSGIApplication(Application):
def __init__(self, app_uri):
self.app_uri = app_uri
def load(self):
return util.import_app(self.app_uri)
class DjangoApplication(Application):
def __init__(self, settings_modname, project_path):
self.project_path = project_path
self.settings_modname = settings_modname
# update sys.path
sys.path.insert(0, project_path)
sys.path.append(os.path.join(project_path, os.pardir))
def load(self):
import django.core.handlers.wsgi
os.environ['DJANGO_SETTINGS_MODULE'] = self.settings_modname
return django.core.handlers.wsgi.WSGIHandler()
class PasterApplication(Application):
def __init__(self, cfgurl, relpath, global_opts):
self.cfgurl = cfgurl
self.relpath = relpath
self.global_opts = global_opts
def local_conf(self):
from paste.deploy import loadwsgi
ctx = loadwsgi.loadcontext(loadwsgi.SERVER, self.cfgurl,
relative_to=self.relpath)
def mk_bind():
host = ctx.local_conf.get('host')
port = ctx.local_conf.get('port')
if host and port:
return '%s:%s' % (host, port)
elif host:
return host
ret = {}
vars = {
'bind': mk_bind,
'workers': lambda: ctx.local_conf.get('workers', 1),
'umask': lambda: int(ctx.local_conf.get('umask', UMASK)),
'group': lambda: ctx.local_conf.get('group'),
'user': lambda: ctx.local_conf.get('user')
}
for vn in vars:
if self.global_ops.get(vn):
val = vars[vn]()
if val:
ret[vn] = val
keys = ctx.local_conf.items()
keys = filter(self.global_opts.get, keys)
keys = filter(ret.has_key, keys)
ret.update((k, ctx.local_conf[k]) for k in keys)
if not self.global_opts.get("debug"):
ret['debug'] = (ctx.global_conf.get('debug') == "true")
ret['default_proc_name'] = ctx.global_conf.get('__file__')
return ret
def load(self):
from paste.deploy import loadapp
return loadapp(self.cfgurl, relative_to=self.relpath)

View File

@ -44,19 +44,30 @@ class Config(object):
)
def __init__(self, opts, path=None):
self.cfg = self.DEFAULTS.copy()
self.cfg = {}
self.opts = opts
if path is None:
path = os.path.join(os.getcwd(), self.DEFAULT_CONFIG_FILE)
if os.path.exists(path):
self.path = path
self.load()
def update(self, opts):
opts = dict((k, v) for (k, v) in opts.iteritems() if v is not None)
self.opts.update(opts)
self.cfg.update(opts)
def load(self):
self.cfg = self.DEFAULTS.copy()
if os.path.exists(self.path):
try:
execfile(path, globals(), self.cfg)
execfile(self.path, globals(), self.cfg)
except Exception, e:
sys.exit("Could not read config file: %r\n %s" % (path, e))
sys.exit("Could not read config file: %r\n %s" % (self.path, e))
self.cfg.pop("__builtins__", None)
opts = [(k, v) for (k, v) in opts.iteritems() if v is not None]
self.cfg.update(dict(opts))
opts = dict((k, v) for (k, v) in opts.iteritems() if v is not None)
self.cfg.update(opts)
def __getitem__(self, key):
try:

View File

@ -69,6 +69,7 @@ def main(usage, get_app):
cfg = Config(opts.__dict__, opts.config)
app = get_app(parser, opts, args)
cfg.update(app.get_config())
if cfg.spew:
spew()
if cfg.daemon:
@ -77,7 +78,7 @@ def main(usage, get_app):
os.setpgrp()
configure_logging(cfg)
Arbiter(cfg, app).run()
Arbiter(cfg, app.load()).run()
def run():
"""\
@ -90,10 +91,13 @@ def run():
if len(args) != 1:
parser.error("No application module specified.")
opts.default_proc_name = args[0]
application = app.WSGIApplication(args[0])
try:
return util.import_app(args[0])
application.load()
except Exception, e:
parser.error("Failed to import application module:\n %s" % e)
return application
main("%prog [OPTIONS] APP_MODULE", get_app)
@ -124,22 +128,15 @@ def run_django():
settings_path = os.path.join(project_path, "settings.py")
if not os.path.exists(settings_path):
settings_notfound(settings_path)
project_name = os.path.split(project_path)[-1]
sys.path.insert(0, project_path)
sys.path.append(os.path.join(project_path, os.pardir))
# set environ
project_name = os.path.split(project_path)[-1]
settings_name, ext = os.path.splitext(os.path.basename(settings_path))
settings_modname = '%s.%s' % (project_name, settings_name)
os.environ['DJANGO_SETTINGS_MODULE'] = settings_modname
settings_modname = "%s.%s" % (project_name, settings_name)
opts.default_proc_name = settings_modname
# django wsgi app
return django.core.handlers.wsgi.WSGIHandler()
return app.DjangoApplication(settings_modname, project_path)
@ -150,7 +147,7 @@ def run_paster():
The ``gunicorn_paster`` command for launcing Paster compatible
apllications like Pylons or Turbogears2
"""
from paste.deploy import loadapp, loadwsgi
from paste.deploy import loadwsgi
def get_app(parser, opts, args):
if len(args) != 1:
@ -169,43 +166,8 @@ def run_paster():
# add to eggs
pkg_resources.working_set.add_entry(relpath)
ctx = loadwsgi.loadcontext(loadwsgi.SERVER, cfgurl, relative_to=relpath)
if not opts.workers:
opts.workers = ctx.local_conf.get('workers', 1)
if not opts.umask:
opts.umask = int(ctx.local_conf.get('umask', UMASK))
if not opts.group:
opts.group = ctx.local_conf.get('group')
if not opts.user:
opts.user = ctx.local_conf.get('user')
if not opts.bind:
host = ctx.local_conf.get('host')
port = ctx.local_conf.get('port')
if host:
if port:
bind = "%s:%s" % (host, port)
else:
bind = host
opts.bind = bind
for k, v in ctx.local_conf.items():
if not hasattr(opts, k):
setattr(opts, k, v)
if not opts.debug:
opts.debug = (ctx.global_conf.get('debug') == "true")
opts.default_proc_name= ctx.global_conf.get('__file__')
app = loadapp(cfgurl, relative_to=relpath)
return app
return app.PasterApplication(cfgurl, relpath, opts.__dict__)
main("%prog [OPTIONS] pasteconfig.ini", get_app)