Repository: benoitc/gunicorn

Issue:
Remove paste server block support
In issues like #1073 the paste `server` block has come up. Right now, gunicorn supports some config options there.

I'm tempted to propose deprecate support for that completely so that all the gunicorn config has to happen in a gunicorn config file.

Feedback requested.



Discussion:
Just to clarify but we would keep the `--paste` option right? 

Yes.

:+1:  then :)


Resolving patch:
diff --git a/gunicorn/app/pasterapp.py b/gunicorn/app/pasterapp.py
--- a/gunicorn/app/pasterapp.py
+++ b/gunicorn/app/pasterapp.py
@@ -3,206 +3,73 @@
 # This file is part of gunicorn released under the MIT license.
 # See the NOTICE for more information.
 
-# pylint: skip-file
-
+import configparser
 import os
-import pkg_resources
-import sys
-
-try:
-    import configparser as ConfigParser
-except ImportError:
-    import ConfigParser
-
-from paste.deploy import loadapp, loadwsgi
-SERVER = loadwsgi.SERVER
-
-from gunicorn.app.base import Application
-from gunicorn.config import Config, get_default_config_file
-from gunicorn import util
-
-
-def _has_logging_config(paste_file):
-    cfg_parser = ConfigParser.ConfigParser()
-    cfg_parser.read([paste_file])
-    return cfg_parser.has_section('loggers')
-
-
-def paste_config(gconfig, config_url, relative_to, global_conf=None):
-    # add entry to pkg_resources
-    sys.path.insert(0, relative_to)
-    pkg_resources.working_set.add_entry(relative_to)
-
-    config_url = config_url.split('#')[0]
-    cx = loadwsgi.loadcontext(SERVER, config_url, relative_to=relative_to,
-                              global_conf=global_conf)
-    gc, lc = cx.global_conf.copy(), cx.local_conf.copy()
-    cfg = {}
-
-    host, port = lc.pop('host', ''), lc.pop('port', '')
-    if host and port:
-        cfg['bind'] = '%s:%s' % (host, port)
-    elif host:
-        cfg['bind'] = host.split(',')
-
-    cfg['default_proc_name'] = gc.get('__file__')
-
-    # init logging configuration
-    config_file = config_url.split(':')[1]
-    if _has_logging_config(config_file):
-        cfg.setdefault('logconfig', config_file)
-
-    for k, v in gc.items():
-        if k not in gconfig.settings:
-            continue
-        cfg[k] = v
-
-    for k, v in lc.items():
-        if k not in gconfig.settings:
-            continue
-        cfg[k] = v
-
-    return cfg
-
-
-def load_pasteapp(config_url, relative_to, global_conf=None):
-    return loadapp(config_url, relative_to=relative_to,
-            global_conf=global_conf)
-
-class PasterBaseApplication(Application):
-    gcfg = None
-
-    def app_config(self):
-        return paste_config(self.cfg, self.cfgurl, self.relpath,
-                global_conf=self.gcfg)
-
-    def load_config(self):
-        super(PasterBaseApplication, self).load_config()
-
-        # reload logging conf
-        if hasattr(self, "cfgfname"):
-            parser = ConfigParser.ConfigParser()
-            parser.read([self.cfgfname])
-            if parser.has_section('loggers'):
-                from logging.config import fileConfig
-                config_file = os.path.abspath(self.cfgfname)
-                fileConfig(config_file, dict(__file__=config_file,
-                                             here=os.path.dirname(config_file)))
 
+from paste.deploy import loadapp
 
-class PasterApplication(PasterBaseApplication):
+from gunicorn.app.wsgiapp import WSGIApplication
+from gunicorn.config import get_default_config_file
 
-    def init(self, parser, opts, args):
-        if len(args) != 1:
-            parser.error("No application name specified.")
 
-        cwd = util.getcwd()
-        cfgfname = os.path.normpath(os.path.join(cwd, args[0]))
-        cfgfname = os.path.abspath(cfgfname)
-        if not os.path.exists(cfgfname):
-            parser.error("Config file not found: %s" % cfgfname)
+def get_wsgi_app(config_uri, name=None, defaults=None):
+    if ':' not in config_uri:
+        config_uri = "config:%s" % config_uri
 
-        self.cfgurl = 'config:%s' % cfgfname
-        self.relpath = os.path.dirname(cfgfname)
-        self.cfgfname = cfgfname
+    return loadapp(
+        config_uri,
+        name=name,
+        relative_to=os.getcwd(),
+        global_conf=defaults,
+    )
 
-        sys.path.insert(0, self.relpath)
-        pkg_resources.working_set.add_entry(self.relpath)
 
-        return self.app_config()
+def has_logging_config(config_file):
+    parser = configparser.ConfigParser()
+    parser.read([config_file])
+    return parser.has_section('loggers')
 
-    def load(self):
-        # chdir to the configured path before loading,
-        # default is the current dir
-        os.chdir(self.cfg.chdir)
 
