diff --git a/gunicorn/config.py b/gunicorn/config.py index 24d9c2cd..bfab5bda 100644 --- a/gunicorn/config.py +++ b/gunicorn/config.py @@ -229,9 +229,23 @@ def validate_class(val): def validate_callable(arity): def _validate_callable(val): + if isinstance(val, basestring): + try: + mod_name, obj_name = val.rsplit(".", 1) + except ValueError: + raise TypeError("Value '%s' is not import string. " + "Format: module[.submodules...].object" % val) + try: + mod = __import__(mod_name, fromlist=[obj_name]) + val = getattr(mod, obj_name) + except ImportError as e: + raise TypeError(str(e)) + except AttributeError: + raise TypeError("Can not load '%s' from '%s'" + "" % (obj_name, mod_name)) if not callable(val): raise TypeError("Value is not callable: %s" % val) - if arity != len(inspect.getargspec(val)[0]): + if arity != -1 and arity != len(inspect.getargspec(val)[0]): raise TypeError("Value must have an arity of: %s" % arity) return val return _validate_callable @@ -265,21 +279,13 @@ def validate_group(val): raise ConfigError("No such group: '%s'" % val) def validate_post_request(val): - - # decorator - def wrap_post_request(fun): - def _wrapped(instance, req, environ): - return fun(instance, req) - return _wrapped - - if not callable(val): - raise TypeError("Value isn't a callable: %s" % val) + val = validate_callable(-1)(val) largs = len(inspect.getargspec(val)[0]) if largs == 3: return val elif largs == 2: - return wrap_post_request(val) + return lambda worker, req, _: val(worker, req) else: raise TypeError("Value must have an arity of: 3") diff --git a/tests/003-test-config.py b/tests/003-test-config.py index 50fb13bc..555ba9f8 100644 --- a/tests/003-test-config.py +++ b/tests/003-test-config.py @@ -150,6 +150,28 @@ def test_callable_validation(): t.raises(TypeError, c.set, "pre_fork", 1) t.raises(TypeError, c.set, "pre_fork", lambda x: True) +def test_callable_validation_for_string(): + from os.path import isdir as testfunc + t.eq( + config.validate_callable(-1)("os.path.isdir"), + testfunc + ) + + # invalid values tests + t.raises( + TypeError, + config.validate_callable(-1), "" + ) + t.raises( + TypeError, + config.validate_callable(-1), "os.path.not_found_func" + ) + t.raises( + TypeError, + config.validate_callable(-1), "notfoundmodule.func" + ) + + def test_cmd_line(): with AltArgs(["prog_name", "-b", "blargh"]): app = NoConfigApp()