Merge pull request #727 from benoitc/feature/651

PR that rebase #651. Should also fix #625.

Add the capability to load gunicorn.base.Application without the
loading of the arguments of the command line.
This commit is contained in:
Randall Leeds 2014-05-07 14:48:40 -07:00
commit fd22ea24ae
4 changed files with 173 additions and 29 deletions

61
docs/source/custom.rst Normal file
View File

@ -0,0 +1,61 @@
.. _custom:
==================
Custom Application
==================
.. versionadded:: 19.0
Sometimes, you want to integrate Gunicorn with your WSGI application. In this
case, you can inherit from :class:`gunicorn.app.base.BaseApplication`.
Here is a small example where we create a very small WSGI app and load it with a
custom Application::
#!/usr/bin/env python
import gunicorn.app.base
def handler_app(environ, start_response):
response_body = 'Works fine'
status = '200 OK'
response_headers = [
('Content-Type', 'text/plain'),
('Content-Length', str(len(response_body)))
]
start_response(status, response_headers)
return [response_body]
class StandaloneApplication(gunicorn.app.base.BaseApplication):
def __init__(self, app, options=None):
self.options = dict(options or {})
self.application = app
super(StandaloneApplication, self).__init__()
def load_config(self):
tmp_config = map(
lambda item: (item[0].lower(), item[1]),
self.options.iteritems()
)
config = dict(
(key, value)
for key, value in tmp_config
if key in self.cfg.settings and value is not None
)
for key, value in config.iteritems():
self.cfg.set(key.lower(), value)
def load(self):
return self.application
if __name__ == '__main__':
options = {
'bind': '%s:%s' % ('127.0.0.1', '8080'),
'workers': 4,
# 'pidfile': pidfile,
}
StandaloneApplication(handler_app, options).run()

View File

@ -43,6 +43,7 @@ Contents
deploy
design
signals
custom
community
faq
news

View File

@ -0,0 +1,63 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# An example of a standalone application using the internal API of Gunicorn.
#
# $ python standalone_app.py
#
# This file is part of gunicorn released under the MIT license.
# See the NOTICE for more information.
import gunicorn
import gunicorn.app.base
import multiprocessing
def number_of_workers():
return (multiprocessing.cpu_count() * 2) + 1
def handler_app(environ, start_response):
response_body = 'Works fine'
status = '200 OK'
response_headers = [
('Content-Type', 'text/plain'),
('Content-Lenfth', str(len(response_body))),
]
start_response(status, response_headers)
return [response_body]
class StandaloneApplication(gunicorn.app.base.BaseApplication):
def __init__(self, app, options=None):
self.options = dict(options or {})
self.application = app
super(StandaloneApplication, self).__init__()
def load_config(self):
tmp_config = map(
lambda item: (item[0].lower(), item[1]),
self.options.iteritems()
)
config = dict(
(key, value)
for key, value in tmp_config
if key in self.cfg.settings and value is not None
)
for key, value in config.iteritems():
self.cfg.set(key.lower(), value)
def load(self):
return self.application
if __name__ == '__main__':
options = {
'bind': '%s:%s' % ('127.0.0.1', '8080'),
'workers': number_of_workers(),
}
StandaloneApplication(handler_app, options).run()

View File

@ -13,12 +13,11 @@ from gunicorn.config import Config, get_default_config_file
from gunicorn import debug
from gunicorn.six import execfile_
class Application(object):
"""\
class BaseApplication(object):
"""
An application interface for configuring and loading
the various necessities for any given web framework.
"""
def __init__(self, usage=None, prog=None):
self.usage = usage
self.cfg = None
@ -28,15 +27,59 @@ class Application(object):
self.do_load_config()
def do_load_config(self):
"""
Loads the configuration
"""
try:
self.load_default_config()
self.load_config()
except Exception as e:
sys.stderr.write("\nError: %s\n" % str(e))
sys.stderr.flush()
sys.exit(1)
def load_config_from_file(self, filename):
def load_default_config(self):
# init configuration
self.cfg = Config(self.usage, prog=self.prog)
def init(self, parser, opts, args):
raise NotImplementedError
def load(self):
raise NotImplementedError
def load_config(self):
"""
This method is used to load the configuration from one or several input(s).
Custom Command line, configuration file.
You have to override this method in your class.
"""
raise NotImplementedError
def reload(self):
self.do_load_config()
if self.cfg.spew:
debug.spew()
def wsgi(self):
if self.callable is None:
self.callable = self.load()
return self.callable
def run(self):
try:
Arbiter(self).run()
except RuntimeError as e:
sys.stderr.write("\nError: %s\n\n" % e)
sys.stderr.flush()
sys.exit(1)
class Application(BaseApplication):
def load_config_from_file(self, filename):
"""
Loads the configuration file: the file is a python file, otherwise raise an RuntimeError
Exception or stop the process if the configuration file contains a syntax error.
"""
if not os.path.exists(filename):
raise RuntimeError("%r doesn't exist" % filename)
@ -67,9 +110,6 @@ class Application(object):
return cfg
def load_config(self):
# init configuration
self.cfg = Config(self.usage, prog=self.prog)
# parse console args
parser = self.cfg.parser()
args = parser.parse_args()
@ -98,22 +138,6 @@ class Application(object):
continue
self.cfg.set(k.lower(), v)
def init(self, parser, opts, args):
raise NotImplementedError
def load(self):
raise NotImplementedError
def reload(self):
self.do_load_config()
if self.cfg.spew:
debug.spew()
def wsgi(self):
if self.callable is None:
self.callable = self.load()
return self.callable
def run(self):
if self.cfg.check_config:
try:
@ -139,9 +163,4 @@ class Application(object):
if pythonpath not in sys.path:
sys.path.insert(0, pythonpath)
try:
Arbiter(self).run()
except RuntimeError as e:
sys.stderr.write("\nError: %s\n\n" % e)
sys.stderr.flush()
sys.exit(1)
super(Application, self).run()