Parse comma-separated lists of values in all parameter types. This can be enabled for a specific parameter by setting the "csv" option to True.

Remove "List" parameter type and replace all occurences of it with appropriate
multi-valued parameter ("Str" in most cases) with csv enabled.

Add new parameter type "Any", capable of holding values of any type. This is
needed by the "batch" command, as "Str" is not suitable type for the "methods"
parameter.

ticket 2007
This commit is contained in:
Jan Cholasta
2011-11-21 10:50:27 -05:00
committed by Martin Kosek
parent 2ac9d4816a
commit 135ccf89de
18 changed files with 540 additions and 542 deletions

712
API.txt

File diff suppressed because it is too large Load Diff

5
TODO
View File

@@ -63,11 +63,6 @@ Command Line interface
own replacement for optparse. The isn't way to make optparse deal with the own replacement for optparse. The isn't way to make optparse deal with the
global options the way Jason would like, so that's another motivation. global options the way Jason would like, so that's another motivation.
* All "comma-separated list of..." parameters should really be changed to
multivalue and have a flag that tells the CLI whether a multivalue should
be parsed as comma-separated. The List type currently satisfy this, but it
would be nice to have a comma-separated multivalue of any type.
* Add a File param type so an argument may be read from a file. This is * Add a File param type so an argument may be read from a file. This is
needed for cert-request to pass along the CSR. needed for cert-request to pass along the CSR.

View File

@@ -79,4 +79,4 @@ IPA_DATA_VERSION=20100614120000
# # # #
######################################################## ########################################################
IPA_API_VERSION_MAJOR=2 IPA_API_VERSION_MAJOR=2
IPA_API_VERSION_MINOR=16 IPA_API_VERSION_MINOR=17

View File

@@ -878,7 +878,7 @@ from backend import Backend
from frontend import Command, LocalOrRemote, Updater from frontend import Command, LocalOrRemote, Updater
from frontend import Object, Method, Property from frontend import Object, Method, Property
from crud import Create, Retrieve, Update, Delete, Search from crud import Create, Retrieve, Update, Delete, Search
from parameters import DefaultFrom, Bool, Flag, Int, Float, Bytes, Str, IA5Str, Password,List from parameters import DefaultFrom, Bool, Flag, Int, Float, Bytes, Str, IA5Str, Password
from parameters import BytesEnum, StrEnum, AccessTime, File from parameters import BytesEnum, StrEnum, AccessTime, File
from errors import SkipPluginModule from errors import SkipPluginModule
from text import _, ngettext, GettextFactory, NGettextFactory from text import _, ngettext, GettextFactory, NGettextFactory

View File

