mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
Change parameters to use only default_from for dynamic default values.
Replace all occurences of create_default with equivalent default_from and remove create_default from the framework. This is needed for proper parameter validation, as there is no way to tell which parameters to validate prior to calling create_default, because create_default does not provide information about which parameters are used for generating the default value.
This commit is contained in:
committed by
Martin Kosek
parent
5a55e11a25
commit
a2299070c8
@@ -198,6 +198,8 @@ class DefaultFrom(ReadOnly):
|
|||||||
self.callback = callback
|
self.callback = callback
|
||||||
if len(keys) == 0:
|
if len(keys) == 0:
|
||||||
fc = callback.func_code
|
fc = callback.func_code
|
||||||
|
if fc.co_flags & 0x0c:
|
||||||
|
raise ValueError("callback: variable-length argument list not allowed")
|
||||||
self.keys = fc.co_varnames[:fc.co_argcount]
|
self.keys = fc.co_varnames[:fc.co_argcount]
|
||||||
else:
|
else:
|
||||||
self.keys = keys
|
self.keys = keys
|
||||||
@@ -308,13 +310,9 @@ class Param(ReadOnly):
|
|||||||
encoder
|
encoder
|
||||||
- default_from: a custom function for generating default values of
|
- default_from: a custom function for generating default values of
|
||||||
parameter instance
|
parameter instance
|
||||||
- create_default: a custom function for generating default values of
|
|
||||||
parameter instance. Unlike default_from attribute, this function
|
|
||||||
is not wrapped. `Param.get_default()` documentation provides further
|
|
||||||
details
|
|
||||||
- autofill: by default, only `required` parameters get a default value
|
- autofill: by default, only `required` parameters get a default value
|
||||||
from default_from or create_default functions. When autofill is
|
from the default_from function. When autofill is enabled, optional
|
||||||
enabled, optional attributes get the default value filled too
|
attributes get the default value filled too
|
||||||
- query: this attribute is controlled by framework. When the `query`
|
- query: this attribute is controlled by framework. When the `query`
|
||||||
is enabled, framework assumes that the value is only queried and not
|
is enabled, framework assumes that the value is only queried and not
|
||||||
inserted in the LDAP. Validation is then relaxed - custom
|
inserted in the LDAP. Validation is then relaxed - custom
|
||||||
@@ -379,7 +377,6 @@ class Param(ReadOnly):
|
|||||||
('normalizer', callable, None),
|
('normalizer', callable, None),
|
||||||
('encoder', callable, None),
|
('encoder', callable, None),
|
||||||
('default_from', DefaultFrom, None),
|
('default_from', DefaultFrom, None),
|
||||||
('create_default', callable, None),
|
|
||||||
('autofill', bool, False),
|
('autofill', bool, False),
|
||||||
('query', bool, False),
|
('query', bool, False),
|
||||||
('attribute', bool, False),
|
('attribute', bool, False),
|
||||||
@@ -481,20 +478,6 @@ class Param(ReadOnly):
|
|||||||
class_rules.append(getattr(self, rule_name))
|
class_rules.append(getattr(self, rule_name))
|
||||||
check_name(self.cli_name)
|
check_name(self.cli_name)
|
||||||
|
|
||||||
# Check that only default_from or create_default was provided:
|
|
||||||
assert not hasattr(self, '_get_default'), self.nice
|
|
||||||
if callable(self.default_from):
|
|
||||||
if callable(self.create_default):
|
|
||||||
raise ValueError(
|
|
||||||
'%s: cannot have both %r and %r' % (
|
|
||||||
self.nice, 'default_from', 'create_default')
|
|
||||||
)
|
|
||||||
self._get_default = self.default_from
|
|
||||||
elif callable(self.create_default):
|
|
||||||
self._get_default = self.create_default
|
|
||||||
else:
|
|
||||||
self._get_default = None
|
|
||||||
|
|
||||||
# Check that only 'include' or 'exclude' was provided:
|
# Check that only 'include' or 'exclude' was provided:
|
||||||
if None not in (self.include, self.exclude):
|
if None not in (self.include, self.exclude):
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
@@ -990,59 +973,9 @@ class Param(ReadOnly):
|
|||||||
>>> kw = dict(first=u'John', department=u'Engineering')
|
>>> kw = dict(first=u'John', department=u'Engineering')
|
||||||
>>> login.get_default(**kw)
|
>>> login.get_default(**kw)
|
||||||
u'my-static-login-default'
|
u'my-static-login-default'
|
||||||
|
|
||||||
The second, less common way to construct a dynamic default is to provide
|
|
||||||
a callback via the ``create_default`` keyword argument. Unlike a
|
|
||||||
``default_from`` callback, your ``create_default`` callback will not get
|
|
||||||
wrapped in any dispatcher. Instead, it will be called directly, which
|
|
||||||
means your callback must accept arbitrary keyword arguments, although
|
|
||||||
whether your callback utilises these values is up to your
|
|
||||||
implementation. For example:
|
|
||||||
|
|
||||||
>>> def make_csr(**kw):
|
|
||||||
... print ' make_csr(%r)' % (kw,) # Note output below
|
|
||||||
... return 'Certificate Signing Request'
|
|
||||||
...
|
|
||||||
>>> csr = Bytes('csr', create_default=make_csr)
|
|
||||||
|
|
||||||
Your ``create_default`` callback will be called with whatever keyword
|
|
||||||
arguments are passed to `Param.get_default()`. For example:
|
|
||||||
|
|
||||||
>>> kw = dict(arbitrary='Keyword', arguments='Here')
|
|
||||||
>>> csr.get_default(**kw)
|
|
||||||
make_csr({'arguments': 'Here', 'arbitrary': 'Keyword'})
|
|
||||||
'Certificate Signing Request'
|
|
||||||
|
|
||||||
And your ``create_default`` callback is called even if
|
|
||||||
`Param.get_default()` is called with *zero* keyword arguments.
|
|
||||||
For example:
|
|
||||||
|
|
||||||
>>> csr.get_default()
|
|
||||||
make_csr({})
|
|
||||||
'Certificate Signing Request'
|
|
||||||
|
|
||||||
The ``create_default`` callback will most likely be used as a
|
|
||||||
pre-execute hook to perform some special client-side operation. For
|
|
||||||
example, the ``csr`` parameter above might make a call to
|
|
||||||
``/usr/bin/openssl``. However, often a ``create_default`` callback
|
|
||||||
could also be implemented as a ``default_from`` callback. When this is
|
|
||||||
the case, a ``default_from`` callback should be used as they are more
|
|
||||||
structured and therefore less error-prone.
|
|
||||||
|
|
||||||
The ``default_from`` and ``create_default`` keyword arguments are
|
|
||||||
mutually exclusive. If you provide both, a ``ValueError`` will be
|
|
||||||
raised. For example:
|
|
||||||
|
|
||||||
>>> homedir = Str('home',
|
|
||||||
... default_from=lambda login: '/home/%s' % login,
|
|
||||||
... create_default=lambda **kw: '/lets/use/this',
|
|
||||||
... )
|
|
||||||
Traceback (most recent call last):
|
|
||||||
...
|
|
||||||
ValueError: Str('home'): cannot have both 'default_from' and 'create_default'
|
|
||||||
"""
|
"""
|
||||||
if self._get_default is not None:
|
if self.default_from is not None:
|
||||||
default = self._get_default(**kw)
|
default = self.default_from(**kw)
|
||||||
if default is not None:
|
if default is not None:
|
||||||
try:
|
try:
|
||||||
return self.convert(self.normalize(default))
|
return self.convert(self.normalize(default))
|
||||||
|
|||||||
@@ -234,7 +234,7 @@ def _rname_validator(ugettext, zonemgr):
|
|||||||
return unicode(e)
|
return unicode(e)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _create_zone_serial(**kwargs):
|
def _create_zone_serial():
|
||||||
""" Generate serial number for zones. The format follows RFC 1912 """
|
""" Generate serial number for zones. The format follows RFC 1912 """
|
||||||
return int('%s01' % time.strftime('%Y%m%d'))
|
return int('%s01' % time.strftime('%Y%m%d'))
|
||||||
|
|
||||||
@@ -1554,7 +1554,7 @@ class dnszone(LDAPObject):
|
|||||||
label=_('SOA serial'),
|
label=_('SOA serial'),
|
||||||
doc=_('SOA record serial number'),
|
doc=_('SOA record serial number'),
|
||||||
minvalue=1,
|
minvalue=1,
|
||||||
create_default=_create_zone_serial,
|
default_from=_create_zone_serial,
|
||||||
autofill=True,
|
autofill=True,
|
||||||
),
|
),
|
||||||
Int('idnssoarefresh',
|
Int('idnssoarefresh',
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ class passwd(Command):
|
|||||||
label=_('User name'),
|
label=_('User name'),
|
||||||
primary_key=True,
|
primary_key=True,
|
||||||
autofill=True,
|
autofill=True,
|
||||||
create_default=lambda **kw: util.get_current_principal(),
|
default_from=lambda: util.get_current_principal(),
|
||||||
normalizer=lambda value: normalize_principal(value),
|
normalizer=lambda value: normalize_principal(value),
|
||||||
),
|
),
|
||||||
Password('password',
|
Password('password',
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ class join(Command):
|
|||||||
validate_host,
|
validate_host,
|
||||||
cli_name='hostname',
|
cli_name='hostname',
|
||||||
doc=_("The hostname to register as"),
|
doc=_("The hostname to register as"),
|
||||||
create_default=lambda **kw: unicode(util.get_fqdn()),
|
default_from=lambda: unicode(util.get_fqdn()),
|
||||||
autofill=True,
|
autofill=True,
|
||||||
#normalizer=lamda value: value.lower(),
|
#normalizer=lamda value: value.lower(),
|
||||||
),
|
),
|
||||||
@@ -60,7 +60,7 @@ class join(Command):
|
|||||||
takes_options= (
|
takes_options= (
|
||||||
Str('realm',
|
Str('realm',
|
||||||
doc=_("The IPA realm"),
|
doc=_("The IPA realm"),
|
||||||
create_default=lambda **kw: get_realm(),
|
default_from=lambda: get_realm(),
|
||||||
autofill=True,
|
autofill=True,
|
||||||
),
|
),
|
||||||
Str('nshardwareplatform?',
|
Str('nshardwareplatform?',
|
||||||
|
|||||||
@@ -56,9 +56,9 @@ class IPATypeChecker(TypeChecker):
|
|||||||
'ipalib.plugins.misc.env': ['env'],
|
'ipalib.plugins.misc.env': ['env'],
|
||||||
'ipalib.parameters.Param': ['cli_name', 'cli_short_name', 'label',
|
'ipalib.parameters.Param': ['cli_name', 'cli_short_name', 'label',
|
||||||
'doc', 'required', 'multivalue', 'primary_key', 'normalizer',
|
'doc', 'required', 'multivalue', 'primary_key', 'normalizer',
|
||||||
'default', 'default_from', 'create_default', 'autofill', 'query',
|
'default', 'default_from', 'autofill', 'query', 'attribute',
|
||||||
'attribute', 'include', 'exclude', 'flags', 'hint', 'alwaysask',
|
'include', 'exclude', 'flags', 'hint', 'alwaysask', 'sortorder',
|
||||||
'sortorder', 'csv', 'csv_separator', 'csv_skipspace'],
|
'csv', 'csv_separator', 'csv_skipspace'],
|
||||||
'ipalib.parameters.Bool': ['truths', 'falsehoods'],
|
'ipalib.parameters.Bool': ['truths', 'falsehoods'],
|
||||||
'ipalib.parameters.Int': ['minvalue', 'maxvalue'],
|
'ipalib.parameters.Int': ['minvalue', 'maxvalue'],
|
||||||
'ipalib.parameters.Decimal': ['minvalue', 'maxvalue', 'precision'],
|
'ipalib.parameters.Decimal': ['minvalue', 'maxvalue', 'precision'],
|
||||||
|
|||||||
1
makeapi
1
makeapi
@@ -45,7 +45,6 @@ PARAM_IGNORED_KW_ATTRIBUTES = ('label',
|
|||||||
'normalizer',
|
'normalizer',
|
||||||
'encoder',
|
'encoder',
|
||||||
'default_from',
|
'default_from',
|
||||||
'create_default',
|
|
||||||
'hint',
|
'hint',
|
||||||
'flags',
|
'flags',
|
||||||
'sortorder',)
|
'sortorder',)
|
||||||
|
|||||||
@@ -64,6 +64,16 @@ class test_DefaultFrom(ClassChecker):
|
|||||||
e = raises(TypeError, self.cls, callback, 'givenname', 17)
|
e = raises(TypeError, self.cls, callback, 'givenname', 17)
|
||||||
assert str(e) == TYPE_ERROR % ('keys', str, 17, int)
|
assert str(e) == TYPE_ERROR % ('keys', str, 17, int)
|
||||||
|
|
||||||
|
# Test that ValueError is raised when inferring keys from a callback
|
||||||
|
# which has *args:
|
||||||
|
e = raises(ValueError, self.cls, lambda foo, *args: None)
|
||||||
|
assert str(e) == "callback: variable-length argument list not allowed"
|
||||||
|
|
||||||
|
# Test that ValueError is raised when inferring keys from a callback
|
||||||
|
# which has **kwargs:
|
||||||
|
e = raises(ValueError, self.cls, lambda foo, **kwargs: None)
|
||||||
|
assert str(e) == "callback: variable-length argument list not allowed"
|
||||||
|
|
||||||
def test_repr(self):
|
def test_repr(self):
|
||||||
"""
|
"""
|
||||||
Test the `ipalib.parameters.DefaultFrom.__repr__` method.
|
Test the `ipalib.parameters.DefaultFrom.__repr__` method.
|
||||||
@@ -185,8 +195,6 @@ class test_Param(ClassChecker):
|
|||||||
assert o.normalizer is None
|
assert o.normalizer is None
|
||||||
assert o.default is None
|
assert o.default is None
|
||||||
assert o.default_from is None
|
assert o.default_from is None
|
||||||
assert o.create_default is None
|
|
||||||
assert o._get_default is None
|
|
||||||
assert o.autofill is False
|
assert o.autofill is False
|
||||||
assert o.query is False
|
assert o.query is False
|
||||||
assert o.attribute is False
|
assert o.attribute is False
|
||||||
@@ -250,16 +258,6 @@ class test_Param(ClassChecker):
|
|||||||
assert str(e) == \
|
assert str(e) == \
|
||||||
"Param('my_param'): takes no such kwargs: 'ape', 'great'"
|
"Param('my_param'): takes no such kwargs: 'ape', 'great'"
|
||||||
|
|
||||||
# Test that ValueError is raised if you provide both default_from and
|
|
||||||
# create_default:
|
|
||||||
e = raises(ValueError, self.cls, 'my_param',
|
|
||||||
default_from=lambda first, last: first[0] + last,
|
|
||||||
create_default=lambda **kw: 'The Default'
|
|
||||||
)
|
|
||||||
assert str(e) == '%s: cannot have both %r and %r' % (
|
|
||||||
"Param('my_param')", 'default_from', 'create_default',
|
|
||||||
)
|
|
||||||
|
|
||||||
# Test that ValueError is raised if you provide both include and
|
# Test that ValueError is raised if you provide both include and
|
||||||
# exclude:
|
# exclude:
|
||||||
e = raises(ValueError, self.cls, 'my_param',
|
e = raises(ValueError, self.cls, 'my_param',
|
||||||
@@ -276,15 +274,11 @@ class test_Param(ClassChecker):
|
|||||||
e = raises(ValueError, self.cls, 'my_param', csv=True)
|
e = raises(ValueError, self.cls, 'my_param', csv=True)
|
||||||
assert str(e) == '%s: cannot have csv without multivalue' % "Param('my_param')"
|
assert str(e) == '%s: cannot have csv without multivalue' % "Param('my_param')"
|
||||||
|
|
||||||
# Test that _get_default gets set:
|
# Test that default_from gets set:
|
||||||
call1 = lambda first, last: first[0] + last
|
call = lambda first, last: first[0] + last
|
||||||
call2 = lambda **kw: 'The Default'
|
o = self.cls('my_param', default_from=call)
|
||||||
o = self.cls('my_param', default_from=call1)
|
assert type(o.default_from) is parameters.DefaultFrom
|
||||||
assert o.default_from.callback is call1
|
assert o.default_from.callback is call
|
||||||
assert o._get_default is o.default_from
|
|
||||||
o = self.cls('my_param', create_default=call2)
|
|
||||||
assert o.create_default is call2
|
|
||||||
assert o._get_default is call2
|
|
||||||
|
|
||||||
def test_repr(self):
|
def test_repr(self):
|
||||||
"""
|
"""
|
||||||
@@ -579,7 +573,7 @@ class test_Param(ClassChecker):
|
|||||||
|
|
||||||
def test_get_default(self):
|
def test_get_default(self):
|
||||||
"""
|
"""
|
||||||
Test the `ipalib.parameters.Param._get_default` method.
|
Test the `ipalib.parameters.Param.get_default` method.
|
||||||
"""
|
"""
|
||||||
class PassThrough(object):
|
class PassThrough(object):
|
||||||
value = None
|
value = None
|
||||||
@@ -624,17 +618,6 @@ class test_Param(ClassChecker):
|
|||||||
assert o._convert_scalar.value is default
|
assert o._convert_scalar.value is default
|
||||||
assert o.normalizer.value is default
|
assert o.normalizer.value is default
|
||||||
|
|
||||||
# Test with create_default:
|
|
||||||
o = Str('my_str',
|
|
||||||
normalizer=PassThrough(),
|
|
||||||
default=u'Static Default',
|
|
||||||
create_default=lambda **kw: u'The created default',
|
|
||||||
)
|
|
||||||
default = o.get_default(first=u'john', last='doe')
|
|
||||||
assert_equal(default, u'The created default')
|
|
||||||
assert o._convert_scalar.value is default
|
|
||||||
assert o.normalizer.value is default
|
|
||||||
|
|
||||||
def test_split_csv(self):
|
def test_split_csv(self):
|
||||||
"""
|
"""
|
||||||
Test the `ipalib.parameters.Param.split_csv` method with csv.
|
Test the `ipalib.parameters.Param.split_csv` method with csv.
|
||||||
|
|||||||
Reference in New Issue
Block a user