Skip to content

Commit

Permalink
Merge pull request benoitc#1450 from hramezani/issue_1359
Browse files Browse the repository at this point in the history
Specify wsgi_app in config
  • Loading branch information
tilgovi authored May 3, 2020
2 parents b2dc036 + 57a9e2e commit bac7f03
Show file tree
Hide file tree
Showing 6 changed files with 81 additions and 6 deletions.
7 changes: 5 additions & 2 deletions docs/source/run.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,15 @@ gunicorn

Basic usage::

$ gunicorn [OPTIONS] APP_MODULE
$ gunicorn [OPTIONS] [WSGI_APP]

Where ``APP_MODULE`` is of the pattern ``$(MODULE_NAME):$(VARIABLE_NAME)``. The
Where ``WSGI_APP`` is of the pattern ``$(MODULE_NAME):$(VARIABLE_NAME)``. The
module name can be a full dotted path. The variable name refers to a WSGI
callable that should be found in the specified module.

.. versionchanged:: 20.1.0
``WSGI_APP`` is optional if it is defined in a :ref:`config` file.

Example with the test app:

.. code-block:: python
Expand Down
11 changes: 11 additions & 0 deletions docs/source/settings.rst
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,17 @@ application specific configuration.
Loading the config from a Python module requires the ``python:``
prefix.

.. _wsgi-app:

wsgi_app
~~~~~~~~

* ``None``

A WSGI application path in pattern ``$(MODULE_NAME):$(VARIABLE_NAME)``.

.. versionadded:: 20.1.0

Debugging
---------

Expand Down
17 changes: 13 additions & 4 deletions gunicorn/app/wsgiapp.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@

class WSGIApplication(Application):
def init(self, parser, opts, args):
self.app_uri = None

if opts.paste:
from .pasterapp import has_logging_config

Expand All @@ -29,11 +31,18 @@ def init(self, parser, opts, args):

return

if not args:
parser.error("No application module specified.")
if len(args) > 0:
self.cfg.set("default_proc_name", args[0])
self.app_uri = args[0]

def load_config(self):
super().load_config()

self.cfg.set("default_proc_name", args[0])
self.app_uri = args[0]
if self.app_uri is None:
if self.cfg.wsgi_app is not None:
self.app_uri = self.cfg.wsgi_app
else:
raise ConfigError("No application module specified.")

def load_wsgiapp(self):
return util.import_app(self.app_uri)
Expand Down
11 changes: 11 additions & 0 deletions gunicorn/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,17 @@ class ConfigFile(Setting):
prefix.
"""

class WSGIApp(Setting):
name = "wsgi_app"
section = "Config File"
meta = "STRING"
validator = validate_string
default = None
desc = """\
A WSGI application path in pattern ``$(MODULE_NAME):$(VARIABLE_NAME)``.
.. versionadded:: 20.1.0
"""

class Bind(Setting):
name = "bind"
Expand Down
1 change: 1 addition & 0 deletions tests/config/test_cfg_with_wsgi_app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
wsgi_app = "app1:app1"
40 changes: 40 additions & 0 deletions tests/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

from gunicorn import config
from gunicorn.app.base import Application
from gunicorn.app.wsgiapp import WSGIApplication
from gunicorn.errors import ConfigError
from gunicorn.workers.sync import SyncWorker
from gunicorn import glogging
Expand All @@ -25,6 +26,8 @@ def cfg_file():
return os.path.join(dirname, "config", "test_cfg.py")
def alt_cfg_file():
return os.path.join(dirname, "config", "test_cfg_alt.py")
def cfg_file_with_wsgi_app():
return os.path.join(dirname, "config", "test_cfg_with_wsgi_app.py")
def paster_ini():
return os.path.join(dirname, "..", "examples", "frameworks", "pylonstest", "nose.ini")

Expand Down Expand Up @@ -52,6 +55,14 @@ def load(self):
pass


class WSGIApp(WSGIApplication):
def __init__(self):
super().__init__("no_usage", prog="gunicorn_test")

def load(self):
pass


def test_defaults():
c = config.Config()
for s in config.KNOWN_SETTINGS:
Expand Down Expand Up @@ -353,13 +364,42 @@ def test_invalid_enviroment_variables_config(monkeypatch, capsys):
_, err = capsys.readouterr()
assert "error: unrecognized arguments: --foo" in err


def test_cli_overrides_enviroment_variables_module(monkeypatch):
monkeypatch.setenv("GUNICORN_CMD_ARGS", "--workers=4")
with AltArgs(["prog_name", "-c", cfg_file(), "--workers", "3"]):
app = NoConfigApp()
assert app.cfg.workers == 3


@pytest.mark.parametrize("options, expected", [
(["app:app"], 'app:app'),
(["-c", cfg_file(), "app:app"], 'app:app'),
(["-c", cfg_file_with_wsgi_app(), "app:app"], 'app:app'),
(["-c", cfg_file_with_wsgi_app()], 'app1:app1'),
])
def test_wsgi_app_config(options, expected):
cmdline = ["prog_name"]
cmdline.extend(options)
with AltArgs(cmdline):
app = WSGIApp()
assert app.app_uri == expected


@pytest.mark.parametrize("options", [
([]),
(["-c", cfg_file()]),
])
def test_non_wsgi_app(options, capsys):
cmdline = ["prog_name"]
cmdline.extend(options)
with AltArgs(cmdline):
with pytest.raises(SystemExit):
WSGIApp()
_, err = capsys.readouterr()
assert "Error: No application module specified." in err


@pytest.mark.parametrize("options, expected", [
(["myapp:app"], False),
(["--reload", "myapp:app"], True),
Expand Down

0 comments on commit bac7f03

Please sign in to comment.