@@ -381,6 +381,9 @@ class Param(ReadOnly):
('hint', (str, Gettext), None), ('hint', (str, Gettext), None),
('alwaysask', bool, False), ('alwaysask', bool, False),
('sortorder', int, 2), # see finalize() ('sortorder', int, 2), # see finalize()
('csv', bool, False),
('csv_separator', str, ','),
('csv_skipspace', bool, True),
# The 'default' kwarg gets appended in Param.__init__(): # The 'default' kwarg gets appended in Param.__init__():
# ('default', self.type, None), # ('default', self.type, None),
@@ -493,6 +496,10 @@ class Param(ReadOnly):
) )
) )
# Check that if csv is set, multivalue is set too
if self.csv and not self.multivalue:
raise ValueError('%s: cannot have csv without multivalue' % self.nice)
# Check that all the rules are callable # Check that all the rules are callable
self.class_rules = tuple(class_rules) self.class_rules = tuple(class_rules)
self.rules = rules self.rules = rules
@@ -663,6 +670,23 @@ class Param(ReadOnly):
kw.update(overrides) kw.update(overrides)
return klass(name, *self.rules, **kw) return klass(name, *self.rules, **kw)
# The following 2 functions were taken from the Python
# documentation at http://docs.python.org/library/csv.html
def __utf_8_encoder(self, unicode_csv_data):
for line in unicode_csv_data:
yield line.encode('utf-8')
def __unicode_csv_reader(self, unicode_csv_data, dialect=csv.excel, **kwargs):
# csv.py doesn't do Unicode; encode temporarily as UTF-8:
csv_reader = csv.reader(self.__utf_8_encoder(unicode_csv_data),
dialect=dialect,
delimiter=self.csv_separator, escapechar='\\',
skipinitialspace=self.csv_skipspace,
**kwargs)
for row in csv_reader:
# decode UTF-8 back to Unicode, cell by cell:
yield [unicode(cell, 'utf-8') for cell in row]
def normalize(self, value): def normalize(self, value):
""" """
Normalize ``value`` using normalizer callback. Normalize ``value`` using normalizer callback.
@@ -686,15 +710,20 @@ class Param(ReadOnly):
:param value: A proposed value for this parameter. :param value: A proposed value for this parameter.
""" """
if self.multivalue:
if self.csv and isinstance(value, basestring):
csvreader = self.__unicode_csv_reader([unicode(value)])
value = tuple(csvreader.next()) #pylint: disable=E1101
elif type(value) not in (tuple, list):
value = (value,)
if self.normalizer is None: if self.normalizer is None:
return value return value
if self.multivalue: if self.multivalue:
if type(value) in (tuple, list): return tuple(
return tuple( self._normalize_scalar(v) for v in value
self._normalize_scalar(v) for v in value )
) else:
return (self._normalize_scalar(value),) # Return a tuple return self._normalize_scalar(value)
return self._normalize_scalar(value)
def _normalize_scalar(self, value): def _normalize_scalar(self, value):
""" """
@@ -1525,47 +1554,12 @@ class StrEnum(Enum):
type = unicode type = unicode
class List(Param): class Any(Param):
""" """
Base class for parameters as a list of values. The input is a delimited A parameter capable of holding values of any type. For internal use only.
string.
""" """
type = tuple
kwargs = Param.kwargs + ( type = object
('separator', str, ','),
('skipspace', bool, True),
)
# The following 2 functions were taken from the Python
# documentation at http://docs.python.org/library/csv.html
def __utf_8_encoder(self, unicode_csv_data):
for line in unicode_csv_data:
yield line.encode('utf-8')
def __unicode_csv_reader(self, unicode_csv_data, dialect=csv.excel, **kwargs):
# csv.py doesn't do Unicode; encode temporarily as UTF-8:
csv_reader = csv.reader(self.__utf_8_encoder(unicode_csv_data),
dialect=dialect,
delimiter=self.separator, escapechar='\\',
skipinitialspace=self.skipspace,
**kwargs)
for row in csv_reader:
# decode UTF-8 back to Unicode, cell by cell:
yield [unicode(cell, 'utf-8') for cell in row]
def __init__(self, name, *rules, **kw):
kw['multivalue'] = True
super(List, self).__init__(name, *rules, **kw)
def normalize(self, value):
if value and not type(value) in (list, tuple):
reader = self.__unicode_csv_reader([value])
value = []
for row in reader:
value = value + row
value = tuple(value)
return super(List, self).normalize(value)
def _convert_scalar(self, value, index=None): def _convert_scalar(self, value, index=None):
return value return value

View File

