Merge remote-tracking branch 'hiisi13/master'

Signed-off-by: Jannis Leidel <jannis@leidel.info>
This commit is contained in:
Jannis Leidel 2015-01-06 22:47:06 +01:00
commit 44476bdd1d
3 changed files with 101 additions and 2 deletions

View file

@ -1,3 +1,4 @@
import inspect
import sys
from django.core.exceptions import ImproperlyConfigured
@ -122,3 +123,76 @@ except ImportError:
raise Exception
except: # Needed because we might need to catch a SystemExit
largs.append(arg)
# Copied over from Sphinx
if sys.version_info >= (3, 0):
from functools import partial
def getargspec(func):
"""Like inspect.getargspec but supports functools.partial as well."""
if inspect.ismethod(func):
func = func.__func__
if type(func) is partial:
orig_func = func.func
argspec = getargspec(orig_func)
args = list(argspec[0])
defaults = list(argspec[3] or ())
kwoargs = list(argspec[4])
kwodefs = dict(argspec[5] or {})
if func.args:
args = args[len(func.args):]
for arg in func.keywords or ():
try:
i = args.index(arg) - len(args)
del args[i]
try:
del defaults[i]
except IndexError:
pass
except ValueError: # must be a kwonly arg
i = kwoargs.index(arg)
del kwoargs[i]
del kwodefs[arg]
return inspect.FullArgSpec(args, argspec[1], argspec[2],
tuple(defaults), kwoargs,
kwodefs, argspec[6])
while hasattr(func, '__wrapped__'):
func = func.__wrapped__
if not inspect.isfunction(func):
raise TypeError('%r is not a Python function' % func)
return inspect.getfullargspec(func)
else: # 2.6, 2.7
from functools import partial
def getargspec(func):
"""Like inspect.getargspec but supports functools.partial as well."""
if inspect.ismethod(func):
func = func.im_func
parts = 0, ()
if type(func) is partial:
keywords = func.keywords
if keywords is None:
keywords = {}
parts = len(func.args), keywords.keys()
func = func.func
if not inspect.isfunction(func):
raise TypeError('%r is not a Python function' % func)
args, varargs, varkw = inspect.getargs(func.func_code)
func_defaults = func.func_defaults
if func_defaults is None:
func_defaults = []
else:
func_defaults = list(func_defaults)
if parts[0]:
args = args[parts[0]:]
if parts[1]:
for arg in parts[1]:
i = args.index(arg) - len(args)
del args[i]
try:
del func_defaults[i]
except IndexError:
pass
return inspect.ArgSpec(args, varargs, varkw, func_defaults)

View file

@ -8,7 +8,7 @@ from django.core import validators
from django.core.exceptions import ValidationError, ImproperlyConfigured
from django.utils import six
from .utils import import_by_path
from .utils import import_by_path, getargspec
def setup_value(target, name, value):
@ -140,10 +140,20 @@ class CastingMixin(object):
error = 'Cannot use caster of {0} ({1!r})'.format(self,
self.caster)
raise ValueError(error)
try:
arg_names = getargspec(self._caster)[0]
self._params = dict((name, kwargs[name])
for name in arg_names
if name in kwargs)
except TypeError:
self._params = {}
def to_python(self, value):
try:
return self._caster(value)
if self._params:
return self._caster(value, **self._params)
else:
return self._caster(value)
except self.exception:
raise ValueError(self.message.format(value))

View file

@ -295,6 +295,21 @@ class ValueTests(TestCase):
'USER': '',
}})
def test_database_url_additional_args(self):
def mock_database_url_caster(self, url, engine=None):
return { 'URL': url, 'ENGINE': engine }
with patch('configurations.values.DatabaseURLValue.caster', mock_database_url_caster):
value = DatabaseURLValue(engine='django_mysqlpool.backends.mysqlpool')
with env(DATABASE_URL='sqlite://'):
self.assertEqual(value.setup('DATABASE_URL'), {
'default': {
'URL': 'sqlite://',
'ENGINE': 'django_mysqlpool.backends.mysqlpool'
}
})
def test_email_url_value(self):
value = EmailURLValue()
self.assertEqual(value.default, {})