From e273052e3f53ec2770df939fa04eaf71a903207f Mon Sep 17 00:00:00 2001 From: Stephane Wirtel Date: Sun, 1 Dec 2013 15:51:20 +0100 Subject: [PATCH 1/5] Add the capability to load gunicorn.base.Application without the loading of the arguments of the command line. --- gunicorn/app/base.py | 63 ++++++++++++++++++++++++-------------------- 1 file changed, 34 insertions(+), 29 deletions(-) diff --git a/gunicorn/app/base.py b/gunicorn/app/base.py index 21f07081..1ad0aeb5 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 @@ -29,14 +28,43 @@ class Application(object): def do_load_config(self): 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 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): if not os.path.exists(filename): raise RuntimeError("%r doesn't exist" % filename) @@ -67,9 +95,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 +123,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 +148,5 @@ 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() + From b9d4afb7bd34972935167ad226bf98c5254ea8a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Wirtel?= Date: Sun, 29 Dec 2013 22:15:37 +0100 Subject: [PATCH 2/5] Add an example for the gunicorn.app.base.BaseApplication --- docs/source/run.rst | 57 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/docs/source/run.rst b/docs/source/run.rst index 1a28346a..2ff0ce1c 100644 --- a/docs/source/run.rst +++ b/docs/source/run.rst @@ -127,3 +127,60 @@ However, in this configuration, Gunicorn does not reload the application when new workers are started. See the note about preloading_. .. _preloading: configure.html#preload-app + + +custom application +------------------ + +Sometimes, you want to integrate Gunicorn with your WSGI application. In this +case, you can inherit from gunicorn.app.base.BaseApplication. + +Example:: + + #!/usr/bin/env python + import gunicorn.app.base + + def my_index(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 MyApplication(gunicorn.app.base.BaseApplication): + def __init__(self, app, options=None): + self.options = dict(options or {}) + self.application = app + super(MyApplication, 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, + } + MyApplication(my_index, options).run() From a44807f9f5c2736f937e90789f439effa576dc5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Wirtel?= Date: Tue, 22 Apr 2014 21:48:57 +0200 Subject: [PATCH 3/5] Add an example of a standalone app --- docs/source/run.rst | 8 ++--- examples/standalone_app.py | 63 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 4 deletions(-) create mode 100644 examples/standalone_app.py diff --git a/docs/source/run.rst b/docs/source/run.rst index 2ff0ce1c..a91cb3b5 100644 --- a/docs/source/run.rst +++ b/docs/source/run.rst @@ -140,7 +140,7 @@ Example:: #!/usr/bin/env python import gunicorn.app.base - def my_index(environ, start_response): + def handler_app(environ, start_response): response_body = 'Works fine' status = '200 OK' @@ -153,11 +153,11 @@ Example:: return [response_body] - class MyApplication(gunicorn.app.base.BaseApplication): + class StandaloneApplication(gunicorn.app.base.BaseApplication): def __init__(self, app, options=None): self.options = dict(options or {}) self.application = app - super(MyApplication, self).__init__() + super(StandaloneApplication, self).__init__() def load_config(self): tmp_config = map( @@ -183,4 +183,4 @@ Example:: 'workers': 4, # 'pidfile': pidfile, } - MyApplication(my_index, options).run() + StandaloneApplication(handler_app, options).run() diff --git a/examples/standalone_app.py b/examples/standalone_app.py new file mode 100644 index 00000000..800d5a1c --- /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() \ No newline at end of file From 6211560c476474dcf2ad0e2e79d5caf7e862bb32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Wirtel?= Date: Wed, 30 Apr 2014 10:59:44 +0200 Subject: [PATCH 4/5] Start the documentation for the BaseApplication class --- gunicorn/app/base.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/gunicorn/app/base.py b/gunicorn/app/base.py index 1ad0aeb5..08958e25 100644 --- a/gunicorn/app/base.py +++ b/gunicorn/app/base.py @@ -27,6 +27,9 @@ class BaseApplication(object): self.do_load_config() def do_load_config(self): + """ + Loads the configuration + """ try: self.load_default_config() self.load_config() @@ -45,6 +48,14 @@ class BaseApplication(object): 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: @@ -65,6 +76,10 @@ class BaseApplication(object): 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) From 33d35a1a665b4388a4d5cb77ea9903d0a2973d49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Wirtel?= Date: Wed, 30 Apr 2014 11:41:18 +0200 Subject: [PATCH 5/5] Work In Progress: Move the custom application section to a specific file and add it into the TOC --- docs/source/custom.rst | 61 ++++++++++++++++++++++++++++++++++++++++++ docs/source/index.rst | 1 + docs/source/run.rst | 56 -------------------------------------- 3 files changed, 62 insertions(+), 56 deletions(-) create mode 100644 docs/source/custom.rst diff --git a/docs/source/custom.rst b/docs/source/custom.rst new file mode 100644 index 00000000..4dfba9e6 --- /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/docs/source/run.rst b/docs/source/run.rst index a91cb3b5..baa0fa32 100644 --- a/docs/source/run.rst +++ b/docs/source/run.rst @@ -128,59 +128,3 @@ new workers are started. See the note about preloading_. .. _preloading: configure.html#preload-app - -custom application ------------------- - -Sometimes, you want to integrate Gunicorn with your WSGI application. In this -case, you can inherit from gunicorn.app.base.BaseApplication. - -Example:: - - #!/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()