@@ -120,7 +120,7 @@ targetattr REPLACES the current attributes, it does not add to them.
from ipalib import api, crud, errors from ipalib import api, crud, errors
from ipalib import Object, Command from ipalib import Object, Command
from ipalib import Flag, Int, List, Str, StrEnum from ipalib import Flag, Int, Str, StrEnum
from ipalib.aci import ACI from ipalib.aci import ACI
from ipalib import output from ipalib import output
from ipalib import _, ngettext from ipalib import _, ngettext
@@ -430,18 +430,20 @@ class aci(Object):
doc=_('User group ACI grants access to'), doc=_('User group ACI grants access to'),
flags=('virtual_attribute',), flags=('virtual_attribute',),
), ),
List('permissions', validate_permissions, Str('permissions+', validate_permissions,
cli_name='permissions', cli_name='permissions',
label=_('Permissions'), label=_('Permissions'),
doc=_('comma-separated list of permissions to grant' \ doc=_('comma-separated list of permissions to grant' \
'(read, write, add, delete, all)'), '(read, write, add, delete, all)'),
csv=True,
normalizer=_normalize_permissions, normalizer=_normalize_permissions,
flags=('virtual_attribute',), flags=('virtual_attribute',),
), ),
List('attrs?', Str('attrs*',
cli_name='attrs', cli_name='attrs',
label=_('Attributes'), label=_('Attributes'),
doc=_('Comma-separated list of attributes'), doc=_('Comma-separated list of attributes'),
csv=True,
flags=('virtual_attribute',), flags=('virtual_attribute',),
), ),
StrEnum('type?', StrEnum('type?',

View File

@@ -109,18 +109,18 @@ INCLUDE_RE = 'automemberinclusiveregex'
EXCLUDE_RE = 'automemberexclusiveregex' EXCLUDE_RE = 'automemberexclusiveregex'
regex_attrs = ( regex_attrs = (
List('automemberinclusiveregex?', Str('automemberinclusiveregex*',
cli_name='inclusive_regex', cli_name='inclusive_regex',
label=_('Inclusive Regex'), label=_('Inclusive Regex'),
doc=_('Inclusive Regex'), doc=_('Inclusive Regex'),
multivalue=True, csv=True,
alwaysask=True, alwaysask=True,
), ),
List('automemberexclusiveregex?', Str('automemberexclusiveregex*',
cli_name='exclusive_regex', cli_name='exclusive_regex',
label=_('Exclusive Regex'), label=_('Exclusive Regex'),
doc=_('Exclusive Regex'), doc=_('Exclusive Regex'),
multivalue=True, csv=True,
alwaysask=True, alwaysask=True,
), ),
Str('key', Str('key',

View File

@@ -27,7 +27,7 @@ from copy import deepcopy
from ipalib import api, crud, errors from ipalib import api, crud, errors
from ipalib import Method, Object, Command from ipalib import Method, Object, Command
from ipalib import Flag, Int, List, Str from ipalib import Flag, Int, Str
from ipalib.base import NameSpace from ipalib.base import NameSpace
from ipalib.cli import to_cli, from_cli from ipalib.cli import to_cli, from_cli
from ipalib import output from ipalib import output
@@ -1298,8 +1298,9 @@ class LDAPModMember(LDAPQuery):
ldap_obj = self.api.Object[ldap_obj_name] ldap_obj = self.api.Object[ldap_obj_name]
name = to_cli(ldap_obj_name) name = to_cli(ldap_obj_name)
doc = self.member_param_doc % ldap_obj.object_name_plural doc = self.member_param_doc % ldap_obj.object_name_plural
yield List('%s?' % name, cli_name='%ss' % name, doc=doc, yield Str('%s*' % name, cli_name='%ss' % name, doc=doc,
label=_('member %s') % ldap_obj.object_name, alwaysask=True) label=_('member %s') % ldap_obj.object_name,
csv=True, alwaysask=True)
def get_member_dns(self, **options): def get_member_dns(self, **options):
dns = {} dns = {}
@@ -1593,18 +1594,18 @@ class LDAPSearch(BaseLDAPCommand, crud.Search):
ldap_obj.object_name_plural ldap_obj.object_name_plural
) )
name = '%s%s' % (relationship[1], to_cli(ldap_obj_name)) name = '%s%s' % (relationship[1], to_cli(ldap_obj_name))
yield List( yield Str(
'%s?' % name, cli_name='%ss' % name, doc=doc, '%s*' % name, cli_name='%ss' % name, doc=doc,
label=ldap_obj.object_name label=ldap_obj.object_name, csv=True
) )
doc = self.member_param_excl_doc % ( doc = self.member_param_excl_doc % (
self.obj.object_name_plural, relationship[0].lower(), self.obj.object_name_plural, relationship[0].lower(),
ldap_obj.object_name_plural ldap_obj.object_name_plural
) )
name = '%s%s' % (relationship[2], to_cli(ldap_obj_name)) name = '%s%s' % (relationship[2], to_cli(ldap_obj_name))
yield List( yield Str(
'%s?' % name, cli_name='%ss' % name, doc=doc, '%s*' % name, cli_name='%ss' % name, doc=doc,
label=ldap_obj.object_name label=ldap_obj.object_name, csv=True
) )
def get_member_filter(self, ldap, **options): def get_member_filter(self, ldap, **options):
@@ -1784,8 +1785,9 @@ class LDAPModReverseMember(LDAPQuery):
ldap_obj = self.api.Object[ldap_obj_name] ldap_obj = self.api.Object[ldap_obj_name]
name = to_cli(ldap_obj_name) name = to_cli(ldap_obj_name)
doc = self.reverse_param_doc % ldap_obj.object_name_plural doc = self.reverse_param_doc % ldap_obj.object_name_plural
yield List('%s?' % name, cli_name='%ss' % name, doc=doc, yield Str('%s*' % name, cli_name='%ss' % name, doc=doc,
label=ldap_obj.object_name, alwaysask=True) label=ldap_obj.object_name, csv=True,
alwaysask=True)
class LDAPAddReverseMember(LDAPModReverseMember): class LDAPAddReverseMember(LDAPModReverseMember):

View File

@@ -47,7 +47,7 @@ And then a nested response for each IPA command method sent in the request
from ipalib import api, errors from ipalib import api, errors
from ipalib import Command from ipalib import Command
from ipalib import Str, List from ipalib.parameters import Str, Any
from ipalib.output import Output from ipalib.output import Output
from ipalib import output from ipalib import output
from ipalib.text import _ from ipalib.text import _
@@ -58,10 +58,10 @@ class batch(Command):
NO_CLI = True NO_CLI = True
takes_args = ( takes_args = (
List('methods?', Any('methods*',
doc=_('Nested Methods to execute'), doc=_('Nested Methods to execute'),
), ),
) )
take_options = ( take_options = (
Str('version', Str('version',
@@ -71,7 +71,7 @@ class batch(Command):
flags=['no_option', 'no_output'], flags=['no_option', 'no_output'],
default=API_VERSION, default=API_VERSION,
autofill=True, autofill=True,
) ),
) )
has_output = ( has_output = (

View File

@@ -148,15 +148,17 @@ class config(LDAPObject):
doc=_('Base for certificate subjects (OU=Test,O=Example)'), doc=_('Base for certificate subjects (OU=Test,O=Example)'),
flags=['no_update'], flags=['no_update'],
), ),
List('ipagroupobjectclasses?', Str('ipagroupobjectclasses*',
cli_name='groupobjectclasses', cli_name='groupobjectclasses',
label=_('Default group objectclasses'), label=_('Default group objectclasses'),
doc=_('Default group objectclasses (comma-separated list)'), doc=_('Default group objectclasses (comma-separated list)'),
csv=True,
), ),
List('ipauserobjectclasses?', Str('ipauserobjectclasses*',
cli_name='userobjectclasses', cli_name='userobjectclasses',
label=_('Default user objectclasses'), label=_('Default user objectclasses'),
doc=_('Default user objectclasses (comma-separated list)'), doc=_('Default user objectclasses (comma-separated list)'),
csv=True,
), ),
Int('ipapwdexpadvnotify?', Int('ipapwdexpadvnotify?',
cli_name='pwdexpnotify', cli_name='pwdexpnotify',

View File

@@ -19,7 +19,7 @@
import copy import copy
from ipalib import api, _, ngettext from ipalib import api, _, ngettext
from ipalib import Flag, Str, List from ipalib import Flag, Str
from ipalib.request import context from ipalib.request import context
from ipalib import api, crud, errors from ipalib import api, crud, errors
from ipalib import output from ipalib import output
@@ -110,16 +110,18 @@ class delegation(Object):
doc=_('Delegation name'), doc=_('Delegation name'),
primary_key=True, primary_key=True,
), ),
List('permissions?', Str('permissions*',
cli_name='permissions', cli_name='permissions',
label=_('Permissions'), label=_('Permissions'),
doc=_('Comma-separated list of permissions to grant ' \ doc=_('Comma-separated list of permissions to grant ' \
'(read, write). Default is write.'), '(read, write). Default is write.'),
csv=True,
), ),
List('attrs', Str('attrs+',
cli_name='attrs', cli_name='attrs',
label=_('Attributes'), label=_('Attributes'),
doc=_('Comma-separated list of attributes'), doc=_('Comma-separated list of attributes'),
csv=True,
normalizer=lambda value: value.lower(), normalizer=lambda value: value.lower(),
), ),
Str('memberof', Str('memberof',

View File

@@ -24,7 +24,7 @@ import re
from ipalib import api, errors, output from ipalib import api, errors, output
from ipalib import Command from ipalib import Command
from ipalib import Flag, Bool, Int, List, Str, StrEnum from ipalib import Flag, Bool, Int, Str, StrEnum
from ipalib.plugins.baseldap import * from ipalib.plugins.baseldap import *
from ipalib import _, ngettext from ipalib import _, ngettext
from ipalib.util import validate_zonemgr, normalize_zonemgr, validate_hostname from ipalib.util import validate_zonemgr, normalize_zonemgr, validate_hostname
@@ -1040,16 +1040,16 @@ class dnsrecord_cmd_w_record_options(Command):
validator = _record_validators.get(rec_type) validator = _record_validators.get(rec_type)
normalizer = _record_normalizers.get(rec_type) normalizer = _record_normalizers.get(rec_type)
if validator: if validator:
return List( return Str(
'%srecord?' % rec_type.lower(), validator, normalizer=normalizer, '%srecord*' % rec_type.lower(), validator, normalizer=normalizer,
cli_name='%s_rec' % rec_type.lower(), doc=doc, cli_name='%s_rec' % rec_type.lower(), doc=doc,
label='%s record' % rec_type, attribute=True label='%s record' % rec_type, csv=True, attribute=True
) )
else: else:
return List( return Str(
'%srecord?' % rec_type.lower(), cli_name='%s_rec' % rec_type.lower(), '%srecord*' % rec_type.lower(), cli_name='%s_rec' % rec_type.lower(),
normalizer=normalizer, doc=doc, label='%s record' % rec_type, normalizer=normalizer, doc=doc, label='%s record' % rec_type,
attribute=True csv=True, attribute=True
) )
def prompt_record_options(self, rec_type_list): def prompt_record_options(self, rec_type_list):

View File

@@ -18,7 +18,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from ipalib import api, errors, output from ipalib import api, errors, output
from ipalib import Command, List, Str, Flag from ipalib import Command, Str, Flag
from types import NoneType from types import NoneType
from ipalib.cli import to_cli from ipalib.cli import to_cli
from ipalib import _, ngettext from ipalib import _, ngettext
@@ -186,9 +186,10 @@ class hbactest(Command):
cli_name='service', cli_name='service',
label=_('Service'), label=_('Service'),
), ),
List('rules?', Str('rules*',
cli_name='rules', cli_name='rules',
label=_('Rules to test. If not specified, --enabled is assumed'), label=_('Rules to test. If not specified, --enabled is assumed'),
csv=True,
), ),
Flag('nodetail?', Flag('nodetail?',
cli_name='nodetail', cli_name='nodetail',

View File

@@ -21,7 +21,7 @@ import re
import ldap as _ldap import ldap as _ldap
from ipalib import api, errors, output from ipalib import api, errors, output
from ipalib import Command, List, Password, Str, Flag, StrEnum from ipalib import Command, Password, Str, Flag, StrEnum
from ipalib.cli import to_cli from ipalib.cli import to_cli
from ipalib.dn import * from ipalib.dn import *
if api.env.in_server and api.env.context in ['lite', 'server']: if api.env.in_server and api.env.context in ['lite', 'server']:
@@ -351,45 +351,51 @@ class migrate_ds(Command):
default=u'ou=groups', default=u'ou=groups',
autofill=True, autofill=True,
), ),
List('userobjectclass?', Str('userobjectclass*',
cli_name='user_objectclass', cli_name='user_objectclass',
label=_('User object class'), label=_('User object class'),
doc=_('Comma-separated list of objectclasses used to search for user entries in DS'), doc=_('Comma-separated list of objectclasses used to search for user entries in DS'),
csv=True,
default=(u'person',), default=(u'person',),
autofill=True, autofill=True,
), ),
List('groupobjectclass?', Str('groupobjectclass*',
cli_name='group_objectclass', cli_name='group_objectclass',
label=_('Group object class'), label=_('Group object class'),
doc=_('Comma-separated list of objectclasses used to search for group entries in DS'), doc=_('Comma-separated list of objectclasses used to search for group entries in DS'),
csv=True,
default=(u'groupOfUniqueNames', u'groupOfNames'), default=(u'groupOfUniqueNames', u'groupOfNames'),
autofill=True, autofill=True,
), ),
List('userignoreobjectclass?', Str('userignoreobjectclass*',
cli_name='user_ignore_objectclass', cli_name='user_ignore_objectclass',
label=_('Ignore user object class'), label=_('Ignore user object class'),
doc=_('Comma-separated list of objectclasses to be ignored for user entries in DS'), doc=_('Comma-separated list of objectclasses to be ignored for user entries in DS'),
csv=True,
default=tuple(), default=tuple(),
autofill=True, autofill=True,
), ),
List('userignoreattribute?', Str('userignoreattribute*',
cli_name='user_ignore_attribute', cli_name='user_ignore_attribute',
label=_('Ignore user attribute'), label=_('Ignore user attribute'),
doc=_('Comma-separated list of attributes to be ignored for user entries in DS'), doc=_('Comma-separated list of attributes to be ignored for user entries in DS'),
csv=True,
default=tuple(), default=tuple(),
autofill=True, autofill=True,
), ),
List('groupignoreobjectclass?', Str('groupignoreobjectclass*',
cli_name='group_ignore_objectclass', cli_name='group_ignore_objectclass',
label=_('Ignore group object class'), label=_('Ignore group object class'),
doc=_('Comma-separated list of objectclasses to be ignored for group entries in DS'), doc=_('Comma-separated list of objectclasses to be ignored for group entries in DS'),
csv=True,
default=tuple(), default=tuple(),
autofill=True, autofill=True,
), ),
List('groupignoreattribute?', Str('groupignoreattribute*',
cli_name='group_ignore_attribute', cli_name='group_ignore_attribute',
label=_('Ignore group attribute'), label=_('Ignore group attribute'),
doc=_('Comma-separated list of attributes to be ignored for group entries in DS'), doc=_('Comma-separated list of attributes to be ignored for group entries in DS'),
csv=True,
default=tuple(), default=tuple(),
autofill=True, autofill=True,
), ),
@@ -457,9 +463,9 @@ can use their Kerberos accounts.''')
ldap_obj = self.api.Object[ldap_obj_name] ldap_obj = self.api.Object[ldap_obj_name]
name = 'exclude_%ss' % to_cli(ldap_obj_name) name = 'exclude_%ss' % to_cli(ldap_obj_name)
doc = self.exclude_doc % ldap_obj.object_name_plural doc = self.exclude_doc % ldap_obj.object_name_plural
yield List( yield Str(
'%s?' % name, cli_name=name, doc=doc, default=tuple(), '%s*' % name, cli_name=name, doc=doc, csv=True,
autofill=True default=tuple(), autofill=True
) )
def normalize_options(self, options): def normalize_options(self, options):
@@ -470,7 +476,7 @@ can use their Kerberos accounts.''')
plugin doesn't like that - convert back to empty lists. plugin doesn't like that - convert back to empty lists.
""" """
for p in self.params(): for p in self.params():
if isinstance(p, List): if p.csv:
if options[p.name]: if options[p.name]:
options[p.name] = tuple( options[p.name] = tuple(
v.lower() for v in options[p.name] v.lower() for v in options[p.name]

View File

@@ -114,16 +114,18 @@ class permission(LDAPObject):
label=_('Permission name'), label=_('Permission name'),
primary_key=True, primary_key=True,
), ),
List('permissions', Str('permissions+',
cli_name='permissions', cli_name='permissions',
label=_('Permissions'), label=_('Permissions'),
doc=_('Comma-separated list of permissions to grant ' \ doc=_('Comma-separated list of permissions to grant ' \
'(read, write, add, delete, all)'), '(read, write, add, delete, all)'),
csv=True,
), ),
List('attrs?', Str('attrs*',
cli_name='attrs', cli_name='attrs',
label=_('Attributes'), label=_('Attributes'),
doc=_('Comma-separated list of attributes'), doc=_('Comma-separated list of attributes'),
csv=True,
normalizer=lambda value: value.lower(), normalizer=lambda value: value.lower(),
flags=('ask_create', 'ask_update'), flags=('ask_create', 'ask_update'),
), ),

View File

@@ -19,7 +19,7 @@
import copy import copy
from ipalib import api, _, ngettext from ipalib import api, _, ngettext
from ipalib import Flag, Str, List from ipalib import Flag, Str
from ipalib.request import context from ipalib.request import context
from ipalib import api, crud, errors from ipalib import api, crud, errors
from ipalib import output from ipalib import output
@@ -83,16 +83,18 @@ class selfservice(Object):
doc=_('Self-service name'), doc=_('Self-service name'),
primary_key=True, primary_key=True,
), ),
List('permissions?', Str('permissions*',
cli_name='permissions', cli_name='permissions',
label=_('Permissions'), label=_('Permissions'),
doc=_('Comma-separated list of permissions to grant ' \ doc=_('Comma-separated list of permissions to grant ' \
'(read, write). Default is write.'), '(read, write). Default is write.'),
csv=True,
), ),
List('attrs', Str('attrs+',
cli_name='attrs', cli_name='attrs',
label=_('Attributes'), label=_('Attributes'),
doc=_('Comma-separated list of attributes'), doc=_('Comma-separated list of attributes'),
csv=True,
normalizer=lambda value: value.lower(), normalizer=lambda value: value.lower(),
), ),
) )

