diff --git a/docs/source/configure.rst b/docs/source/configure.rst index c340f5ee..0a8e7fe7 100644 --- a/docs/source/configure.rst +++ b/docs/source/configure.rst @@ -38,7 +38,12 @@ Once again, in order of least to most authoritative: .. note:: - To check your configuration when using the command line or the + To print your resolved configuration when using the command line or the + configuration file you can run the following command:: + + $ gunicorn --print-config APP_MODULE + + To check your resolved configuration when using the command line or the configuration file you can run the following command:: $ gunicorn --check-config APP_MODULE diff --git a/docs/source/settings.rst b/docs/source/settings.rst index 01d49883..8ebe0484 100644 --- a/docs/source/settings.rst +++ b/docs/source/settings.rst @@ -123,6 +123,16 @@ check_config Check the configuration. +.. _print-config: + +print_config +~~~~~~~~~~~~ + +* ``--print-config`` +* ``False`` + +Print the configuration settings as fully resolved. Implies :ref:`check-config`. + Logging ------- diff --git a/gunicorn/app/base.py b/gunicorn/app/base.py index 240f5eb2..df8c666f 100644 --- a/gunicorn/app/base.py +++ b/gunicorn/app/base.py @@ -200,7 +200,10 @@ class Application(BaseApplication): self.chdir() def run(self): - if self.cfg.check_config: + if self.cfg.print_config: + print(self.cfg) + + if self.cfg.print_config or self.cfg.check_config: try: self.load() except Exception: diff --git a/gunicorn/config.py b/gunicorn/config.py index 1032dae6..17e49279 100644 --- a/gunicorn/config.py +++ b/gunicorn/config.py @@ -51,6 +51,16 @@ class Config(object): self.prog = prog or os.path.basename(sys.argv[0]) self.env_orig = os.environ.copy() + def __str__(self): + lines = [] + kmax = max(len(k) for k in self.settings) + for k in sorted(self.settings): + v = self.settings[k].value + if callable(v): + v = "<{}()>".format(v.__qualname__) + lines.append("{k:{kmax}} = {v}".format(k=k, v=v, kmax=kmax)) + return "\n".join(lines) + def __getattr__(self, name): if name not in self.settings: raise AttributeError("No configuration setting for: %s" % name) @@ -951,6 +961,18 @@ class ConfigCheck(Setting): """ +class PrintConfig(Setting): + name = "print_config" + section = "Debugging" + cli = ["--print-config"] + validator = validate_bool + action = "store_true" + default = False + desc = """\ + Print the configuration settings as fully resolved. Implies :ref:`check-config`. + """ + + class PreloadApp(Setting): name = "preload_app" section = "Server Mechanics" diff --git a/tests/test_config.py b/tests/test_config.py index 8b1922e6..c176b66b 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -4,6 +4,7 @@ # See the NOTICE for more information. import os +import re import sys import pytest @@ -442,3 +443,38 @@ def test_repr(): c.set("workers", 5) assert "with value 5" in repr(c.settings['workers']) + + +def test_str(): + c = config.Config() + o = str(c) + + # match the first few lines, some different types, but don't go OTT + # to avoid needless test fails with changes + OUTPUT_MATCH = { + 'access_log_format': '%(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s"', + 'accesslog': 'None', + 'backlog': '2048', + 'bind': "['127.0.0.1:8000']", + 'capture_output': 'False', + 'child_exit': '', + } + for i, line in enumerate(o.splitlines()): + m = re.match(r'^(\w+)\s+= ', line) + assert m, "Line {} didn't match expected format: {!r}".format(i, line) + + key = m.group(1) + try: + s = OUTPUT_MATCH.pop(key) + except KeyError: + continue + + line_re = r'^{}\s+= {}$'.format(key, re.escape(s)) + assert re.match(line_re, line), '{!r} != {!r}'.format(line_re, line) + + if not OUTPUT_MATCH: + break + else: + assert False, 'missing expected setting lines? {}'.format( + OUTPUT_MATCH.keys() + )