diff --git a/docs/source/custom.rst b/docs/source/custom.rst new file mode 100644 index 00000000..88f5eaf2 --- /dev/null +++ b/docs/source/custom.rst @@ -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() diff --git a/docs/source/index.rst b/docs/source/index.rst index 51f19959..b7e33b45 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -43,6 +43,7 @@ Contents deploy design signals + custom community faq news diff --git a/examples/standalone_app.py b/examples/standalone_app.py new file mode 100644 index 00000000..a9a7267a --- /dev/null +++ b/examples/standalone_app.py @@ -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() diff --git a/gunicorn/app/base.py b/gunicorn/app/base.py index 21f07081..b23b6b35 100644 --- a/gunicorn/app/base.py +++ b/gunicorn/app/base.py @@ -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()