View File

@@ -43,30 +43,30 @@ IGNORE_PATHS = ('build', 'dist', 'install/po/test_i18n.py', 'lite-server.py',
class IPATypeChecker(TypeChecker): class IPATypeChecker(TypeChecker):
# 'class': ('generated', 'properties',) # 'class': ('generated', 'properties',)
ignore = { ignore = {
'ipalib.base.NameSpace': ('find',), 'ipalib.base.NameSpace': ['find'],
'ipalib.cli.Collector': ('__options',), 'ipalib.cli.Collector': ['__options'],
'ipalib.config.Env': ('*'), 'ipalib.config.Env': ['*'],
'ipalib.plugable.API': ('Command', 'Object', 'Method', 'Property', 'ipalib.plugable.API': ['Command', 'Object', 'Method', 'Property',
'Backend', 'log', 'plugins'), 'Backend', 'log', 'plugins'],
'ipalib.plugable.Plugin': ('Command', 'Object', 'Method', 'Property', 'ipalib.plugable.Plugin': ['Command', 'Object', 'Method', 'Property',
'Backend', 'env', 'debug', 'info', 'warning', 'error', 'critical', 'Backend', 'env', 'debug', 'info', 'warning', 'error', 'critical',
'exception', 'context', 'log'), 'exception', 'context', 'log'],
'ipalib.plugins.baseldap.CallbackInterface': ('pre_callback', 'ipalib.plugins.baseldap.CallbackInterface': ['pre_callback',
'post_callback', 'exc_callback'), 'post_callback', 'exc_callback'],
'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', 'create_default', 'autofill', 'query',
'attribute', 'include', 'exclude', 'flags', 'hint', 'alwaysask'), 'attribute', 'include', 'exclude', 'flags', 'hint', 'alwaysask',
'ipalib.parameters.Bool': ('truths', 'falsehoods'), 'sortorder', 'csv', 'csv_separator', 'csv_skipspace'],
'ipalib.parameters.Int': ('minvalue', 'maxvalue'), 'ipalib.parameters.Bool': ['truths', 'falsehoods'],
'ipalib.parameters.Float': ('minvalue', 'maxvalue'), 'ipalib.parameters.Int': ['minvalue', 'maxvalue'],
'ipalib.parameters.Data': ('minlength', 'maxlength', 'length', 'ipalib.parameters.Float': ['minvalue', 'maxvalue'],
'pattern', 'pattern_errmsg'), 'ipalib.parameters.Data': ['minlength', 'maxlength', 'length',
'ipalib.parameters.Enum': ('values',), 'pattern', 'pattern_errmsg'],
'ipalib.parameters.List': ('separator', 'skipspace'), 'ipalib.parameters.Enum': ['values'],
'ipalib.parameters.File': ('stdin_if_missing'), 'ipalib.parameters.File': ['stdin_if_missing'],
'urlparse.SplitResult': ('netloc',), 'urlparse.SplitResult': ['netloc'],
} }
def _related_classes(self, klass): def _related_classes(self, klass):

View File

@@ -192,6 +192,10 @@ class test_Param(ClassChecker):
assert o.include is None assert o.include is None
assert o.exclude is None assert o.exclude is None
assert o.flags == frozenset() assert o.flags == frozenset()
assert o.sortorder == 2
assert o.csv is False
assert o.csv_separator == ','
assert o.csv_skipspace is True
# Test that doc defaults from label: # Test that doc defaults from label:
o = self.cls('my_param', doc=_('Hello world')) o = self.cls('my_param', doc=_('Hello world'))
@@ -267,6 +271,10 @@ class test_Param(ClassChecker):
'exclude', frozenset(['client', 'bar']), 'exclude', frozenset(['client', 'bar']),
) )
# Test that ValueError is raised if csv is set and multivalue is not set:
e = raises(ValueError, self.cls, 'my_param', csv=True)
assert str(e) == '%s: cannot have csv without multivalue' % "Param('my_param')"
# Test that _get_default gets set: # Test that _get_default gets set:
call1 = lambda first, last: first[0] + last call1 = lambda first, last: first[0] + last
call2 = lambda **kw: 'The Default' call2 = lambda **kw: 'The Default'
@@ -626,6 +634,48 @@ 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
def test_csv_normalize(self):
"""
Test the `ipalib.parameters.Param.normalize` method with csv.
"""
o = self.cls('my_list+', csv=True)
n = o.normalize('a,b')
assert type(n) is tuple
assert len(n) is 2
n = o.normalize('bar, "hi, there",foo')
assert type(n) is tuple
assert len(n) is 3
def test_csv_normalize_separator(self):
"""
Test the `ipalib.parameters.Param.normalize` method with csv and a separator.
"""
o = self.cls('my_list+', csv=True, csv_separator='|')
n = o.normalize('a')
assert type(n) is tuple
assert len(n) is 1
n = o.normalize('a|b')
assert type(n) is tuple
assert len(n) is 2
def test_csv_normalize_skipspace(self):
"""
Test the `ipalib.parameters.Param.normalize` method with csv without skipping spaces.
"""
o = self.cls('my_list+', csv=True, csv_skipspace=False)
n = o.normalize('a')
assert type(n) is tuple
assert len(n) is 1
n = o.normalize('a, "b,c", d')
assert type(n) is tuple
# the output w/o skipspace is ['a',' "b','c"',' d']
assert len(n) is 4
class test_Flag(ClassChecker): class test_Flag(ClassChecker):
""" """
@@ -1324,66 +1374,6 @@ class test_Float(ClassChecker):
assert dummy.called() is True assert dummy.called() is True
dummy.reset() dummy.reset()
class test_List(ClassChecker):
"""
Test the `ipalib.parameters.List` class.
"""
_cls = parameters.List
def test_init(self):
"""
Test the `ipalib.parameters.List.__init__` method.
"""
# Test with no kwargs:
o = self.cls('my_list')
assert o.type is tuple
assert isinstance(o, parameters.List)
assert o.multivalue is True
assert o.skipspace is True
def test_normalize(self):
"""
Test the `ipalib.parameters.List.normalize` method.
"""
o = self.cls('my_list')
n = o.normalize('a,b')
assert type(n) is tuple
assert len(n) is 2
n = o.normalize('bar, "hi, there",foo')
assert type(n) is tuple
assert len(n) is 3
def test_normalize_separator(self):
"""
Test the `ipalib.parameters.List.normalize` method with a separator.
"""
o = self.cls('my_list', separator='|')
n = o.normalize('a')
assert type(n) is tuple
assert len(n) is 1
n = o.normalize('a|b')
assert type(n) is tuple
assert len(n) is 2
def test_normalize_skipspace(self):
"""
Test the `ipalib.parameters.List.normalize` method without skipping spaces.
"""
o = self.cls('my_list', skipspace=False)
n = o.normalize('a')
assert type(n) is tuple
assert len(n) is 1
n = o.normalize('a, "b,c", d')
assert type(n) is tuple
# the output w/o skipspace is ['a',' "b','c"',' d']
assert len(n) is 4
class test_AccessTime(ClassChecker): class test_AccessTime(ClassChecker):
""" """
Test the `ipalib.parameters.AccessTime` class. Test the `ipalib.parameters.AccessTime` class.