mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2024-12-24 08:00:02 -06:00
Removed depreciated code from frontend.py; frontend.py no longer imports ipa_types
This commit is contained in:
parent
f3e0900ebc
commit
2b2e73e7df
@ -27,7 +27,7 @@ import plugable
|
||||
from plugable import lock, check_name
|
||||
import errors
|
||||
from errors import check_type, check_isinstance, raise_TypeError
|
||||
import ipa_types
|
||||
import parameters
|
||||
from util import make_repr
|
||||
|
||||
|
||||
@ -42,453 +42,6 @@ def is_rule(obj):
|
||||
return callable(obj) and getattr(obj, RULE_FLAG, False) is True
|
||||
|
||||
|
||||
class DefaultFrom(plugable.ReadOnly):
|
||||
"""
|
||||
Derive a default value from other supplied values.
|
||||
|
||||
For example, say you wanted to create a default for the user's login from
|
||||
the user's first and last names. It could be implemented like this:
|
||||
|
||||
>>> login = DefaultFrom(lambda first, last: first[0] + last)
|
||||
>>> login(first='John', last='Doe')
|
||||
'JDoe'
|
||||
|
||||
If you do not explicitly provide keys when you create a DefaultFrom
|
||||
instance, the keys are implicitly derived from your callback by
|
||||
inspecting ``callback.func_code.co_varnames``. The keys are available
|
||||
through the ``DefaultFrom.keys`` instance attribute, like this:
|
||||
|
||||
>>> login.keys
|
||||
('first', 'last')
|
||||
|
||||
The callback is available through the ``DefaultFrom.callback`` instance
|
||||
attribute, like this:
|
||||
|
||||
>>> login.callback # doctest:+ELLIPSIS
|
||||
<function <lambda> at 0x...>
|
||||
>>> login.callback.func_code.co_varnames # The keys
|
||||
('first', 'last')
|
||||
|
||||
The keys can be explicitly provided as optional positional arguments after
|
||||
the callback. For example, this is equivalent to the ``login`` instance
|
||||
above:
|
||||
|
||||
>>> login2 = DefaultFrom(lambda a, b: a[0] + b, 'first', 'last')
|
||||
>>> login2.keys
|
||||
('first', 'last')
|
||||
>>> login2.callback.func_code.co_varnames # Not the keys
|
||||
('a', 'b')
|
||||
>>> login2(first='John', last='Doe')
|
||||
'JDoe'
|
||||
|
||||
If any keys are missing when calling your DefaultFrom instance, your
|
||||
callback is not called and None is returned. For example:
|
||||
|
||||
>>> login(first='John', lastname='Doe') is None
|
||||
True
|
||||
>>> login() is None
|
||||
True
|
||||
|
||||
Any additional keys are simply ignored, like this:
|
||||
|
||||
>>> login(last='Doe', first='John', middle='Whatever')
|
||||
'JDoe'
|
||||
|
||||
As above, because `DefaultFrom.__call__` takes only pure keyword
|
||||
arguments, they can be supplied in any order.
|
||||
|
||||
Of course, the callback need not be a lambda expression. This third
|
||||
example is equivalent to both the ``login`` and ``login2`` instances
|
||||
above:
|
||||
|
||||
>>> def get_login(first, last):
|
||||
... return first[0] + last
|
||||
...
|
||||
>>> login3 = DefaultFrom(get_login)
|
||||
>>> login3.keys
|
||||
('first', 'last')
|
||||
>>> login3.callback.func_code.co_varnames
|
||||
('first', 'last')
|
||||
>>> login3(first='John', last='Doe')
|
||||
'JDoe'
|
||||
"""
|
||||
|
||||
def __init__(self, callback, *keys):
|
||||
"""
|
||||
:param callback: The callable to call when all keys are present.
|
||||
:param keys: Optional keys used for source values.
|
||||
"""
|
||||
if not callable(callback):
|
||||
raise TypeError('callback must be callable; got %r' % callback)
|
||||
self.callback = callback
|
||||
if len(keys) == 0:
|
||||
fc = callback.func_code
|
||||
self.keys = fc.co_varnames[:fc.co_argcount]
|
||||
else:
|
||||
self.keys = keys
|
||||
for key in self.keys:
|
||||
if type(key) is not str:
|
||||
raise_TypeError(key, str, 'keys')
|
||||
lock(self)
|
||||
|
||||
def __call__(self, **kw):
|
||||
"""
|
||||
If all keys are present, calls the callback; otherwise returns None.
|
||||
|
||||
:param kw: The keyword arguments.
|
||||
"""
|
||||
vals = tuple(kw.get(k, None) for k in self.keys)
|
||||
if None in vals:
|
||||
return
|
||||
try:
|
||||
return self.callback(*vals)
|
||||
except StandardError:
|
||||
pass
|
||||
|
||||
|
||||
def parse_param_spec(spec):
|
||||
"""
|
||||
Parse a param spec into to (name, kw).
|
||||
|
||||
The ``spec`` string determines the param name, whether the param is
|
||||
required, and whether the param is multivalue according the following
|
||||
syntax:
|
||||
|
||||
====== ===== ======== ==========
|
||||
Spec Name Required Multivalue
|
||||
====== ===== ======== ==========
|
||||
'var' 'var' True False
|
||||
'var?' 'var' False False
|
||||
'var*' 'var' False True
|
||||
'var+' 'var' True True
|
||||
====== ===== ======== ==========
|
||||
|
||||
For example,
|
||||
|
||||
>>> parse_param_spec('login')
|
||||
('login', {'required': True, 'multivalue': False})
|
||||
>>> parse_param_spec('gecos?')
|
||||
('gecos', {'required': False, 'multivalue': False})
|
||||
>>> parse_param_spec('telephone_numbers*')
|
||||
('telephone_numbers', {'required': False, 'multivalue': True})
|
||||
>>> parse_param_spec('group+')
|
||||
('group', {'required': True, 'multivalue': True})
|
||||
|
||||
:param spec: A spec string.
|
||||
"""
|
||||
if type(spec) is not str:
|
||||
raise_TypeError(spec, str, 'spec')
|
||||
if len(spec) < 2:
|
||||
raise ValueError(
|
||||
'param spec must be at least 2 characters; got %r' % spec
|
||||
)
|
||||
_map = {
|
||||
'?': dict(required=False, multivalue=False),
|
||||
'*': dict(required=False, multivalue=True),
|
||||
'+': dict(required=True, multivalue=True),
|
||||
}
|
||||
end = spec[-1]
|
||||
if end in _map:
|
||||
return (spec[:-1], _map[end])
|
||||
return (spec, dict(required=True, multivalue=False))
|
||||
|
||||
|
||||
class Param(plugable.ReadOnly):
|
||||
"""
|
||||
A parameter accepted by a `Command`.
|
||||
|
||||
============ ================= ==================
|
||||
Keyword Type Default
|
||||
============ ================= ==================
|
||||
cli_name str defaults to name
|
||||
type ipa_type.Type ipa_type.Unicode()
|
||||
doc str ""
|
||||
required bool True
|
||||
multivalue bool False
|
||||
primary_key bool False
|
||||
normalize callable None
|
||||
default same as type.type None
|
||||
default_from callable None
|
||||
flags frozenset frozenset()
|
||||
============ ================= ==================
|
||||
"""
|
||||
__nones = (None, '', tuple(), [])
|
||||
__defaults = dict(
|
||||
cli_name=None,
|
||||
type=ipa_types.Unicode(),
|
||||
doc='',
|
||||
required=True,
|
||||
multivalue=False,
|
||||
primary_key=False,
|
||||
normalize=None,
|
||||
default=None,
|
||||
default_from=None,
|
||||
flags=frozenset(),
|
||||
rules=tuple(),
|
||||
)
|
||||
|
||||
def __init__(self, name, **override):
|
||||
self.__param_spec = name
|
||||
self.__override = override
|
||||
self.__kw = dict(self.__defaults)
|
||||
if not ('required' in override or 'multivalue' in override):
|
||||
(name, kw_from_spec) = parse_param_spec(name)
|
||||
self.__kw.update(kw_from_spec)
|
||||
self.__kw['cli_name'] = name
|
||||
if not set(self.__kw).issuperset(override):
|
||||
extra = sorted(set(override) - set(self.__kw))
|
||||
raise TypeError(
|
||||
'Param.__init__() takes no such kwargs: %s' % ', '.join(extra)
|
||||
)
|
||||
self.__kw.update(override)
|
||||
self.name = check_name(name)
|
||||
self.cli_name = check_name(self.__kw.get('cli_name', name))
|
||||
self.type = self.__check_isinstance(ipa_types.Type, 'type')
|
||||
self.doc = self.__check_type(str, 'doc')
|
||||
self.required = self.__check_type(bool, 'required')
|
||||
self.multivalue = self.__check_type(bool, 'multivalue')
|
||||
self.default = self.__kw['default']
|
||||
df = self.__kw['default_from']
|
||||
if callable(df) and not isinstance(df, DefaultFrom):
|
||||
df = DefaultFrom(df)
|
||||
self.default_from = check_type(df, DefaultFrom, 'default_from',
|
||||
allow_none=True
|
||||
)
|
||||
self.flags = frozenset(self.__kw['flags'])
|
||||
self.__normalize = self.__kw['normalize']
|
||||
self.rules = self.__check_type(tuple, 'rules')
|
||||
self.all_rules = (self.type.validate,) + self.rules
|
||||
self.primary_key = self.__check_type(bool, 'primary_key')
|
||||
lock(self)
|
||||
|
||||
def ispassword(self):
|
||||
"""
|
||||
Return ``True`` is this Param is a password.
|
||||
"""
|
||||
return 'password' in self.flags
|
||||
|
||||
def __clone__(self, **override):
|
||||
"""
|
||||
Return a new `Param` instance similar to this one.
|
||||
"""
|
||||
kw = dict(self.__kw)
|
||||
kw.update(override)
|
||||
return self.__class__(self.name, **kw)
|
||||
|
||||
def __check_type(self, type_, name, allow_none=False):
|
||||
value = self.__kw[name]
|
||||
return check_type(value, type_, name, allow_none)
|
||||
|
||||
def __check_isinstance(self, type_, name, allow_none=False):
|
||||
value = self.__kw[name]
|
||||
return check_isinstance(value, type_, name, allow_none)
|
||||
|
||||
def __dispatch(self, value, scalar):
|
||||
"""
|
||||
Helper method used by `normalize` and `convert`.
|
||||
"""
|
||||
if value in self.__nones:
|
||||
return
|
||||
if self.multivalue:
|
||||
if type(value) in (tuple, list):
|
||||
return tuple(
|
||||
scalar(v, i) for (i, v) in enumerate(value)
|
||||
)
|
||||
return (scalar(value, 0),) # tuple
|
||||
return scalar(value)
|
||||
|
||||
def __normalize_scalar(self, value, index=None):
|
||||
"""
|
||||
Normalize a scalar value.
|
||||
|
||||
This method is called once with each value in multivalue.
|
||||
"""
|
||||
if not isinstance(value, basestring):
|
||||
return value
|
||||
try:
|
||||
return self.__normalize(value)
|
||||
except StandardError:
|
||||
return value
|
||||
|
||||
def normalize(self, value):
|
||||
"""
|
||||
Normalize ``value`` using normalize callback.
|
||||
|
||||
For example:
|
||||
|
||||
>>> param = Param('telephone',
|
||||
... normalize=lambda value: value.replace('.', '-')
|
||||
... )
|
||||
>>> param.normalize('800.123.4567')
|
||||
'800-123-4567'
|
||||
|
||||
If this `Param` instance does not have a normalize callback,
|
||||
``value`` is returned unchanged.
|
||||
|
||||
If this `Param` instance has a normalize callback and ``value`` is
|
||||
a basestring, the normalize callback is called and its return value
|
||||
is returned.
|
||||
|
||||
If ``value`` is not a basestring, or if an exception is caught
|
||||
when calling the normalize callback, ``value`` is returned unchanged.
|
||||
|
||||
:param value: A proposed value for this parameter.
|
||||
"""
|
||||
if self.__normalize is None:
|
||||
return value
|
||||
return self.__dispatch(value, self.__normalize_scalar)
|
||||
|
||||
def __convert_scalar(self, value, index=None):
|
||||
"""
|
||||
Convert a scalar value.
|
||||
|
||||
This method is called once with each value in multivalue.
|
||||
"""
|
||||
if value in self.__nones:
|
||||
return
|
||||
converted = self.type(value)
|
||||
if converted is None:
|
||||
raise errors.ConversionError(
|
||||
self.name, value, self.type, index=index
|
||||
)
|
||||
return converted
|
||||
|
||||
def convert(self, value):
|
||||
"""
|
||||
Convert/coerce ``value`` to Python type for this `Param`.
|
||||
|
||||
For example:
|
||||
|
||||
>>> param = Param('an_int', type=ipa_types.Int())
|
||||
>>> param.convert(7.2)
|
||||
7
|
||||
>>> param.convert(" 7 ")
|
||||
7
|
||||
|
||||
If ``value`` can not be converted, ConversionError is raised, which
|
||||
is as subclass of ValidationError.
|
||||
|
||||
If ``value`` is None, conversion is not attempted and None is
|
||||
returned.
|
||||
|
||||
:param value: A proposed value for this parameter.
|
||||
"""
|
||||
return self.__dispatch(value, self.__convert_scalar)
|
||||
|
||||
def __validate_scalar(self, value, index=None):
|
||||
"""
|
||||
Validate a scalar value.
|
||||
|
||||
This method is called once with each value in multivalue.
|
||||
"""
|
||||
if type(value) is not self.type.type:
|
||||
raise_TypeError(value, self.type.type, 'value')
|
||||
for rule in self.rules:
|
||||
error = rule(value)
|
||||
if error is not None:
|
||||
raise errors.RuleError(
|
||||
self.name, value, error, rule, index=index
|
||||
)
|
||||
|
||||
def validate(self, value):
|
||||
"""
|
||||
Check validity of a value.
|
||||
|
||||
Each validation rule is called in turn and if any returns and error,
|
||||
RuleError is raised, which is a subclass of ValidationError.
|
||||
|
||||
:param value: A proposed value for this parameter.
|
||||
"""
|
||||
if value is None:
|
||||
if self.required:
|
||||
raise errors.RequirementError(self.name)
|
||||
return
|
||||
if self.multivalue:
|
||||
if type(value) is not tuple:
|
||||
raise_TypeError(value, tuple, 'value')
|
||||
for (i, v) in enumerate(value):
|
||||
self.__validate_scalar(v, i)
|
||||
else:
|
||||
self.__validate_scalar(value)
|
||||
|
||||
def get_default(self, **kw):
|
||||
"""
|
||||
Return a default value for this parameter.
|
||||
|
||||
If this `Param` instance does not have a default_from() callback, this
|
||||
method always returns the static Param.default instance attribute.
|
||||
|
||||
On the other hand, if this `Param` instance has a default_from()
|
||||
callback, the callback is called and its return value is returned
|
||||
(assuming that value is not None).
|
||||
|
||||
If the default_from() callback returns None, or if an exception is
|
||||
caught when calling the default_from() callback, the static
|
||||
Param.default instance attribute is returned.
|
||||
|
||||
:param kw: Optional keyword arguments to pass to default_from().
|
||||
"""
|
||||
if self.default_from is not None:
|
||||
default = self.default_from(**kw)
|
||||
if default is not None:
|
||||
try:
|
||||
return self.convert(self.normalize(default))
|
||||
except errors.ValidationError:
|
||||
return None
|
||||
return self.default
|
||||
|
||||
def get_values(self):
|
||||
"""
|
||||
Return a tuple of possible values.
|
||||
|
||||
For enumerable types, a tuple containing the possible values is
|
||||
returned. For all other types, an empty tuple is returned.
|
||||
"""
|
||||
if self.type.name in ('Enum', 'CallbackEnum'):
|
||||
return self.type.values
|
||||
return tuple()
|
||||
|
||||
def __call__(self, value, **kw):
|
||||
if value in self.__nones:
|
||||
value = self.get_default(**kw)
|
||||
else:
|
||||
value = self.convert(self.normalize(value))
|
||||
self.validate(value)
|
||||
return value
|
||||
|
||||
def __repr__(self):
|
||||
"""
|
||||
Return an expresion that could construct this `Param` instance.
|
||||
"""
|
||||
return make_repr(
|
||||
self.__class__.__name__,
|
||||
self.__param_spec,
|
||||
**self.__override
|
||||
)
|
||||
|
||||
|
||||
def create_param(spec):
|
||||
"""
|
||||
Create a `Param` instance from a param spec.
|
||||
|
||||
If ``spec`` is a `Param` instance, ``spec`` is returned unchanged.
|
||||
|
||||
If ``spec`` is an str instance, then ``spec`` is parsed and an
|
||||
appropriate `Param` instance is created and returned.
|
||||
|
||||
See `parse_param_spec` for the definition of the spec syntax.
|
||||
|
||||
:param spec: A spec string or a `Param` instance.
|
||||
"""
|
||||
if type(spec) is Param:
|
||||
return spec
|
||||
if type(spec) is not str:
|
||||
raise TypeError(
|
||||
'create_param() takes %r or %r; got %r' % (str, Param, spec)
|
||||
)
|
||||
return Param(spec)
|
||||
|
||||
|
||||
class Command(plugable.Plugin):
|
||||
"""
|
||||
A public IPA atomic operation.
|
||||
@ -810,7 +363,7 @@ class LocalOrRemote(Command):
|
||||
"""
|
||||
|
||||
takes_options = (
|
||||
Param('server?', type=ipa_types.Bool(), default=False,
|
||||
parameters.Flag('server?',
|
||||
doc='Forward to server instead of running locally',
|
||||
),
|
||||
)
|
||||
@ -1064,7 +617,7 @@ class Property(Attribute):
|
||||
'type',
|
||||
)).union(Attribute.__public__)
|
||||
|
||||
type = ipa_types.Unicode()
|
||||
type = parameters.Str
|
||||
required = False
|
||||
multivalue = False
|
||||
default = None
|
||||
|
@ -70,420 +70,6 @@ def test_is_rule():
|
||||
assert not is_rule(call(None))
|
||||
|
||||
|
||||
class test_DefaultFrom(ClassChecker):
|
||||
"""
|
||||
Test the `ipalib.frontend.DefaultFrom` class.
|
||||
"""
|
||||
_cls = frontend.DefaultFrom
|
||||
|
||||
def test_class(self):
|
||||
"""
|
||||
Test the `ipalib.frontend.DefaultFrom` class.
|
||||
"""
|
||||
assert self.cls.__bases__ == (plugable.ReadOnly,)
|
||||
|
||||
def test_init(self):
|
||||
"""
|
||||
Test the `ipalib.frontend.DefaultFrom.__init__` method.
|
||||
"""
|
||||
def callback(*args):
|
||||
return args
|
||||
keys = ('givenname', 'sn')
|
||||
o = self.cls(callback, *keys)
|
||||
assert read_only(o, 'callback') is callback
|
||||
assert read_only(o, 'keys') == keys
|
||||
lam = lambda first, last: first[0] + last
|
||||
o = self.cls(lam)
|
||||
assert read_only(o, 'keys') == ('first', 'last')
|
||||
|
||||
def test_call(self):
|
||||
"""
|
||||
Test the `ipalib.frontend.DefaultFrom.__call__` method.
|
||||
"""
|
||||
def callback(givenname, sn):
|
||||
return givenname[0] + sn[0]
|
||||
keys = ('givenname', 'sn')
|
||||
o = self.cls(callback, *keys)
|
||||
kw = dict(
|
||||
givenname='John',
|
||||
sn='Public',
|
||||
hello='world',
|
||||
)
|
||||
assert o(**kw) == 'JP'
|
||||
assert o() is None
|
||||
for key in ('givenname', 'sn'):
|
||||
kw_copy = dict(kw)
|
||||
del kw_copy[key]
|
||||
assert o(**kw_copy) is None
|
||||
|
||||
# Test using implied keys:
|
||||
o = self.cls(lambda first, last: first[0] + last)
|
||||
assert o(first='john', last='doe') == 'jdoe'
|
||||
assert o(first='', last='doe') is None
|
||||
assert o(one='john', two='doe') is None
|
||||
|
||||
# Test that co_varnames slice is used:
|
||||
def callback2(first, last):
|
||||
letter = first[0]
|
||||
return letter + last
|
||||
o = self.cls(callback2)
|
||||
assert o.keys == ('first', 'last')
|
||||
assert o(first='john', last='doe') == 'jdoe'
|
||||
|
||||
|
||||
def test_parse_param_spec():
|
||||
"""
|
||||
Test the `ipalib.frontend.parse_param_spec` function.
|
||||
"""
|
||||
f = frontend.parse_param_spec
|
||||
|
||||
assert f('name') == ('name', dict(required=True, multivalue=False))
|
||||
assert f('name?') == ('name', dict(required=False, multivalue=False))
|
||||
assert f('name*') == ('name', dict(required=False, multivalue=True))
|
||||
assert f('name+') == ('name', dict(required=True, multivalue=True))
|
||||
|
||||
|
||||
class test_Param(ClassChecker):
|
||||
"""
|
||||
Test the `ipalib.frontend.Param` class.
|
||||
"""
|
||||
_cls = frontend.Param
|
||||
|
||||
def test_class(self):
|
||||
"""
|
||||
Test the `ipalib.frontend.Param` class.
|
||||
"""
|
||||
assert self.cls.__bases__ == (plugable.ReadOnly,)
|
||||
|
||||
def test_init(self):
|
||||
"""
|
||||
Test the `ipalib.frontend.Param.__init__` method.
|
||||
"""
|
||||
name = 'sn'
|
||||
o = self.cls(name)
|
||||
assert o.__islocked__() is True
|
||||
|
||||
# Test default values
|
||||
assert read_only(o, 'name') is name
|
||||
assert read_only(o, 'cli_name') is name
|
||||
assert isinstance(read_only(o, 'type'), ipa_types.Unicode)
|
||||
assert read_only(o, 'doc') == ''
|
||||
assert read_only(o, 'required') is True
|
||||
assert read_only(o, 'multivalue') is False
|
||||
assert read_only(o, 'default') is None
|
||||
assert read_only(o, 'default_from') is None
|
||||
assert read_only(o, 'flags') == frozenset()
|
||||
assert read_only(o, 'rules') == tuple()
|
||||
assert len(read_only(o, 'all_rules')) == 1
|
||||
assert read_only(o, 'primary_key') is False
|
||||
|
||||
# Test all kw args:
|
||||
t = ipa_types.Int()
|
||||
assert self.cls(name, cli_name='last').cli_name == 'last'
|
||||
assert self.cls(name, type=t).type is t
|
||||
assert self.cls(name, doc='the doc').doc == 'the doc'
|
||||
assert self.cls(name, required=False).required is False
|
||||
assert self.cls(name, multivalue=True).multivalue is True
|
||||
assert self.cls(name, default=u'Hello').default == u'Hello'
|
||||
df = frontend.DefaultFrom(lambda f, l: f + l,
|
||||
'first', 'last',
|
||||
)
|
||||
lam = lambda first, last: first + last
|
||||
for cb in (df, lam):
|
||||
o = self.cls(name, default_from=cb)
|
||||
assert type(o.default_from) is frontend.DefaultFrom
|
||||
assert o.default_from.keys == ('first', 'last')
|
||||
assert o.default_from.callback('butt', 'erfly') == 'butterfly'
|
||||
assert self.cls(name, flags=('one', 'two', 'three')).flags == \
|
||||
frozenset(['one', 'two', 'three'])
|
||||
rules = (lambda whatever: 'Not okay!',)
|
||||
o = self.cls(name, rules=rules)
|
||||
assert o.rules is rules
|
||||
assert o.all_rules[1:] == rules
|
||||
assert self.cls(name, primary_key=True).primary_key is True
|
||||
|
||||
# Test default type_:
|
||||
o = self.cls(name)
|
||||
assert isinstance(o.type, ipa_types.Unicode)
|
||||
|
||||
# Test param spec parsing:
|
||||
o = self.cls('name?')
|
||||
assert o.name == 'name'
|
||||
assert o.required is False
|
||||
assert o.multivalue is False
|
||||
|
||||
o = self.cls('name*')
|
||||
assert o.name == 'name'
|
||||
assert o.required is False
|
||||
assert o.multivalue is True
|
||||
|
||||
o = self.cls('name+')
|
||||
assert o.name == 'name'
|
||||
assert o.required is True
|
||||
assert o.multivalue is True
|
||||
|
||||
e = raises(TypeError, self.cls, name, whatever=True, another=False)
|
||||
assert str(e) == \
|
||||
'Param.__init__() takes no such kwargs: another, whatever'
|
||||
|
||||
def test_ispassword(self):
|
||||
"""
|
||||
Test the `ipalib.frontend.Param.ispassword` method.
|
||||
"""
|
||||
name = 'userpassword'
|
||||
okay = 'password'
|
||||
nope = ['', 'pass', 'word', 'passwd']
|
||||
for flag in nope:
|
||||
o = self.cls(name, flags=[flag])
|
||||
assert o.ispassword() is False
|
||||
o = self.cls(name, flags=[flag, okay])
|
||||
assert o.ispassword() is True
|
||||
assert self.cls(name).ispassword() is False
|
||||
assert self.cls(name, flags=[okay]).ispassword() is True
|
||||
assert self.cls(name, flags=[okay]+nope).ispassword() is True
|
||||
|
||||
def test_clone(self):
|
||||
"""
|
||||
Test the `ipalib.frontend.Param.__clone__` method.
|
||||
"""
|
||||
def compare(o, kw):
|
||||
for (k, v) in kw.iteritems():
|
||||
assert getattr(o, k) == v, (k, v, getattr(o, k))
|
||||
default = dict(
|
||||
required=False,
|
||||
multivalue=False,
|
||||
default=None,
|
||||
default_from=None,
|
||||
rules=tuple(),
|
||||
)
|
||||
name = 'hair_color?'
|
||||
type_ = ipa_types.Int()
|
||||
o = self.cls(name, type=type_)
|
||||
compare(o, default)
|
||||
|
||||
override = dict(multivalue=True, default=42)
|
||||
d = dict(default)
|
||||
d.update(override)
|
||||
clone = o.__clone__(**override)
|
||||
assert clone.name == 'hair_color'
|
||||
assert clone.type is o.type
|
||||
compare(clone, d)
|
||||
|
||||
def test_convert(self):
|
||||
"""
|
||||
Test the `ipalib.frontend.Param.convert` method.
|
||||
"""
|
||||
name = 'some_number'
|
||||
type_ = ipa_types.Int()
|
||||
okay = (7, 7L, 7.0, ' 7 ')
|
||||
fail = ('7.0', '7L', 'whatever', object)
|
||||
none = (None, '', u'', tuple(), [])
|
||||
|
||||
# Scenario 1: multivalue=False
|
||||
o = self.cls(name, type=type_)
|
||||
for n in none:
|
||||
assert o.convert(n) is None
|
||||
for value in okay:
|
||||
new = o.convert(value)
|
||||
assert new == 7
|
||||
assert type(new) is int
|
||||
for value in fail:
|
||||
e = raises(errors.ConversionError, o.convert, value)
|
||||
assert e.name is name
|
||||
assert e.value is value
|
||||
assert e.error is type_.conversion_error
|
||||
assert e.index is None
|
||||
|
||||
# Scenario 2: multivalue=True
|
||||
o = self.cls(name, type=type_, multivalue=True)
|
||||
for n in none:
|
||||
assert o.convert(n) is None
|
||||
for value in okay:
|
||||
assert o.convert((value,)) == (7,)
|
||||
assert o.convert([value]) == (7,)
|
||||
assert o.convert(okay) == tuple(int(v) for v in okay)
|
||||
cnt = 5
|
||||
for value in fail:
|
||||
for i in xrange(cnt):
|
||||
others = list(7 for x in xrange(cnt))
|
||||
others[i] = value
|
||||
for v in [tuple(others), list(others)]:
|
||||
e = raises(errors.ConversionError, o.convert, v)
|
||||
assert e.name is name
|
||||
assert e.value is value
|
||||
assert e.error is type_.conversion_error
|
||||
assert e.index == i
|
||||
|
||||
def test_normalize(self):
|
||||
"""
|
||||
Test the `ipalib.frontend.Param.normalize` method.
|
||||
"""
|
||||
name = 'sn'
|
||||
callback = lambda value: value.lower()
|
||||
values = (None, u'Hello', (u'Hello',), 'hello', ['hello'])
|
||||
none = (None, '', u'', tuple(), [])
|
||||
|
||||
# Scenario 1: multivalue=False, normalize=None
|
||||
o = self.cls(name)
|
||||
for v in values:
|
||||
# When normalize=None, value is returned, no type checking:
|
||||
assert o.normalize(v) is v
|
||||
|
||||
# Scenario 2: multivalue=False, normalize=callback
|
||||
o = self.cls(name, normalize=callback)
|
||||
for v in (u'Hello', u'hello', 'Hello'): # Okay
|
||||
assert o.normalize(v) == 'hello'
|
||||
for v in [None, 42, (u'Hello',)]: # Not basestring
|
||||
assert o.normalize(v) is v
|
||||
for n in none:
|
||||
assert o.normalize(n) is None
|
||||
|
||||
# Scenario 3: multivalue=True, normalize=None
|
||||
o = self.cls(name, multivalue=True)
|
||||
for v in values:
|
||||
# When normalize=None, value is returned, no type checking:
|
||||
assert o.normalize(v) is v
|
||||
|
||||
# Scenario 4: multivalue=True, normalize=callback
|
||||
o = self.cls(name, multivalue=True, normalize=callback)
|
||||
assert o.normalize([]) is None
|
||||
assert o.normalize(tuple()) is None
|
||||
for value in [(u'Hello',), (u'hello',), 'Hello', ['Hello']]: # Okay
|
||||
assert o.normalize(value) == (u'hello',)
|
||||
fail = 42 # Not basestring
|
||||
for v in [[fail], (u'hello', fail)]: # Non basestring member
|
||||
assert o.normalize(v) == tuple(v)
|
||||
for n in none:
|
||||
assert o.normalize(n) is None
|
||||
|
||||
def test_validate(self):
|
||||
"""
|
||||
Test the `ipalib.frontend.Param.validate` method.
|
||||
"""
|
||||
name = 'sn'
|
||||
type_ = ipa_types.Unicode()
|
||||
def case_rule(value):
|
||||
if not value.islower():
|
||||
return 'Must be lower case'
|
||||
my_rules = (case_rule,)
|
||||
okay = u'whatever'
|
||||
fail_case = u'Whatever'
|
||||
fail_type = 'whatever'
|
||||
|
||||
# Scenario 1: multivalue=False
|
||||
o = self.cls(name, type=type_, rules=my_rules)
|
||||
assert o.rules == my_rules
|
||||
assert o.all_rules == (type_.validate, case_rule)
|
||||
o.validate(okay)
|
||||
e = raises(errors.RuleError, o.validate, fail_case)
|
||||
assert e.name is name
|
||||
assert e.value is fail_case
|
||||
assert e.error == 'Must be lower case'
|
||||
assert e.rule is case_rule
|
||||
assert e.index is None
|
||||
check_TypeError(fail_type, unicode, 'value', o.validate, fail_type)
|
||||
|
||||
## Scenario 2: multivalue=True
|
||||
o = self.cls(name, type=type_, multivalue=True, rules=my_rules)
|
||||
o.validate((okay,))
|
||||
cnt = 5
|
||||
for i in xrange(cnt):
|
||||
others = list(okay for x in xrange(cnt))
|
||||
others[i] = fail_case
|
||||
value = tuple(others)
|
||||
e = raises(errors.RuleError, o.validate, value)
|
||||
assert e.name is name
|
||||
assert e.value is fail_case
|
||||
assert e.error == 'Must be lower case'
|
||||
assert e.rule is case_rule
|
||||
assert e.index == i
|
||||
for not_tuple in (okay, [okay]):
|
||||
check_TypeError(not_tuple, tuple, 'value', o.validate, not_tuple)
|
||||
for has_str in [(fail_type,), (okay, fail_type)]:
|
||||
check_TypeError(fail_type, unicode, 'value', o.validate, has_str)
|
||||
|
||||
def test_get_default(self):
|
||||
"""
|
||||
Test the `ipalib.frontend.Param.get_default` method.
|
||||
"""
|
||||
name = 'greeting'
|
||||
default = u'Hello, world!'
|
||||
default_from = frontend.DefaultFrom(
|
||||
lambda first, last: u'Hello, %s %s!' % (first, last),
|
||||
'first', 'last'
|
||||
)
|
||||
|
||||
# Scenario 1: multivalue=False
|
||||
o = self.cls(name,
|
||||
default=default,
|
||||
default_from=default_from,
|
||||
)
|
||||
assert o.default is default
|
||||
assert o.default_from is default_from
|
||||
assert o.get_default() == default
|
||||
assert o.get_default(first='John', last='Doe') == 'Hello, John Doe!'
|
||||
|
||||
# Scenario 2: multivalue=True
|
||||
default = (default,)
|
||||
o = self.cls(name,
|
||||
default=default,
|
||||
default_from=default_from,
|
||||
multivalue=True,
|
||||
)
|
||||
assert o.default is default
|
||||
assert o.default_from is default_from
|
||||
assert o.get_default() == default
|
||||
assert o.get_default(first='John', last='Doe') == ('Hello, John Doe!',)
|
||||
|
||||
def test_get_value(self):
|
||||
"""
|
||||
Test the `ipalib.frontend.Param.get_values` method.
|
||||
"""
|
||||
name = 'status'
|
||||
values = (u'Active', u'Inactive')
|
||||
o = self.cls(name, type=ipa_types.Unicode())
|
||||
assert o.get_values() == tuple()
|
||||
o = self.cls(name, type=ipa_types.Enum(*values))
|
||||
assert o.get_values() == values
|
||||
|
||||
def test_repr(self):
|
||||
"""
|
||||
Test the `ipalib.frontend.Param.__repr__` method.
|
||||
"""
|
||||
for name in ['name', 'name?', 'name*', 'name+']:
|
||||
o = self.cls(name)
|
||||
assert repr(o) == 'Param(%r)' % name
|
||||
o = self.cls('name', required=False)
|
||||
assert repr(o) == "Param('name', required=False)"
|
||||
o = self.cls('name', multivalue=True)
|
||||
assert repr(o) == "Param('name', multivalue=True)"
|
||||
|
||||
|
||||
def test_create_param():
|
||||
"""
|
||||
Test the `ipalib.frontend.create_param` function.
|
||||
"""
|
||||
f = frontend.create_param
|
||||
for name in ['arg', 'arg?', 'arg*', 'arg+']:
|
||||
o = f(name)
|
||||
assert type(o) is frontend.Param
|
||||
assert type(o.type) is ipa_types.Unicode
|
||||
assert o.name == 'arg'
|
||||
assert f(o) is o
|
||||
o = f('arg')
|
||||
assert o.required is True
|
||||
assert o.multivalue is False
|
||||
o = f('arg?')
|
||||
assert o.required is False
|
||||
assert o.multivalue is False
|
||||
o = f('arg*')
|
||||
assert o.required is False
|
||||
assert o.multivalue is True
|
||||
o = f('arg+')
|
||||
assert o.required is True
|
||||
assert o.multivalue is True
|
||||
|
||||
|
||||
class test_Command(ClassChecker):
|
||||
"""
|
||||
Test the `ipalib.frontend.Command` class.
|
||||
|
Loading…
Reference in New Issue
Block a user