Merge branch 'master' of https://github.com/benoitc/gunicorn into statsd-logger

This commit is contained in:
Alexis Le-Quoc 2014-05-12 17:51:02 +02:00
commit 8a3a3fcfa3
7 changed files with 177 additions and 42 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()

View File

@ -463,8 +463,8 @@ class Arbiter(object):
continue
worker.tmp.close()
except OSError as e:
if e.errno == errno.ECHILD:
pass
if e.errno != errno.ECHILD:
raise
def manage_workers(self):
"""\

View File

@ -78,10 +78,6 @@ class Config(object):
help="show program's version number and exit")
parser.add_argument("args", nargs="*", help=argparse.SUPPRESS)
keys = list(self.settings)
def sorter(k):
return (self.settings[k].section, self.settings[k].order)
keys = sorted(self.settings, key=self.settings.__getitem__)
for k in keys:
self.settings[k].add_option(parser)

View File

@ -45,18 +45,13 @@ except ImportError:
def make_options():
g_settings = make_settings(ignore=("version"))
keys = g_settings.keys()
def sorter(k):
return (g_settings[k].section, g_settings[k].order)
opts = [
make_option('--adminmedia', dest='admin_media_path', default='',
help='Specifies the directory from which to serve admin media.')
]
g_settings = make_settings(ignore=("version"))
keys = g_settings.keys()
for k in keys:
if k in ('pythonpath', 'django_settings',):
continue