From 13c5d72bd198655b950d124942fbad153f613fb4 Mon Sep 17 00:00:00 2001 From: Robert Coup Date: Fri, 31 May 2019 11:49:55 +0100 Subject: [PATCH 1/3] Add --print-config option to print the resolved settings at startup. --- gunicorn/app/base.py | 5 ++++- gunicorn/config.py | 22 ++++++++++++++++++++++ tests/test_config.py | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 60 insertions(+), 1 deletion(-) diff --git a/gunicorn/app/base.py b/gunicorn/app/base.py index 470b40ab..d5c99d38 100644 --- a/gunicorn/app/base.py +++ b/gunicorn/app/base.py @@ -191,7 +191,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: diff --git a/gunicorn/config.py b/gunicorn/config.py index e8e0f926..02e06d0c 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 = "<%s()>" % v.__qualname__ + lines.append(f"{k:{kmax}} = {v}") + return "\n".join(lines) + def __getattr__(self, name): if name not in self.settings: raise AttributeError("No configuration setting for: %s" % name) @@ -936,6 +946,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 resolved configuration. + """ + + class PreloadApp(Setting): name = "preload_app" section = "Server Mechanics" diff --git a/tests/test_config.py b/tests/test_config.py index 0587c63c..67480af3 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 @@ -435,3 +436,36 @@ def test_bind_fd(): with AltArgs(["prog_name", "-b", "fd://42"]): app = NoConfigApp() assert app.cfg.bind == ["fd://42"] + + +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, f"Config line {i} didn't match expected format: {line!r}" + + key = m.group(1) + try: + s = OUTPUT_MATCH.pop(key) + except KeyError: + continue + + line_re = fr'^{key}\s+= {re.escape(s)}$' + assert re.match(line_re, line), f'{line_re!r} != {line!r}' + + if not OUTPUT_MATCH: + break + else: + assert False, f'missing expected setting lines? {list(OUTPUT_MATCH.keys())}' From 93d2687d2458e69b98c968aa193ae18ae064fff0 Mon Sep 17 00:00:00 2001 From: Robert Coup Date: Fri, 31 May 2019 12:12:39 +0100 Subject: [PATCH 2/3] f-strings only date back to Py3.6 --- gunicorn/config.py | 4 ++-- tests/test_config.py | 10 ++++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/gunicorn/config.py b/gunicorn/config.py index 02e06d0c..553962fc 100644 --- a/gunicorn/config.py +++ b/gunicorn/config.py @@ -57,8 +57,8 @@ class Config(object): for k in sorted(self.settings): v = self.settings[k].value if callable(v): - v = "<%s()>" % v.__qualname__ - lines.append(f"{k:{kmax}} = {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): diff --git a/tests/test_config.py b/tests/test_config.py index 67480af3..71a11ea1 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -454,7 +454,7 @@ def test_str(): } for i, line in enumerate(o.splitlines()): m = re.match(r'^(\w+)\s+= ', line) - assert m, f"Config line {i} didn't match expected format: {line!r}" + assert m, "Line {} didn't match expected format: {!r}".format(i, line) key = m.group(1) try: @@ -462,10 +462,12 @@ def test_str(): except KeyError: continue - line_re = fr'^{key}\s+= {re.escape(s)}$' - assert re.match(line_re, line), f'{line_re!r} != {line!r}' + 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, f'missing expected setting lines? {list(OUTPUT_MATCH.keys())}' + assert False, 'missing expected setting lines? {}'.format( + OUTPUT_MATCH.keys() + ) From 000236aae204d629fccada3527eebf7954239846 Mon Sep 17 00:00:00 2001 From: Robert Coup Date: Fri, 31 May 2019 12:22:08 +0100 Subject: [PATCH 3/3] Docs. --- docs/source/configure.rst | 4 ++-- docs/source/settings.rst | 10 ++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/docs/source/configure.rst b/docs/source/configure.rst index 9ed1a484..2af39e3b 100644 --- a/docs/source/configure.rst +++ b/docs/source/configure.rst @@ -25,10 +25,10 @@ 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 --check-config APP_MODULE + $ gunicorn --print-config APP_MODULE It also allows you to know if your application can be launched. diff --git a/docs/source/settings.rst b/docs/source/settings.rst index 16d8961a..c0c99ef4 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 -------