mirror of
https://github.com/frappe/gunicorn.git
synced 2026-01-14 11:09:11 +08:00
Major refactor of the configuration mechanics.
* All configuration and bootup is handled by the Application objects.
* There is now a strict ordering on the precedence of configuration
settings:
1. Each option is given a default value of some sort in options.ini
2. Any detectable framework configuration settings override the hard
coded defaults for options. Currently, only Paster applications
have support for this.
3. Anything that is specified in a Gunicorn configuration file (by
default gunicorn.conf.py) overrides what was possibly set by a
framework specific configuration source.
4. Anything specified on the command line reins supreme. The command
line is the final authority on a given configuration option.
Though, not all configuration options are available via command
line.
* Configuration metadata is pulled from an options.ini. In the future I'll
use this to build the example gunicorn.conf.py and the config.rst file
in docs/site/config.rst.
I haven't tested the differences thoroughly. The next item on my agenda
is to figure out a way to start testing Gunicorn that doesn't make my
eyes bleed.
This commit is contained in:
parent
5268b8fbbb
commit
036f8b50d9
@ -3,6 +3,7 @@ include LICENSE
|
||||
include NOTICE
|
||||
include README.rst
|
||||
include THANKS
|
||||
include gunicorn/options.ini
|
||||
recursive-include tests *
|
||||
recursive-include examples *
|
||||
recursive-include doc *
|
||||
|
||||
@ -1,94 +0,0 @@
|
||||
# -*- coding: utf-8 -
|
||||
#
|
||||
# This file is part of gunicorn released under the MIT license.
|
||||
# See the NOTICE for more information.
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
from gunicorn import util
|
||||
|
||||
class Application(object):
|
||||
"""\
|
||||
An application interface for configuring and loading
|
||||
the various necessities for any given web framework.
|
||||
"""
|
||||
|
||||
def get_config(self):
|
||||
return {}
|
||||
|
||||
def load(self):
|
||||
raise NotImplementedError
|
||||
|
||||
class WSGIApplication(Application):
|
||||
|
||||
def __init__(self, app_uri):
|
||||
self.app_uri = app_uri
|
||||
|
||||
def load(self):
|
||||
return util.import_app(self.app_uri)
|
||||
|
||||
class DjangoApplication(Application):
|
||||
|
||||
def __init__(self, settings_modname, project_path):
|
||||
self.project_path = project_path
|
||||
self.settings_modname = settings_modname
|
||||
|
||||
# update sys.path
|
||||
sys.path.insert(0, project_path)
|
||||
sys.path.append(os.path.join(project_path, os.pardir))
|
||||
|
||||
def load(self):
|
||||
import django.core.handlers.wsgi
|
||||
os.environ['DJANGO_SETTINGS_MODULE'] = self.settings_modname
|
||||
return django.core.handlers.wsgi.WSGIHandler()
|
||||
|
||||
class PasterApplication(Application):
|
||||
|
||||
def __init__(self, cfgurl, relpath, global_opts):
|
||||
self.cfgurl = cfgurl
|
||||
self.relpath = relpath
|
||||
self.global_opts = global_opts
|
||||
|
||||
def local_conf(self):
|
||||
from paste.deploy import loadwsgi
|
||||
ctx = loadwsgi.loadcontext(loadwsgi.SERVER, self.cfgurl,
|
||||
relative_to=self.relpath)
|
||||
|
||||
def mk_bind():
|
||||
host = ctx.local_conf.get('host')
|
||||
port = ctx.local_conf.get('port')
|
||||
if host and port:
|
||||
return '%s:%s' % (host, port)
|
||||
elif host:
|
||||
return host
|
||||
|
||||
ret = {}
|
||||
vars = {
|
||||
'bind': mk_bind,
|
||||
'workers': lambda: ctx.local_conf.get('workers', 1),
|
||||
'umask': lambda: int(ctx.local_conf.get('umask', UMASK)),
|
||||
'group': lambda: ctx.local_conf.get('group'),
|
||||
'user': lambda: ctx.local_conf.get('user')
|
||||
}
|
||||
for vn in vars:
|
||||
if self.global_ops.get(vn):
|
||||
val = vars[vn]()
|
||||
if val:
|
||||
ret[vn] = val
|
||||
|
||||
keys = ctx.local_conf.items()
|
||||
keys = filter(self.global_opts.get, keys)
|
||||
keys = filter(ret.has_key, keys)
|
||||
ret.update((k, ctx.local_conf[k]) for k in keys)
|
||||
|
||||
if not self.global_opts.get("debug"):
|
||||
ret['debug'] = (ctx.global_conf.get('debug') == "true")
|
||||
|
||||
ret['default_proc_name'] = ctx.global_conf.get('__file__')
|
||||
|
||||
return ret
|
||||
|
||||
def load(self):
|
||||
from paste.deploy import loadapp
|
||||
return loadapp(self.cfgurl, relative_to=self.relpath)
|
||||
0
gunicorn/app/__init__.py
Normal file
0
gunicorn/app/__init__.py
Normal file
100
gunicorn/app/base.py
Normal file
100
gunicorn/app/base.py
Normal file
@ -0,0 +1,100 @@
|
||||
# -*- coding: utf-8 -
|
||||
#
|
||||
# This file is part of gunicorn released under the MIT license.
|
||||
# See the NOTICE for more information.
|
||||
|
||||
import logging
|
||||
import optparse
|
||||
import os
|
||||
|
||||
from gunicorn import __version__
|
||||
from gunicorn import util
|
||||
from gunicorn.arbiter import Arbiter
|
||||
from gunicorn.config import Config
|
||||
from gunicorn.debug import spew
|
||||
|
||||
class Application(object):
|
||||
"""\
|
||||
An application interface for configuring and loading
|
||||
the various necessities for any given web framework.
|
||||
"""
|
||||
def __init__(self, usage):
|
||||
self.log = logging.getLogger(__name__)
|
||||
self.cfg = Config(usage)
|
||||
|
||||
parser = self.cfg.parser()
|
||||
opts, args = parser.parse_args()
|
||||
cfg = self.init(parser, opts, args)
|
||||
|
||||
# Load up the any app specific configuration
|
||||
if cfg:
|
||||
for k, v in cfg:
|
||||
self.cfg.set(k.lower(), v)
|
||||
|
||||
# Load up the config file if its found.
|
||||
if opts.config and os.path.exists(opts.config):
|
||||
cfg = globals().copy()
|
||||
try:
|
||||
execfile(opts.config, cfg, cfg)
|
||||
except Exception, e:
|
||||
print "Failed to read config file: %s" % opts.config
|
||||
traceback.print_exc()
|
||||
sys.exit(1)
|
||||
|
||||
for k, v in cfg.iteritems():
|
||||
self.cfg.set(k.lower(), v)
|
||||
|
||||
# Lastly, update the configuration with any command line
|
||||
# settings.
|
||||
for k, v in opts.__dict__.iteritems():
|
||||
if v is None or self.cfg.modified(k.lower()):
|
||||
continue
|
||||
self.cfg.set(k.lower(), v)
|
||||
|
||||
self.configure_logging()
|
||||
|
||||
def init(self, parser, opts, args):
|
||||
raise NotImplementedError
|
||||
|
||||
def load(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def run(self):
|
||||
if self.cfg.spew:
|
||||
debug.spew()
|
||||
if self.cfg.daemon:
|
||||
util.daemonize()
|
||||
else:
|
||||
os.setpgrp()
|
||||
Arbiter(self).run()
|
||||
|
||||
def configure_logging(self):
|
||||
"""\
|
||||
Set the log level and choose the destination for log output.
|
||||
"""
|
||||
logger = logging.getLogger('gunicorn')
|
||||
|
||||
handlers = []
|
||||
if self.cfg.logfile != "-":
|
||||
handlers.append(logging.FileHandler(self.cfg.logfile))
|
||||
else:
|
||||
handlers.append(logging.StreamHandler())
|
||||
|
||||
levels = {
|
||||
"critical": logging.CRITICAL,
|
||||
"error": logging.ERROR,
|
||||
"warning": logging.WARNING,
|
||||
"info": logging.INFO,
|
||||
"debug": logging.DEBUG
|
||||
}
|
||||
|
||||
loglevel = levels.get(self.cfg.loglevel.lower(), logging.INFO)
|
||||
logger.setLevel(loglevel)
|
||||
|
||||
format = r"%(asctime)s [%(process)d] [%(levelname)s] %(message)s"
|
||||
datefmt = r"%Y-%m-%d %H:%M:%S"
|
||||
for h in handlers:
|
||||
h.setFormatter(logging.Formatter(format, datefmt))
|
||||
logger.addHandler(h)
|
||||
|
||||
|
||||
46
gunicorn/app/djangoapp.py
Normal file
46
gunicorn/app/djangoapp.py
Normal file
@ -0,0 +1,46 @@
|
||||
# -*- coding: utf-8 -
|
||||
#
|
||||
# This file is part of gunicorn released under the MIT license.
|
||||
# See the NOTICE for more information.
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
import django.core.handlers.wsgi
|
||||
|
||||
from gunicorn import util
|
||||
from gunicorn.app.base import Application
|
||||
|
||||
class DjangoApplication(Application):
|
||||
|
||||
def init(parser, opts, args):
|
||||
self.project_path = os.getcwd()
|
||||
|
||||
if args:
|
||||
settings_path = os.path.abspath(os.path.normpath(args[0]))
|
||||
if not os.path.exists(settings_path):
|
||||
self.no_settings(settings_path)
|
||||
else:
|
||||
self.project_path = os.path.dirname(settings_path)
|
||||
else:
|
||||
settings_path = os.path.join(project_path, "settings.py")
|
||||
if not os.path.exists(settings_path):
|
||||
self.no_settings(settings_path)
|
||||
|
||||
project_name = os.path.split(project_path)[-1]
|
||||
settings_name, ext = os.path.splitext(os.path.basename(settings_path))
|
||||
settings_modname = "%s.%s" % (project_name, settings_name)
|
||||
self.cfg.default_proc_name = settings_modname
|
||||
|
||||
sys.path.insert(0, self.project_path)
|
||||
sys.path.append(os.path.join(project_path, os.pardir))
|
||||
|
||||
def no_settings(self, path):
|
||||
error = "Settings file '%s' not found in current folder.\n" % path
|
||||
sys.stderr.write(error)
|
||||
sys.stderr.flush()
|
||||
sys.exit(1)
|
||||
|
||||
def load(self):
|
||||
os.environ['DJANGO_SETTINGS_MODULE'] = self.settings_modname
|
||||
return django.core.handlers.wsgi.WSGIHandler()
|
||||
78
gunicorn/app/pasterapp.py
Normal file
78
gunicorn/app/pasterapp.py
Normal file
@ -0,0 +1,78 @@
|
||||
# -*- coding: utf-8 -
|
||||
#
|
||||
# This file is part of gunicorn released under the MIT license.
|
||||
# See the NOTICE for more information.
|
||||
|
||||
import os
|
||||
import pkg_resources
|
||||
import sys
|
||||
|
||||
from paste.deploy import loadapp, loadwsgi
|
||||
SERVER = loadwsgi.SERVER
|
||||
|
||||
class PasterApplication(Application):
|
||||
|
||||
def init(self, parser, opts, args):
|
||||
if len(args) != 1:
|
||||
parser.error("No application name specified.")
|
||||
|
||||
cfgfname = os.path.normpath(os.path.join(os.getcwd(), args[0]))
|
||||
cfgfname = os.path.abspath(cfgfname)
|
||||
if not os.path.exists(cfgfname):
|
||||
parser.error("Config file not found.")
|
||||
|
||||
self.cfgurl = 'config:%s' % cfgfname
|
||||
self.relpath = os.path.dirname(cfgfname)
|
||||
|
||||
sys.path.insert(0, self.relpath)
|
||||
pkg_resources.working_set.add_entry(self.relpath)
|
||||
|
||||
return self.app_config()
|
||||
|
||||
def app_config(self):
|
||||
cx = loadwsgi.loadcontext(SERVER, self.cfgurl, relative_to=self.relpath)
|
||||
gc, lc = cx.global_conf, cx.local_conf
|
||||
|
||||
cfg = {}
|
||||
|
||||
host, port = lc.get('host'), lc.get('port')
|
||||
if host and port:
|
||||
cfg['bind'] = '%s:%s' % (host, port)
|
||||
elif host:
|
||||
cfg['bind'] = host
|
||||
|
||||
cfg['workers'] = int(lc.get('workers', 1))
|
||||
cfg['umask'] = int(lc.get('umask', 0))
|
||||
cfg['default_proc_name'] = gc.get('__file__')
|
||||
cfg.update(dict((k,v) for (k,v) in lc.items() if k not in cfg))
|
||||
return cfg
|
||||
|
||||
def load(self):
|
||||
return loadapp(self.cfgurl, relative_to=self.relpath)
|
||||
|
||||
class PasterServerApplication(Application):
|
||||
|
||||
def __init__(self, app, *args, **kwargs):
|
||||
self.log = logging.getLogger(__name__)
|
||||
self.cfg = Config()
|
||||
self.app = app
|
||||
|
||||
cfg = {}
|
||||
host, port = kwargs.get('host'), kwargs.get('port')
|
||||
if host and port:
|
||||
cfg['bind'] = '%s:%s' % (host, port)
|
||||
elif host:
|
||||
cfg['bind'] = host
|
||||
|
||||
if gcfg:
|
||||
for k, v in list(gcfg.items()):
|
||||
if k.lower() in self.cfg.settings:
|
||||
self.cfg.set(k.lower(), v)
|
||||
self.cfg.default_proc_name = kwargs.__file__
|
||||
|
||||
self.configure_logging(cfg)
|
||||
|
||||
def load(self):
|
||||
return self.app
|
||||
|
||||
|
||||
31
gunicorn/app/wsgiapp.py
Normal file
31
gunicorn/app/wsgiapp.py
Normal file
@ -0,0 +1,31 @@
|
||||
# -*- coding: utf-8 -
|
||||
#
|
||||
# This file is part of gunicorn released under the MIT license.
|
||||
# See the NOTICE for more information.
|
||||
|
||||
import os
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
from gunicorn import util
|
||||
from gunicorn.app.base import Application
|
||||
|
||||
class WSGIApplication(Application):
|
||||
|
||||
def init(self, parser, opts, args):
|
||||
if len(args) != 1:
|
||||
parser.error("No application module specified.")
|
||||
|
||||
self.cfg.set("default_proc_name", args[0])
|
||||
self.app_uri = args[0]
|
||||
|
||||
sys.path.insert(0, os.getcwd())
|
||||
try:
|
||||
self.load()
|
||||
except:
|
||||
print "Failed to import application: %s" % self.app_uri
|
||||
traceback.print_exc()
|
||||
sys.exit(1)
|
||||
|
||||
def load(self):
|
||||
return util.import_app(self.app_uri)
|
||||
@ -44,24 +44,19 @@ class Arbiter(object):
|
||||
if name[:3] == "SIG" and name[3] != "_"
|
||||
)
|
||||
|
||||
def __init__(self, cfg, app):
|
||||
self.cfg = cfg
|
||||
self.app = app
|
||||
def __init__(self, app):
|
||||
self.cfg = app.cfg
|
||||
self.app = app.load()
|
||||
|
||||
self.log = logging.getLogger(__name__)
|
||||
|
||||
self.address = cfg.address
|
||||
self.num_workers = cfg.workers
|
||||
self.debug = cfg.debug
|
||||
self.timeout = cfg.timeout
|
||||
self.proc_name = cfg.proc_name
|
||||
|
||||
try:
|
||||
self.worker_class = cfg.worker_class
|
||||
except ImportError, e:
|
||||
self.log.error("%s" % e)
|
||||
sys.exit(1)
|
||||
|
||||
self.address = self.cfg.address
|
||||
self.num_workers = self.cfg.workers
|
||||
self.debug = self.cfg.debug
|
||||
self.timeout = self.cfg.timeout
|
||||
self.proc_name = self.cfg.proc_name
|
||||
self.worker_class = self.cfg.worker_class
|
||||
|
||||
self.pidfile = None
|
||||
self.worker_age = 0
|
||||
self.reexec_pid = 0
|
||||
@ -301,7 +296,7 @@ class Arbiter(object):
|
||||
|
||||
os.environ['GUNICORN_FD'] = str(self.LISTENER.fileno())
|
||||
os.chdir(self.START_CTX['cwd'])
|
||||
self.cfg.before_exec(self)
|
||||
self.cfg.pre_exec(self)
|
||||
os.execvpe(self.START_CTX[0], self.START_CTX['args'], os.environ)
|
||||
|
||||
def murder_workers(self):
|
||||
@ -366,7 +361,7 @@ class Arbiter(object):
|
||||
self.worker_age += 1
|
||||
worker = self.worker_class(self.worker_age, self.pid, self.LISTENER,
|
||||
self.app, self.timeout/2.0, self.cfg)
|
||||
self.cfg.before_fork(self, worker)
|
||||
self.cfg.pre_fork(self, worker)
|
||||
pid = os.fork()
|
||||
if pid != 0:
|
||||
self.WORKERS[pid] = worker
|
||||
@ -378,7 +373,7 @@ class Arbiter(object):
|
||||
util._setproctitle("worker [%s]" % self.proc_name)
|
||||
self.log.debug("Booting worker: %s (age: %s)" % (
|
||||
worker_pid, self.worker_age))
|
||||
self.cfg.after_fork(self, worker)
|
||||
self.cfg.post_fork(self, worker)
|
||||
worker.init_process()
|
||||
sys.exit(0)
|
||||
except SystemExit:
|
||||
|
||||
@ -3,99 +3,153 @@
|
||||
# This file is part of gunicorn released under the MIT license.
|
||||
# See the NOTICE for more information.
|
||||
|
||||
import ConfigParser
|
||||
import grp
|
||||
import inspect
|
||||
import optparse
|
||||
import os
|
||||
import pwd
|
||||
import sys
|
||||
import types
|
||||
|
||||
from gunicorn import __version__
|
||||
from gunicorn import util
|
||||
|
||||
class Setting(object):
|
||||
def __init__(self, name, opts):
|
||||
self.name = name
|
||||
self.section = opts["section"]
|
||||
self.order = int(opts.get("order", 0))
|
||||
self.cli = opts["cli"].split()
|
||||
self.type = opts["type"].strip()
|
||||
self.arity = opts.get('arity', None)
|
||||
if self.arity:
|
||||
self.arity = int(self.arity)
|
||||
self.meta = opts.get('meta', "").strip() or None
|
||||
self.action = opts.get('action', "store").strip()
|
||||
self.default = opts.get('default').strip() or None
|
||||
self.desc = opts["desc"]
|
||||
self.short = self.desc.splitlines()[0].strip()
|
||||
|
||||
# Special case the callable types.
|
||||
self.value = None
|
||||
if self.default and self.type != 'callable':
|
||||
self.set(self.default, modified=False)
|
||||
|
||||
# A flag that tells us if this setting
|
||||
# has been altered
|
||||
self.modified = False
|
||||
|
||||
def add_option(self, parser):
|
||||
if not len(self.cli):
|
||||
return
|
||||
args = tuple(self.cli)
|
||||
opttypes = {
|
||||
"pos_int": "int",
|
||||
"bool": None
|
||||
}
|
||||
kwargs = {
|
||||
"dest": self.name,
|
||||
"metavar": self.meta or None,
|
||||
"action": self.action,
|
||||
"type": opttypes.get(self.type, "string"),
|
||||
"default": None,
|
||||
"help": "%s [%s]" % (self.short, self.default)
|
||||
}
|
||||
parser.add_option(*args, **kwargs)
|
||||
|
||||
def get(self):
|
||||
return self.value
|
||||
|
||||
def set(self, val, modified=True):
|
||||
validator = getattr(self, "set_%s" % self.type)
|
||||
self.value = validator(val)
|
||||
self.modified = modified
|
||||
|
||||
def set_bool(self, val):
|
||||
if isinstance(val, types.BooleanType):
|
||||
return val
|
||||
if val.lower().strip() == "true":
|
||||
return True
|
||||
elif val.lower().strip() == "false":
|
||||
return False
|
||||
else:
|
||||
raise ValueError("Invalid boolean: %s" % val)
|
||||
|
||||
def set_pos_int(self, val):
|
||||
if not isinstance(val, (types.IntType, types.LongType)):
|
||||
val = int(val, 0)
|
||||
if val < 0:
|
||||
raise ValueError("Value must be positive: %s" % val)
|
||||
return val
|
||||
|
||||
def set_string(self, val):
|
||||
return val.strip()
|
||||
|
||||
def set_callable(self, val):
|
||||
if not callable(val):
|
||||
raise TypeError("Value is not callable: %s" % val)
|
||||
arity = len(inspect.getargspec(val)[0])
|
||||
if arity != self.arity:
|
||||
raise TypeError("Value must have an arity of: %s" % self.arity)
|
||||
return val
|
||||
|
||||
class Config(object):
|
||||
|
||||
DEFAULT_CONFIG_FILE = 'gunicorn.conf.py'
|
||||
|
||||
DEFAULTS = dict(
|
||||
backlog=2048,
|
||||
bind='127.0.0.1:8000',
|
||||
daemon=False,
|
||||
debug=False,
|
||||
default_proc_name = os.getcwd(),
|
||||
group=None,
|
||||
keepalive=2,
|
||||
logfile='-',
|
||||
loglevel='info',
|
||||
pidfile=None,
|
||||
proc_name = None,
|
||||
spew=False,
|
||||
timeout=30,
|
||||
tmp_upload_dir=None,
|
||||
umask="0",
|
||||
user=None,
|
||||
workers=1,
|
||||
worker_connections=1000,
|
||||
worker_class="egg:gunicorn#sync",
|
||||
|
||||
after_fork=lambda server, worker: server.log.info(
|
||||
"Worker spawned (pid: %s)" % worker.pid),
|
||||
def __init__(self, usage):
|
||||
self.settings = {}
|
||||
self.usage = usage
|
||||
|
||||
path = os.path.join(os.path.dirname(__file__), "options.ini")
|
||||
opts = ConfigParser.SafeConfigParser()
|
||||
if not len(opts.read(path)):
|
||||
raise RuntimeError("Options configuration file is missing!")
|
||||
|
||||
before_fork=lambda server, worker: True,
|
||||
for sect in opts.sections():
|
||||
self.settings[sect] = Setting(sect, dict(opts.items(sect)))
|
||||
|
||||
before_exec=lambda server: server.log.info("Forked child, reexecuting")
|
||||
)
|
||||
|
||||
def __init__(self, opts, path=None):
|
||||
self.cfg = {}
|
||||
self.opts = opts
|
||||
if path is None:
|
||||
path = os.path.join(os.getcwd(), self.DEFAULT_CONFIG_FILE)
|
||||
self.path = path
|
||||
self.load()
|
||||
|
||||
def update(self, opts):
|
||||
opts = dict((k, v) for (k, v) in opts.iteritems() if v is not None)
|
||||
self.opts.update(opts)
|
||||
self.cfg.update(opts)
|
||||
|
||||
def load(self):
|
||||
self.cfg = self.DEFAULTS.copy()
|
||||
|
||||
if os.path.exists(self.path):
|
||||
try:
|
||||
execfile(self.path, globals(), self.cfg)
|
||||
except Exception, e:
|
||||
sys.exit("Could not read config file: %r\n %s" % (self.path, e))
|
||||
self.cfg.pop("__builtins__", None)
|
||||
|
||||
opts = dict((k, v) for (k, v) in opts.iteritems() if v is not None)
|
||||
self.cfg.update(opts)
|
||||
|
||||
def __getitem__(self, key):
|
||||
try:
|
||||
return getattr(self, key)
|
||||
except AttributeError:
|
||||
pass
|
||||
return self.cfg[key]
|
||||
# Special case hook functions
|
||||
self.settings['pre_fork'].set(def_pre_fork, modified=False)
|
||||
self.settings['post_fork'].set(def_post_fork, modified=False)
|
||||
self.settings['pre_exec'].set(def_pre_exec, modified=False)
|
||||
|
||||
def __getattr__(self, key):
|
||||
try:
|
||||
super(Config, self).__getattribute__(key)
|
||||
except AttributeError:
|
||||
if key in self.cfg:
|
||||
return self.cfg[key]
|
||||
raise
|
||||
|
||||
def __contains__(self, key):
|
||||
return (key in self.cfg)
|
||||
|
||||
def __iter__(self):
|
||||
return self.cfg.iteritems()
|
||||
def __getattr__(self, name):
|
||||
if name not in self.settings:
|
||||
raise AttributeError("No configuration setting for: %s" % name)
|
||||
return self.settings[name].get()
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
if name != "settings" and name in self.settings:
|
||||
raise AttributeError("Invalid access!")
|
||||
super(Config, self).__setattr__(name, value)
|
||||
|
||||
def set(self, name, value):
|
||||
if name not in self.settings:
|
||||
raise AttributeError("No configuration setting for: %s" % name)
|
||||
self.settings[name].set(value)
|
||||
|
||||
def get(self, key, default=None):
|
||||
return self.cfg.get(key, default)
|
||||
def parser(self):
|
||||
kwargs = {
|
||||
"usage": self.usage,
|
||||
"version": __version__,
|
||||
"formatter": HelpFormatter()
|
||||
}
|
||||
parser = optparse.OptionParser(**kwargs)
|
||||
|
||||
keys = self.settings.keys()
|
||||
def sorter(k):
|
||||
return (self.settings[k].section, self.settings[k].order)
|
||||
keys.sort(key=sorter)
|
||||
for k in keys:
|
||||
self.settings[k].add_option(parser)
|
||||
return parser
|
||||
|
||||
def was_modified(self, name):
|
||||
return self.settings[name].modified
|
||||
|
||||
@property
|
||||
def worker_class(self):
|
||||
uri = self.cfg.get('worker_class', None) or 'egg:gunicorn#sync'
|
||||
uri = self.settings['worker_class'].get()
|
||||
worker_class = util.load_worker_class(uri)
|
||||
if hasattr(worker_class, "setup"):
|
||||
worker_class.setup()
|
||||
@ -103,72 +157,69 @@ class Config(object):
|
||||
|
||||
@property
|
||||
def workers(self):
|
||||
if not self.cfg.get('workers'):
|
||||
raise RuntimeError("invalid workers number")
|
||||
workers = int(self.cfg["workers"])
|
||||
if not workers:
|
||||
raise RuntimeError("number of workers < 1")
|
||||
if self.cfg['debug'] == True:
|
||||
workers = 1
|
||||
return workers
|
||||
if self.settings['debug'].get():
|
||||
return 1
|
||||
return self.settings['workers'].get()
|
||||
|
||||
@property
|
||||
def address(self):
|
||||
if not self.cfg['bind']:
|
||||
raise RuntimeError("Listener address is not set")
|
||||
return util.parse_address(util.to_bytestring(self.cfg['bind']))
|
||||
|
||||
@property
|
||||
def umask(self):
|
||||
if not self.cfg.get('umask'):
|
||||
return 0
|
||||
umask = self.cfg['umask']
|
||||
if isinstance(umask, basestring):
|
||||
return int(umask, 0)
|
||||
return umask
|
||||
bind = self.settings['bind'].get()
|
||||
return util.parse_address(util.to_bytestring(bind))
|
||||
|
||||
@property
|
||||
def uid(self):
|
||||
if not self.cfg.get('user'):
|
||||
return os.geteuid()
|
||||
user = self.settings['user'].get()
|
||||
|
||||
user = self.cfg.get('user')
|
||||
if user.isdigit() or isinstance(user, int):
|
||||
uid = int(user)
|
||||
if not user:
|
||||
return os.geteuid()
|
||||
elif user.isdigit() or isinstance(user, int):
|
||||
return int(user)
|
||||
else:
|
||||
uid = pwd.getpwnam(user).pw_uid
|
||||
return uid
|
||||
return pwd.getpwnam(user).pw_uid
|
||||
|
||||
@property
|
||||
def gid(self):
|
||||
if not self.cfg.get('group'):
|
||||
group = self.settings['group'].get()
|
||||
|
||||
if not group:
|
||||
return os.getegid()
|
||||
group = self.cfg.get('group')
|
||||
if group.isdigit() or isinstance(group, int):
|
||||
gid = int(group)
|
||||
elif group.isdigit() or isinstance(user, int):
|
||||
return int(group)
|
||||
else:
|
||||
gid = grp.getgrnam(group).gr_gid
|
||||
return gid
|
||||
return grp.getgrnam(group).gr_gid
|
||||
|
||||
@property
|
||||
def proc_name(self):
|
||||
if not self.cfg.get('proc_name'):
|
||||
return self.cfg.get('default_proc_name')
|
||||
return self.cfg.get('proc_name')
|
||||
|
||||
def after_fork(self, *args):
|
||||
return self._hook("after_fork", *args)
|
||||
|
||||
def before_fork(self, *args):
|
||||
return self._hook("before_fork", *args)
|
||||
|
||||
def before_exec(self, *args):
|
||||
return self._hook("before_exec", *args)
|
||||
pn = self.settings['proc_name'].get()
|
||||
if pn:
|
||||
return pn
|
||||
else:
|
||||
return self.settings['default_proc_name']
|
||||
|
||||
@property
|
||||
def pre_fork(self):
|
||||
return self.settings['pre_fork'].get()
|
||||
|
||||
@property
|
||||
def post_fork(self):
|
||||
return self.settings['post_fork'].get()
|
||||
|
||||
@property
|
||||
def pre_exec(self):
|
||||
return self.settings['pre_exec'].get()
|
||||
|
||||
|
||||
def def_pre_fork(server, worker):
|
||||
pass
|
||||
|
||||
def def_post_fork(server, worker):
|
||||
server.log.info("Worker spawned (pid: %s)" % worker.pid)
|
||||
|
||||
def def_pre_exec(server):
|
||||
server.log.info("Forked child, reexecuting.")
|
||||
|
||||
|
||||
class HelpFormatter(optparse.IndentedHelpFormatter):
|
||||
pass
|
||||
|
||||
|
||||
def _hook(self, hookname, *args):
|
||||
hook = self.cfg.get(hookname)
|
||||
if not hook:
|
||||
return
|
||||
if not callable(hook):
|
||||
raise RuntimeError("%r hook isn't a callable" % hookname)
|
||||
return hook(*args)
|
||||
|
||||
@ -45,9 +45,8 @@ class Request(object):
|
||||
"SERVER_SOFTWARE": "gunicorn/%s" % __version__
|
||||
}
|
||||
|
||||
def __init__(self, socket, client_address, server_address, conf):
|
||||
self.debug = conf['debug']
|
||||
self.conf = conf
|
||||
def __init__(self, cfg, socket, client_address, server_address):
|
||||
self.cfg = cfg
|
||||
self.socket = socket
|
||||
|
||||
self.client_address = client_address
|
||||
@ -85,18 +84,17 @@ class Request(object):
|
||||
self.socket.send("HTTP/1.1 100 Continue\r\n\r\n")
|
||||
|
||||
if not self.parser.content_len and not self.parser.is_chunked:
|
||||
wsgi_input = TeeInput(self.socket, self.parser, StringIO(),
|
||||
self.conf)
|
||||
wsgi_input = TeeInput(self.cfg, self.socket, self.parser, StringIO())
|
||||
content_length = "0"
|
||||
else:
|
||||
wsgi_input = TeeInput(self.socket, self.parser, buf2, self.conf)
|
||||
wsgi_input = TeeInput(self.cfg, self.socket, self.parser, buf2)
|
||||
content_length = str(wsgi_input.len)
|
||||
|
||||
# This value should evaluate true if an equivalent application
|
||||
# object may be simultaneously invoked by another process, and
|
||||
# should evaluate false otherwise. In debug mode we fall to one
|
||||
# worker so we comply to pylons and other paster app.
|
||||
wsgi_multiprocess = (self.debug == False)
|
||||
wsgi_multiprocess = self.cfg.workers > 1
|
||||
|
||||
# authors should be aware that REMOTE_HOST and REMOTE_ADDR
|
||||
# may not qualify the remote addr:
|
||||
|
||||
@ -26,8 +26,8 @@ class TeeInput(object):
|
||||
|
||||
CHUNK_SIZE = util.CHUNK_SIZE
|
||||
|
||||
def __init__(self, socket, parser, buf, conf):
|
||||
self.conf = conf
|
||||
def __init__(self, cfg, socket, parser, buf):
|
||||
self.cfg = cfg
|
||||
self.buf = StringIO()
|
||||
self.parser = parser
|
||||
self._sock = socket
|
||||
@ -39,7 +39,7 @@ class TeeInput(object):
|
||||
elif self._len and self._len < util.MAX_BODY:
|
||||
self.tmp = StringIO()
|
||||
else:
|
||||
self.tmp = tempfile.TemporaryFile(dir=self.conf['tmp_upload_dir'])
|
||||
self.tmp = tempfile.TemporaryFile(dir=self.cfg['tmp_upload_dir'])
|
||||
|
||||
if len(buf.getvalue()) > 0:
|
||||
chunk, self.buf = parser.filter_body(buf)
|
||||
|
||||
234
gunicorn/main.py
234
gunicorn/main.py
@ -2,176 +2,32 @@
|
||||
#
|
||||
# This file is part of gunicorn released under the MIT license.
|
||||
# See the NOTICE for more information.
|
||||
|
||||
import logging
|
||||
import optparse as op
|
||||
import os
|
||||
import pkg_resources
|
||||
import sys
|
||||
|
||||
from gunicorn.arbiter import Arbiter
|
||||
from gunicorn.config import Config
|
||||
from gunicorn.debug import spew
|
||||
from gunicorn import util, __version__
|
||||
|
||||
LOG_LEVELS = {
|
||||
"critical": logging.CRITICAL,
|
||||
"error": logging.ERROR,
|
||||
"warning": logging.WARNING,
|
||||
"info": logging.INFO,
|
||||
"debug": logging.DEBUG
|
||||
}
|
||||
|
||||
UMASK = "0"
|
||||
|
||||
def options():
|
||||
""" build command lines options passed to OptParse object """
|
||||
return [
|
||||
op.make_option('-c', '--config', dest='config', type='string',
|
||||
help='Config file. [%default]'),
|
||||
op.make_option('-b', '--bind', dest='bind',
|
||||
help='Adress to listen on. Ex. 127.0.0.1:8000 or unix:/tmp/gunicorn.sock'),
|
||||
op.make_option('-w', '--workers', dest='workers',
|
||||
help='Number of workers to spawn. [1]'),
|
||||
op.make_option('-k', '--worker-class', dest='worker_class',
|
||||
help="The type of request processing to use "+
|
||||
"[egg:gunicorn#sync]"),
|
||||
op.make_option('-p','--pid', dest='pidfile',
|
||||
help='set the background PID FILE'),
|
||||
op.make_option('-D', '--daemon', dest='daemon', action="store_true",
|
||||
help='Run daemonized in the background.'),
|
||||
op.make_option('-m', '--umask', dest="umask", type='string',
|
||||
help="Define umask of daemon process"),
|
||||
op.make_option('-u', '--user', dest="user",
|
||||
help="Change worker user"),
|
||||
op.make_option('-g', '--group', dest="group",
|
||||
help="Change worker group"),
|
||||
op.make_option('-n', '--name', dest='proc_name',
|
||||
help="Process name"),
|
||||
op.make_option('--log-level', dest='loglevel',
|
||||
help='Log level below which to silence messages. [info]'),
|
||||
op.make_option('--log-file', dest='logfile',
|
||||
help='Log to a file. - equals stdout. [-]'),
|
||||
op.make_option('-d', '--debug', dest='debug', action="store_true",
|
||||
default=False, help='Debug mode. only 1 worker.'),
|
||||
op.make_option('--spew', dest='spew', action="store_true",
|
||||
default=False, help="Install a trace hook")
|
||||
]
|
||||
|
||||
def main(usage, get_app):
|
||||
"""\
|
||||
Used by the various runners to setup options and
|
||||
launch the arbiter.
|
||||
"""
|
||||
vrs = "%prog " + __version__
|
||||
parser = op.OptionParser(usage=usage, option_list=options(), version=vrs)
|
||||
opts, args = parser.parse_args()
|
||||
|
||||
cfg = Config(opts.__dict__, opts.config)
|
||||
app = get_app(parser, opts, args)
|
||||
cfg.update(app.get_config())
|
||||
if cfg.spew:
|
||||
spew()
|
||||
if cfg.daemon:
|
||||
daemonize()
|
||||
else:
|
||||
os.setpgrp()
|
||||
configure_logging(cfg)
|
||||
|
||||
Arbiter(cfg, app.load()).run()
|
||||
|
||||
def run():
|
||||
"""\
|
||||
The ``gunicorn`` command line runner for launcing Gunicorn with
|
||||
generic WSGI applications.
|
||||
"""
|
||||
sys.path.insert(0, os.getcwd())
|
||||
|
||||
def get_app(parser, opts, args):
|
||||
if len(args) != 1:
|
||||
parser.error("No application module specified.")
|
||||
opts.default_proc_name = args[0]
|
||||
|
||||
application = app.WSGIApplication(args[0])
|
||||
try:
|
||||
application.load()
|
||||
except Exception, e:
|
||||
parser.error("Failed to import application module:\n %s" % e)
|
||||
return application
|
||||
|
||||
main("%prog [OPTIONS] APP_MODULE", get_app)
|
||||
"""
|
||||
from gunicorn.app.wsgiapp import WSGIApplication
|
||||
WSGIApplication("%prog [OPTIONS] APP_MODULE").run()
|
||||
|
||||
def run_django():
|
||||
"""\
|
||||
The ``gunicorn_django`` command line runner for launching Django
|
||||
applications.
|
||||
"""
|
||||
|
||||
def settings_notfound(path):
|
||||
error = "Settings file '%s' not found in current folder.\n" % path
|
||||
sys.stderr.write(error)
|
||||
sys.stderr.flush()
|
||||
sys.exit(1)
|
||||
|
||||
def get_app(parser, opts, args):
|
||||
import django.core.handlers.wsgi
|
||||
|
||||
project_path = os.getcwd()
|
||||
|
||||
if args:
|
||||
settings_path = os.path.abspath(os.path.normpath(args[0]))
|
||||
if not os.path.exists(settings_path):
|
||||
settings_notfound(settings_path)
|
||||
else:
|
||||
project_path = os.path.dirname(settings_path)
|
||||
else:
|
||||
settings_path = os.path.join(project_path, "settings.py")
|
||||
if not os.path.exists(settings_path):
|
||||
settings_notfound(settings_path)
|
||||
|
||||
# set environ
|
||||
project_name = os.path.split(project_path)[-1]
|
||||
settings_name, ext = os.path.splitext(os.path.basename(settings_path))
|
||||
settings_modname = "%s.%s" % (project_name, settings_name)
|
||||
opts.default_proc_name = settings_modname
|
||||
|
||||
# django wsgi app
|
||||
return app.DjangoApplication(settings_modname, project_path)
|
||||
|
||||
|
||||
|
||||
main("%prog [OPTIONS] [SETTINGS_PATH]", get_app)
|
||||
from gunicorn.app.djangoapp import DjangoApplication
|
||||
DjangoApplication("%prog [OPTIONS] [SETTINGS_PATH]").run()
|
||||
|
||||
def run_paster():
|
||||
"""\
|
||||
The ``gunicorn_paster`` command for launcing Paster compatible
|
||||
apllications like Pylons or Turbogears2
|
||||
"""
|
||||
from paste.deploy import loadwsgi
|
||||
from gunicorn.app.pasterapp import PasterApplication
|
||||
PasterApplication("%prog [OPTIONS] pasteconfig.ini").run()
|
||||
|
||||
def get_app(parser, opts, args):
|
||||
if len(args) != 1:
|
||||
parser.error("No application name specified.")
|
||||
|
||||
cfgfname = os.path.normpath(os.path.join(os.getcwd(), args[0]))
|
||||
cfgfname = os.path.abspath(cfgfname)
|
||||
if not os.path.exists(cfgfname):
|
||||
parser.error("Config file not found.")
|
||||
|
||||
cfgurl = 'config:%s' % cfgfname
|
||||
relpath = os.path.dirname(cfgfname)
|
||||
|
||||
# load module in sys path
|
||||
sys.path.insert(0, relpath)
|
||||
|
||||
# add to eggs
|
||||
pkg_resources.working_set.add_entry(relpath)
|
||||
|
||||
return app.PasterApplication(cfgurl, relpath, opts.__dict__)
|
||||
|
||||
main("%prog [OPTIONS] pasteconfig.ini", get_app)
|
||||
|
||||
def paste_server(app, gcfg=None, host="127.0.0.1", port=None, *args, **kwargs):
|
||||
def paste_server(*args, **kwargs):
|
||||
"""\
|
||||
A paster server.
|
||||
|
||||
@ -183,77 +39,9 @@ def paste_server(app, gcfg=None, host="127.0.0.1", port=None, *args, **kwargs):
|
||||
port = 5000
|
||||
|
||||
"""
|
||||
opts = kwargs.copy()
|
||||
if port and not host.startswith("unix:"):
|
||||
bind = "%s:%s" % (host, port)
|
||||
else:
|
||||
bind = host
|
||||
opts['bind'] = bind
|
||||
from gunicorn.app.pasterapp import PasterServerApplication
|
||||
PasterServerApplication(app, *args, **kwargs).run()
|
||||
|
||||
if gcfg:
|
||||
for key, value in list(gcfg.items()):
|
||||
if value and value is not None:
|
||||
if key == "debug":
|
||||
value = (value == "true")
|
||||
opts[key] = value
|
||||
opts['default_proc_name'] = opts['__file__']
|
||||
|
||||
cfg = Config(opts)
|
||||
|
||||
if cfg.spew:
|
||||
spew()
|
||||
if cfg.daemon:
|
||||
daemonize()
|
||||
else:
|
||||
os.setpgrp()
|
||||
configure_logging(cfg)
|
||||
Arbiter(cfg, app).run()
|
||||
|
||||
|
||||
def daemonize():
|
||||
"""\
|
||||
Standard daemonization of a process. Code is basd on the
|
||||
ActiveState recipe at:
|
||||
http://code.activestate.com/recipes/278731/
|
||||
"""
|
||||
if not 'GUNICORN_FD' in os.environ:
|
||||
if os.fork() == 0:
|
||||
os.setsid()
|
||||
if os.fork() != 0:
|
||||
os.umask(0)
|
||||
else:
|
||||
os._exit(0)
|
||||
else:
|
||||
os._exit(0)
|
||||
|
||||
maxfd = util.get_maxfd()
|
||||
|
||||
# Iterate through and close all file descriptors.
|
||||
for fd in range(0, maxfd):
|
||||
try:
|
||||
os.close(fd)
|
||||
except OSError: # ERROR, fd wasn't open to begin with (ignored)
|
||||
pass
|
||||
|
||||
os.open(util.REDIRECT_TO, os.O_RDWR)
|
||||
os.dup2(0, 1)
|
||||
os.dup2(0, 2)
|
||||
|
||||
def configure_logging(opts):
|
||||
"""\
|
||||
Set the log level and choose the destination for log output.
|
||||
"""
|
||||
handlers = []
|
||||
if opts['logfile'] != "-":
|
||||
handlers.append(logging.FileHandler(opts['logfile']))
|
||||
else:
|
||||
handlers.append(logging.StreamHandler())
|
||||
|
||||
loglevel = LOG_LEVELS.get(opts['loglevel'].lower(), logging.INFO)
|
||||
|
||||
logger = logging.getLogger('gunicorn')
|
||||
logger.setLevel(loglevel)
|
||||
format = r"%(asctime)s [%(process)d] [%(levelname)s] %(message)s"
|
||||
datefmt = r"%Y-%m-%d %H:%M:%S"
|
||||
for h in handlers:
|
||||
h.setFormatter(logging.Formatter(format, datefmt))
|
||||
logger.addHandler(h)
|
||||
|
||||
334
gunicorn/options.ini
Normal file
334
gunicorn/options.ini
Normal file
@ -0,0 +1,334 @@
|
||||
# This file is read by config.py to set up the default
|
||||
# information for all server settings. It is here so
|
||||
# that we can use the data to generate all the various
|
||||
# docs related to configuration information.
|
||||
|
||||
#
|
||||
# Config file
|
||||
#
|
||||
|
||||
[config]
|
||||
section = Config
|
||||
cli = -c --config
|
||||
meta = FILE
|
||||
type = string
|
||||
default = gunicorn.conf.py
|
||||
desc = The path to a Gunicorn config file.
|
||||
|
||||
By default Gunicorn will try to read a file named 'gunicorn.conf.py' in the
|
||||
current directory.
|
||||
|
||||
Only has an effect when specified on the command line or as part of an
|
||||
application specific configuration.
|
||||
|
||||
#
|
||||
# Server Socket
|
||||
#
|
||||
|
||||
[bind]
|
||||
section = Server Socket
|
||||
order = 0
|
||||
cli = -b --bind
|
||||
meta = ADDRESS
|
||||
type = string
|
||||
default = 127.0.0.1:8000
|
||||
desc = The socket to bind.
|
||||
|
||||
[backlog]
|
||||
section = Server Socket
|
||||
order = 1
|
||||
cli = --backlog
|
||||
meta = INT
|
||||
type = pos_int
|
||||
default = 2048
|
||||
desc = The maximum number of pending connections.
|
||||
|
||||
This refers to the number of clients that can be waiting to be served.
|
||||
Exceeding this number results in the client getting an error when attempting
|
||||
to connect. It should only affect servers under significant load.
|
||||
|
||||
Must be a positive integer. Generally set in the 64-2048 range.
|
||||
|
||||
#
|
||||
# Worker processes
|
||||
#
|
||||
|
||||
[workers]
|
||||
section = Worker Processes
|
||||
order = 1
|
||||
cli = -w --workers
|
||||
meta = INT
|
||||
type = pos_int
|
||||
default = 1
|
||||
desc = The number of worker process for handling requests.
|
||||
|
||||
A positive integer generally in the 2-4 x $(NUM_CORES) range. You'll want to
|
||||
vary this a bit to find the best for your particular application's work
|
||||
load.
|
||||
|
||||
[worker_class]
|
||||
section = Worker Processes
|
||||
order = 2
|
||||
cli = -k --worker-class
|
||||
meta = STRING
|
||||
type = string
|
||||
default = egg:gunicorn#sync
|
||||
desc = The type of workers to use.
|
||||
|
||||
The default async class should handle most 'normal' types of work loads.
|
||||
You'll want to read http://gunicorn/deployment.hml for information on when
|
||||
you might want to choose one of the other worker classes.
|
||||
|
||||
An string referring to a 'gunicorn.workers' entry point or a MODULE:CLASS
|
||||
pair where CLASS is a subclass of gunicorn.workers.base.Worker. The default
|
||||
provided values are:
|
||||
|
||||
egg:gunicorn#sync
|
||||
egg:gunicorn#eventlet - Requires eventlet >= 0.9.7
|
||||
egg:gunicorn#gevent - Requires gevent >= 0.12.2 (?)
|
||||
egg:gunicorn#tornado - Requires tornado >= 0.2
|
||||
|
||||
[worker_connections]
|
||||
section = Worker Processes
|
||||
order = 3
|
||||
cli = --worker-connections
|
||||
meta = INT
|
||||
type = pos_int
|
||||
default = 1000
|
||||
desc = The maximum number of simultaneous clients.
|
||||
|
||||
This setting only affects the Eventlet and Gevent worker types.
|
||||
|
||||
A positive integer generally set to around 1000.
|
||||
|
||||
[timeout]
|
||||
section = Worker Processes
|
||||
order = 4
|
||||
cli = -t --timeout
|
||||
meta = INT
|
||||
type = pos_int
|
||||
default = 30
|
||||
desc = Workers silent for more than this many seconds are killed and restarted.
|
||||
|
||||
Generally set to thirty seconds. Only set this noticeably higher if you're
|
||||
sure of the repercussions for sync workers. For the non sync workers it just
|
||||
means that the worker process is still communicating and is not tied to the
|
||||
length of time required to handle a single request.
|
||||
|
||||
[keepalive]
|
||||
section = Worker Processes
|
||||
order = 5
|
||||
cli = --keep-alive
|
||||
meta = INT
|
||||
type = pos_int
|
||||
default = 2
|
||||
desc = The number of seconds to wait for requests on a Keep-Alive connection.
|
||||
|
||||
A positive integer. Generally set in the 1-5 seconds range.
|
||||
|
||||
#
|
||||
# Debugging
|
||||
#
|
||||
|
||||
[debug]
|
||||
section = Debugging
|
||||
order = 1
|
||||
cli = -d --debug
|
||||
type = bool
|
||||
action = store_true
|
||||
default = False
|
||||
desc = Turn on debugging in the server.
|
||||
|
||||
This limits the number of worker processes to 1 and changes some error
|
||||
handling that's sent to clients.
|
||||
|
||||
[spew]
|
||||
section = Debugging
|
||||
order = 2
|
||||
cli = --spew
|
||||
type = bool
|
||||
action = store_true
|
||||
default = False
|
||||
desc = Install a trace function that spews every line executed by the server.
|
||||
|
||||
This is the nuclear option.
|
||||
|
||||
#
|
||||
# Server mechanics
|
||||
#
|
||||
|
||||
[daemon]
|
||||
section = Server Mechanics
|
||||
order = 1
|
||||
cli = -D --daemon
|
||||
action = store_true
|
||||
type = bool
|
||||
default = False
|
||||
desc = Daemonize the Gunicorn process.
|
||||
|
||||
Detaches the server from the controlling terminal and enters the background.
|
||||
|
||||
[pidfile]
|
||||
section = Server Mechanics
|
||||
order = 2
|
||||
cli = -p --pid
|
||||
meta = FILE
|
||||
type = string
|
||||
default =
|
||||
desc = A filename to use for the PID file.
|
||||
|
||||
If not set, no PID file will be written.
|
||||
|
||||
[user]
|
||||
section = Server Mechanics
|
||||
order = 3
|
||||
cli = -u --user
|
||||
type = string
|
||||
default =
|
||||
desc = Switch worker processes to run as this user.
|
||||
|
||||
A valid user id (as an integer) or the name of a user that can be retrieved
|
||||
with a call to pwd.getpwnam(value) or None to not change the worker process
|
||||
user.
|
||||
|
||||
[group]
|
||||
section = Server Mechanics
|
||||
order = 4
|
||||
cli = -g --group
|
||||
type = string
|
||||
default =
|
||||
desc = Switch worker process to run as this group.
|
||||
|
||||
A valid group id (as an integer) or the name of a user that can be retrieved
|
||||
with a call to pwd.getgrnam(value) or None to change the worker processes
|
||||
group.
|
||||
|
||||
[umask]
|
||||
section = Server Mechanics
|
||||
order = 5
|
||||
cli = -m --umask
|
||||
meta = INT
|
||||
type = pos_int
|
||||
default = 0
|
||||
desc = A bit mask for the file mode on files written by Gunicorn.
|
||||
|
||||
Note that this affects unix socket permissions.
|
||||
|
||||
A valid value for the os.umask(mode) call or a string compatible with
|
||||
int(value, 0) (0 means Python guesses the base, so values like "0", "0xFF",
|
||||
"0022" are valid for decimal, hex, and octal representations)
|
||||
|
||||
[tmp_upload_dir]
|
||||
section = Server Mechanics
|
||||
order = 6
|
||||
cli =
|
||||
meta = DIR
|
||||
type = string
|
||||
default =
|
||||
desc = Directory to store temporary request data as they are read.
|
||||
|
||||
This may disappear in the near future.
|
||||
|
||||
This path should be writable by the process permissions set for Gunicorn
|
||||
workers. If not specified, Gunicorn will choose a system generated temporary
|
||||
directory.
|
||||
|
||||
#
|
||||
# Logging
|
||||
#
|
||||
|
||||
[logfile]
|
||||
section = Logging
|
||||
order = 1
|
||||
cli = --log-file
|
||||
meta = FILE
|
||||
type = string
|
||||
default = -
|
||||
desc = The log file to write to.
|
||||
|
||||
"-" means log to stdout.
|
||||
|
||||
[loglevel]
|
||||
section = Logging
|
||||
order = 2
|
||||
cli = --log-level
|
||||
meta = LEVEL
|
||||
type = string
|
||||
default = info
|
||||
desc = The granularity of log output
|
||||
|
||||
Valid level names are:
|
||||
|
||||
debug
|
||||
info
|
||||
warning
|
||||
error
|
||||
critical
|
||||
|
||||
#
|
||||
# Process naming
|
||||
#
|
||||
|
||||
[proc_name]
|
||||
section = Process Naming
|
||||
order = 1
|
||||
cli = -n --name
|
||||
meta = STRING
|
||||
type = string
|
||||
default = gunicorn
|
||||
desc = A base to use with setproctitle for process naming.
|
||||
|
||||
This affects things like 'ps' and 'top'. If you're going to be running more
|
||||
than one instance of Gunicorn you'll probably want to set a name to tell
|
||||
them apart. This requires that you install the setproctitle module.
|
||||
|
||||
It defaults to 'gunicorn'.
|
||||
|
||||
[default_proc_name]
|
||||
section = Process Naming
|
||||
order = 2
|
||||
cli =
|
||||
type = string
|
||||
default = gunicorn
|
||||
desc = Internal setting that is adjusted for each type of application.
|
||||
|
||||
#
|
||||
# Server hooks
|
||||
#
|
||||
|
||||
[pre_fork]
|
||||
section = Server Hooks
|
||||
order = 1
|
||||
cli =
|
||||
type = callable
|
||||
arity = 2
|
||||
default =
|
||||
desc = Called just before a worker is forked.
|
||||
|
||||
The callable needs to accept two instance variables for the Arbiter and new
|
||||
Worker.
|
||||
|
||||
[post_fork]
|
||||
section = Server Hooks
|
||||
order = 2
|
||||
cli =
|
||||
type = callable
|
||||
arity = 2
|
||||
default =
|
||||
desc = Called just after a worker has been forked.
|
||||
|
||||
The callable needs to accept two instance variables for the Arbiter and new
|
||||
Worker.
|
||||
|
||||
[pre_exec]
|
||||
section = Server Hooks
|
||||
order = 3
|
||||
cli =
|
||||
type = callable
|
||||
arity = 1
|
||||
default =
|
||||
desc = Called just before a new master process is forked.
|
||||
|
||||
The callable needs to accept a single instance variable for the Arbiter.
|
||||
|
||||
Cannot be specified from the command line.
|
||||
@ -36,7 +36,7 @@ class BaseSocket(object):
|
||||
if not bound:
|
||||
self.bind(sock)
|
||||
sock.setblocking(0)
|
||||
sock.listen(self.conf['backlog'])
|
||||
sock.listen(self.conf.backlog)
|
||||
|
||||
return sock
|
||||
|
||||
|
||||
@ -208,4 +208,33 @@ def to_bytestring(s):
|
||||
return s
|
||||
|
||||
def is_hoppish(header):
|
||||
return header.lower().strip() in hop_headers
|
||||
return header.lower().strip() in hop_headers
|
||||
|
||||
def daemonize():
|
||||
"""\
|
||||
Standard daemonization of a process. Code is basd on the
|
||||
ActiveState recipe at:
|
||||
http://code.activestate.com/recipes/278731/
|
||||
"""
|
||||
if not 'GUNICORN_FD' in os.environ:
|
||||
if os.fork() == 0:
|
||||
os.setsid()
|
||||
if os.fork() != 0:
|
||||
os.umask(0)
|
||||
else:
|
||||
os._exit(0)
|
||||
else:
|
||||
os._exit(0)
|
||||
|
||||
maxfd = util.get_maxfd()
|
||||
|
||||
# Iterate through and close all file descriptors.
|
||||
for fd in range(0, maxfd):
|
||||
try:
|
||||
os.close(fd)
|
||||
except OSError: # ERROR, fd wasn't open to begin with (ignored)
|
||||
pass
|
||||
|
||||
os.open(util.REDIRECT_TO, os.O_RDWR)
|
||||
os.dup2(0, 1)
|
||||
os.dup2(0, 2)
|
||||
@ -21,8 +21,7 @@ class AsyncWorker(Worker):
|
||||
self.worker_connections = self.cfg.worker_connections
|
||||
|
||||
def keepalive_request(self, client, addr):
|
||||
return http.KeepAliveRequest(client, addr, self.address,
|
||||
self.cfg)
|
||||
return http.KeepAliveRequest(self.cfg, client, addr, self.address)
|
||||
|
||||
def handle(self, client, addr):
|
||||
try:
|
||||
|
||||
@ -89,7 +89,7 @@ class SyncWorker(Worker):
|
||||
util.close(client)
|
||||
|
||||
def handle_request(self, client, addr):
|
||||
req = http.Request(client, addr, self.address, self.cfg)
|
||||
req = http.Request(self.cfg, client, addr, self.address)
|
||||
try:
|
||||
environ = req.read()
|
||||
if not environ or not req.parser.status_line:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user