-        return load_pasteapp(self.cfgurl, self.relpath, global_conf=self.gcfg)
-
-
-class PasterServerApplication(PasterBaseApplication):
-
-    def __init__(self, app, gcfg=None, host="127.0.0.1", port=None, **kwargs):
-        # pylint: disable=super-init-not-called
-        self.cfg = Config()
-        self.gcfg = gcfg  # need to hold this for app_config
-        self.app = app
-        self.callable = None
-
-        gcfg = gcfg or {}
-        cfgfname = gcfg.get("__file__")
-        if cfgfname is not None:
-            self.cfgurl = 'config:%s' % cfgfname
-            self.relpath = os.path.dirname(cfgfname)
-            self.cfgfname = cfgfname
-
-        cfg = kwargs.copy()
-
-        if port and not host.startswith("unix:"):
-            bind = "%s:%s" % (host, port)
-        else:
-            bind = host
-        cfg["bind"] = bind.split(',')
-
-        if gcfg:
-            for k, v in gcfg.items():
-                cfg[k] = v
-            cfg["default_proc_name"] = cfg['__file__']
-
-        try:
-            for k, v in cfg.items():
-                if k.lower() in self.cfg.settings and v is not None:
-                    self.cfg.set(k.lower(), v)
-        except Exception as e:
-            print("\nConfig error: %s" % str(e), file=sys.stderr)
-            sys.stderr.flush()
-            sys.exit(1)
-
-        if cfg.get("config"):
-            self.load_config_from_file(cfg["config"])
-        else:
-            default_config = get_default_config_file()
-            if default_config is not None:
-                self.load_config_from_file(default_config)
-
-    def load(self):
-        return self.app
-
-
-def run():
+def serve(app, global_conf, **local_conf):
     """\
-    The ``gunicorn_paster`` command for launching Paster compatible
-    applications like Pylons or Turbogears2
-    """
-    util.warn("""This command is deprecated.
-
-    You should now use the `--paste` option. Ex.:
+    A Paste Deployment server runner.
 
-        gunicorn --paste development.ini
-    """)
+    Example configuration:
 
-    from gunicorn.app.pasterapp import PasterApplication
-    PasterApplication("%(prog)s [OPTIONS] pasteconfig.ini").run()
-
-
-def paste_server(app, gcfg=None, host="127.0.0.1", port=None, **kwargs):
-    """\
-    A paster server.
+        [server:main]
+        use = egg:gunicorn#main
+        host = 127.0.0.1
+        port = 5000
+    """
+    config_file = global_conf['__file__']
+    gunicorn_config_file = local_conf.pop('config', None)
 
-    Then entry point in your paster ini file should looks like this:
+    host = local_conf.pop('host', '')
+    port = local_conf.pop('port', '')
+    if host and port:
+        local_conf['bind'] = '%s:%s' % (host, port)
+    elif host:
+        local_conf['bind'] = host.split(',')
 
-    [server:main]
-    use = egg:gunicorn#main
-    host = 127.0.0.1
-    port = 5000
+    class PasterServerApplication(WSGIApplication):
+        def load_config(self):
+            self.cfg.set("default_proc_name", config_file)
 
-    """
+            if has_logging_config(config_file):
+                self.cfg.set("logconfig", config_file)
 
-    util.warn("""This command is deprecated.
+            if gunicorn_config_file:
+                self.load_config_from_file(gunicorn_config_file)
+            else:
+                default_gunicorn_config_file = get_default_config_file()
+                if default_gunicorn_config_file is not None:
+                    self.load_config_from_file(default_gunicorn_config_file)
 
-    You should now use the `--paste` option. Ex.:
+            for k, v in local_conf.items():
+                if v is not None:
+                    self.cfg.set(k.lower(), v)
 
-        gunicorn --paste development.ini
-    """)
+        def load(self):
+            return app
 
-    from gunicorn.app.pasterapp import PasterServerApplication
-    PasterServerApplication(app, gcfg=gcfg, host=host, port=port, **kwargs).run()
+    PasterServerApplication().run()
diff --git a/gunicorn/app/wsgiapp.py b/gunicorn/app/wsgiapp.py
--- a/gunicorn/app/wsgiapp.py
+++ b/gunicorn/app/wsgiapp.py
@@ -13,22 +13,21 @@
 class WSGIApplication(Application):
     def init(self, parser, opts, args):
         if opts.paste:
-            app_name = 'main'
-            path = opts.paste
-            if '#' in path:
-                path, app_name = path.split('#')
-            path = os.path.abspath(os.path.normpath(
-                os.path.join(util.getcwd(), path)))
+            from .pasterapp import has_logging_config
 
-            if not os.path.exists(path):
-                raise ConfigError("%r not found" % path)
+            config_uri = os.path.abspath(opts.paste)
+            config_file = config_uri.split('#')[0]
 
-            # paste application, load the config
-            self.cfgurl = 'config:%s#%s' % (path, app_name)
-            self.relpath = os.path.dirname(path)
+            if not os.path.exists(config_file):
+                raise ConfigError("%r not found" % config_file)
 
-            from .pasterapp import paste_config
-            return paste_config(self.cfg, self.cfgurl, self.relpath)
+            self.cfg.set("default_proc_name", config_file)
+            self.app_uri = config_uri
+
+            if has_logging_config(config_file):
+                self.cfg.set("logconfig", config_file)
+
+            return
 
         if not args:
             parser.error("No application module specified.")
@@ -37,13 +36,11 @@ def init(self, parser, opts, args):
         self.app_uri = args[0]
 
     def load_wsgiapp(self):
-        # load the app
         return util.import_app(self.app_uri)
 
     def load_pasteapp(self):
-        # load the paste app
-        from .pasterapp import load_pasteapp
-        return load_pasteapp(self.cfgurl, self.relpath, global_conf=self.cfg.paste_global_conf)
+        from .pasterapp import get_wsgi_app
+        return get_wsgi_app(self.app_uri, defaults=self.cfg.paste_global_conf)
 
     def load(self):
         if self.cfg.paste is not None:
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -104,10 +104,9 @@ def run_tests(self):
     entry_points="""
     [console_scripts]
     gunicorn=gunicorn.app.wsgiapp:run
-    gunicorn_paster=gunicorn.app.pasterapp:run
 
     [paste.server_runner]
-    main=gunicorn.app.pasterapp:paste_server
+    main=gunicorn.app.pasterapp:serve
     """,
     extras_require=extra_require,
 )


Test patch: