81: Switch from tab to 4-space indentation

This commit is contained in:
Jason Gerard DeRose 2008-08-08 17:11:29 +00:00
parent f656e31a7e
commit 8e46824815
10 changed files with 1279 additions and 1279 deletions

194
ipa
View File

@ -31,54 +31,54 @@ from ipalib.startup import api
TAB_WIDTH = 2 TAB_WIDTH = 2
def _(msg): def _(msg):
""" """
Dummy gettext function for testing. Dummy gettext function for testing.
""" """
return msg return msg
class row(object): class row(object):
def __init__(self, tab, c1, c2=None): def __init__(self, tab, c1, c2=None):
assert type(tab) is int assert type(tab) is int
assert type(c1) in (str, int) assert type(c1) in (str, int)
assert type(c2) is str or c2 is None assert type(c2) is str or c2 is None
self.tab = tab self.tab = tab
self.c1 = c1 self.c1 = c1
self.c2 = c2 self.c2 = c2
def __len__(self): def __len__(self):
return len(str(self.c1)) return len(str(self.c1))
def pretty_print(self, just): def pretty_print(self, just):
tab = ' ' * (self.tab * TAB_WIDTH) tab = ' ' * (self.tab * TAB_WIDTH)
if self.c2 is None: if self.c2 is None:
print '%s%s' % (tab, self.c1) print '%s%s' % (tab, self.c1)
else: else:
if type(self.c1) is int: if type(self.c1) is int:
c1 = str(self.c1).rjust(just) c1 = str(self.c1).rjust(just)
else: else:
c1 = self.c1.ljust(just) c1 = self.c1.ljust(just)
print '%s%s %s' % (tab, c1, self.c2) print '%s%s %s' % (tab, c1, self.c2)
def pretty_print(rows): def pretty_print(rows):
rows = tuple(rows) rows = tuple(rows)
def get_lengths(): def get_lengths():
yield 0 yield 0
for r in rows: for r in rows:
if r.c2 is not None: if r.c2 is not None:
yield len(r) yield len(r)
max_len = max(get_lengths()) max_len = max(get_lengths())
for r in rows: for r in rows:
r.pretty_print(max_len) r.pretty_print(max_len)
def print_commands(): def print_commands():
print 'Commands:' print 'Commands:'
m = api.max_cmd_len m = api.max_cmd_len
for cmd in api.cmd: for cmd in api.cmd:
print ' %s %s' % (str(cmd).ljust(m), cmd.get_doc(_)) print ' %s %s' % (str(cmd).ljust(m), cmd.get_doc(_))
def print_help(cmd): def print_help(cmd):
print 'Help on %s' % cmd print 'Help on %s' % cmd
@ -94,79 +94,79 @@ def print_help(cmd):
def print_api(): def print_api():
def iter_api(tab): def iter_api(tab):
for name in api: for name in api:
ns = getattr(api, name) ns = getattr(api, name)
yield row( yield row(
tab, tab,
name, name,
repr(ns), repr(ns),
) )
for i in ns: for i in ns:
yield row( yield row(
tab + 1, tab + 1,
i.name, i.name,
repr(i) repr(i)
) )
def iter_obj(tab): def iter_obj(tab):
for obj in api.obj: for obj in api.obj:
yield row( yield row(
tab, tab,
obj.name, obj.name,
repr(obj), repr(obj),
) )
for (n, f) in [('mthd', '.%s()'), ('prop', '.%s')]: for (n, f) in [('mthd', '.%s()'), ('prop', '.%s')]:
ns = getattr(obj, n) ns = getattr(obj, n)
yield row( yield row(
tab + 1, tab + 1,
n, n,
repr(ns), repr(ns),
) )
for attr in ns: for attr in ns:
yield row( yield row(
tab + 2, tab + 2,
f % attr.name, f % attr.name,
repr(attr), repr(attr),
) )
def iter_summary(tab): def iter_summary(tab):
for name in api: for name in api:
ns = getattr(api, name) ns = getattr(api, name)
yield row( yield row(
tab, tab,
len(ns), len(ns),
name name
) )
def print_heading(h): def print_heading(h):
print '\n%s:' % h print '\n%s:' % h
print '-' * (len(h) + 1) print '-' * (len(h) + 1)
tab = 1 tab = 1
print_heading('API Overview') print_heading('API Overview')
pretty_print(iter_api(tab)) pretty_print(iter_api(tab))
print_heading('Object Details') print_heading('Object Details')
pretty_print(iter_obj(tab)) pretty_print(iter_obj(tab))
print_heading('Summary') print_heading('Summary')
pretty_print(iter_summary(tab)) pretty_print(iter_summary(tab))
if len(sys.argv) < 2: if len(sys.argv) < 2:
print_commands() print_commands()
print 'Usage: ipa COMMAND [OPTIONS]' print 'Usage: ipa COMMAND [OPTIONS]'
sys.exit(2) sys.exit(2)
name= sys.argv[1] name= sys.argv[1]
if name == '_api_': if name == '_api_':
print_api() print_api()
sys.exit() sys.exit()
elif name not in api.cmd: elif name not in api.cmd:
print_commands() print_commands()
print 'ipa: ERROR: unknown command %r' % name print 'ipa: ERROR: unknown command %r' % name
sys.exit(2) sys.exit(2)
api.cmd[name]() api.cmd[name]()

View File

@ -22,128 +22,128 @@ All custom errors raised by `ipalib` package.
""" """
class IPAError(Exception): class IPAError(Exception):
""" """
Use this base class for your custom IPA errors unless there is a Use this base class for your custom IPA errors unless there is a
specific reason to subclass from AttributeError, KeyError, etc. specific reason to subclass from AttributeError, KeyError, etc.
""" """
msg = None msg = None
def __init__(self, *args, **kw): def __init__(self, *args, **kw):
self.args = args self.args = args
self.kw = kw self.kw = kw
def __str__(self): def __str__(self):
""" """
Returns the string representation of this exception. Returns the string representation of this exception.
""" """
if self.msg is None: if self.msg is None:
if len(self.args) == 1: if len(self.args) == 1:
return unicode(self.args[0]) return unicode(self.args[0])
return unicode(self.args) return unicode(self.args)
if len(self.args) > 0: if len(self.args) > 0:
return self.msg % self.args return self.msg % self.args
return self.msg % self.kw return self.msg % self.kw
class ValidationError(IPAError): class ValidationError(IPAError):
msg = 'invalid %r value %r: %s' msg = 'invalid %r value %r: %s'
def __init__(self, name, value, error): def __init__(self, name, value, error):
self.name = name self.name = name
self.value = value self.value = value
self.error = error self.error = error
super(ValidationError, self).__init__(name, value, error) super(ValidationError, self).__init__(name, value, error)
class NormalizationError(ValidationError): class NormalizationError(ValidationError):
def __init__(self, name, value, type): def __init__(self, name, value, type):
self.type = type self.type = type
super(NormalizationError, self).__init__(name, value, super(NormalizationError, self).__init__(name, value,
'not %r' % type 'not %r' % type
) )
class RuleError(ValidationError): class RuleError(ValidationError):
def __init__(self, name, value, rule, error): def __init__(self, name, value, rule, error):
self.rule = rule self.rule = rule
super(RuleError, self).__init__(name, value, error) super(RuleError, self).__init__(name, value, error)
class SetError(IPAError): class SetError(IPAError):
msg = 'setting %r, but NameSpace does not allow attribute setting' msg = 'setting %r, but NameSpace does not allow attribute setting'
class RegistrationError(IPAError): class RegistrationError(IPAError):
""" """
Base class for errors that occur during plugin registration. Base class for errors that occur during plugin registration.
""" """
class NameSpaceError(RegistrationError): class NameSpaceError(RegistrationError):
msg = 'name %r does not re.match %r' msg = 'name %r does not re.match %r'
class SubclassError(RegistrationError): class SubclassError(RegistrationError):
""" """
Raised when registering a plugin that is not a subclass of one of the Raised when registering a plugin that is not a subclass of one of the
allowed bases. allowed bases.
""" """
msg = 'plugin %r not subclass of any base in %r' msg = 'plugin %r not subclass of any base in %r'
def __init__(self, cls, allowed): def __init__(self, cls, allowed):
self.cls = cls self.cls = cls
self.allowed = allowed self.allowed = allowed
def __str__(self): def __str__(self):
return self.msg % (self.cls, self.allowed) return self.msg % (self.cls, self.allowed)
class DuplicateError(RegistrationError): class DuplicateError(RegistrationError):
""" """
Raised when registering a plugin whose exact class has already been Raised when registering a plugin whose exact class has already been
registered. registered.
""" """
msg = '%r at %d was already registered' msg = '%r at %d was already registered'
def __init__(self, cls): def __init__(self, cls):
self.cls = cls self.cls = cls
def __str__(self): def __str__(self):
return self.msg % (self.cls, id(self.cls)) return self.msg % (self.cls, id(self.cls))
class OverrideError(RegistrationError): class OverrideError(RegistrationError):
""" """
Raised when override=False yet registering a plugin that overrides an Raised when override=False yet registering a plugin that overrides an
existing plugin in the same namespace. existing plugin in the same namespace.
""" """
msg = 'unexpected override of %s.%s with %r (use override=True if intended)' msg = 'unexpected override of %s.%s with %r (use override=True if intended)'
def __init__(self, base, cls): def __init__(self, base, cls):
self.base = base self.base = base
self.cls = cls self.cls = cls
def __str__(self): def __str__(self):
return self.msg % (self.base.__name__, self.cls.__name__, self.cls) return self.msg % (self.base.__name__, self.cls.__name__, self.cls)
class MissingOverrideError(RegistrationError): class MissingOverrideError(RegistrationError):
""" """
Raised when override=True yet no preexisting plugin with the same name Raised when override=True yet no preexisting plugin with the same name
and base has been registered. and base has been registered.
""" """
msg = '%s.%s has not been registered, cannot override with %r' msg = '%s.%s has not been registered, cannot override with %r'
def __init__(self, base, cls): def __init__(self, base, cls):
self.base = base self.base = base
self.cls = cls self.cls = cls
def __str__(self): def __str__(self):
return self.msg % (self.base.__name__, self.cls.__name__, self.cls) return self.msg % (self.base.__name__, self.cls.__name__, self.cls)
class TwiceSetError(IPAError): class TwiceSetError(IPAError):
msg = '%s.%s cannot be set twice' msg = '%s.%s cannot be set twice'

View File

@ -5,50 +5,50 @@
def get_label(self, _): def get_label(self, _):
return _('Title') # Enum? return _('Title') # Enum?
def get_label(self, _): def get_label(self, _):
return _('First Name') return _('First Name')
def get_label(self, _): def get_label(self, _):
return _('Last Name') return _('Last Name')
def get_label(self, _): def get_label(self, _):
return _('Full Name') # Autofill return _('Full Name') # Autofill
def get_label(self, _): def get_label(self, _):
return _('Display Name') # Autofill return _('Display Name') # Autofill
def get_label(self, _): def get_label(self, _):
return _('Initials') # generated/ro? return _('Initials') # generated/ro?
def get_label(self, _): def get_label(self, _):
return _('Account Status') # Enum (active, inactive) return _('Account Status') # Enum (active, inactive)
def get_label(self, _): def get_label(self, _):
return _('Login') return _('Login')
def get_label(self, _): def get_label(self, _):
return _('Password') return _('Password')
def get_label(self, _): # Same field as above, special interface def get_label(self, _): # Same field as above, special interface
return _('Confirm Password') return _('Confirm Password')
def get_label(self, _): def get_label(self, _):
return _('UID') #ro return _('UID') #ro
def get_label(self, _): def get_label(self, _):
return _('GID') #ro return _('GID') #ro
def get_label(self, _): def get_label(self, _):
return _('Home Directory') #ro return _('Home Directory') #ro
def get_label(self, _): def get_label(self, _):
return _('Login Shell') return _('Login Shell')
def get_label(self, _): def get_label(self, _):
return _('GECOS') return _('GECOS')
def get_label(self, _): def get_label(self, _):
return _('') return _('')

View File

@ -27,323 +27,323 @@ import errors
def to_cli(name): def to_cli(name):
""" """
Takes a Python identifier and transforms it into form suitable for the Takes a Python identifier and transforms it into form suitable for the
Command Line Interface. Command Line Interface.
""" """
assert isinstance(name, str) assert isinstance(name, str)
return name.replace('_', '-') return name.replace('_', '-')
def from_cli(cli_name): def from_cli(cli_name):
""" """
Takes a string from the Command Line Interface and transforms it into a Takes a string from the Command Line Interface and transforms it into a
Python identifier. Python identifier.
""" """
assert isinstance(cli_name, basestring) assert isinstance(cli_name, basestring)
return cli_name.replace('-', '_') return cli_name.replace('-', '_')
def check_identifier(name): def check_identifier(name):
""" """
Raises errors.NameSpaceError if `name` is not a valid Python identifier Raises errors.NameSpaceError if `name` is not a valid Python identifier
suitable for use in a NameSpace. suitable for use in a NameSpace.
""" """
regex = r'^[a-z][_a-z0-9]*[a-z0-9]$' regex = r'^[a-z][_a-z0-9]*[a-z0-9]$'
if re.match(regex, name) is None: if re.match(regex, name) is None:
raise errors.NameSpaceError(name, regex) raise errors.NameSpaceError(name, regex)
class Plugin(object): class Plugin(object):
""" """
Base class for all plugins. Base class for all plugins.
""" """
__api = None __api = None
def __get_api(self): def __get_api(self):
""" """
Returns the plugable.API instance passed to Plugin.finalize(), or Returns the plugable.API instance passed to Plugin.finalize(), or
or returns None if finalize() has not yet been called. or returns None if finalize() has not yet been called.
""" """
return self.__api return self.__api
api = property(__get_api) api = property(__get_api)
def finalize(self, api): def finalize(self, api):
""" """
After all the plugins are instantiated, the plugable.API calls this After all the plugins are instantiated, the plugable.API calls this
method, passing itself as the only argument. This is where plugins method, passing itself as the only argument. This is where plugins
should check that other plugins they depend upon have actually be should check that other plugins they depend upon have actually be
loaded. loaded.
""" """
assert self.__api is None, 'finalize() can only be called once' assert self.__api is None, 'finalize() can only be called once'
assert api is not None, 'finalize() argument cannot be None' assert api is not None, 'finalize() argument cannot be None'
self.__api = api self.__api = api
def __get_name(self): def __get_name(self):
""" """
Returns the class name of this instance. Returns the class name of this instance.
""" """
return self.__class__.__name__ return self.__class__.__name__
name = property(__get_name) name = property(__get_name)
def __repr__(self): def __repr__(self):
""" """
Returns a fully qualified <module><name> representation of the class. Returns a fully qualified <module><name> representation of the class.
""" """
return '%s.%s()' % ( return '%s.%s()' % (
self.__class__.__module__, self.__class__.__module__,
self.__class__.__name__ self.__class__.__name__
) )
class ReadOnly(object): class ReadOnly(object):
""" """
Base class for classes with read-only attributes. Base class for classes with read-only attributes.
""" """
__slots__ = tuple() __slots__ = tuple()
def __setattr__(self, name, value): def __setattr__(self, name, value):
""" """
This raises an AttributeError anytime an attempt is made to set an This raises an AttributeError anytime an attempt is made to set an
attribute. attribute.
""" """
raise AttributeError('read-only: cannot set %s.%s' % raise AttributeError('read-only: cannot set %s.%s' %
(self.__class__.__name__, name) (self.__class__.__name__, name)
) )
def __delattr__(self, name): def __delattr__(self, name):
""" """
This raises an AttributeError anytime an attempt is made to delete an This raises an AttributeError anytime an attempt is made to delete an
attribute. attribute.
""" """
raise AttributeError('read-only: cannot del %s.%s' % raise AttributeError('read-only: cannot del %s.%s' %
(self.__class__.__name__, name) (self.__class__.__name__, name)
) )
class Proxy(ReadOnly): class Proxy(ReadOnly):
__slots__ = ( __slots__ = (
'__base', '__base',
'__target', '__target',
'__name_attr', '__name_attr',
'__public', '__public',
'name', 'name',
) )
def __init__(self, base, target, name_attr='name'): def __init__(self, base, target, name_attr='name'):
if not inspect.isclass(base): if not inspect.isclass(base):
raise TypeError('arg1 must be a class, got %r' % base) raise TypeError('arg1 must be a class, got %r' % base)
if not isinstance(target, base): if not isinstance(target, base):
raise ValueError('arg2 must be instance of arg1, got %r' % target) raise ValueError('arg2 must be instance of arg1, got %r' % target)
object.__setattr__(self, '_Proxy__base', base) object.__setattr__(self, '_Proxy__base', base)
object.__setattr__(self, '_Proxy__target', target) object.__setattr__(self, '_Proxy__target', target)
object.__setattr__(self, '_Proxy__name_attr', name_attr) object.__setattr__(self, '_Proxy__name_attr', name_attr)
object.__setattr__(self, '_Proxy__public', base.__public__) object.__setattr__(self, '_Proxy__public', base.__public__)
object.__setattr__(self, 'name', getattr(target, name_attr)) object.__setattr__(self, 'name', getattr(target, name_attr))
# Check __public # Check __public
assert type(self.__public) is frozenset assert type(self.__public) is frozenset
# Check name # Check name
check_identifier(self.name) check_identifier(self.name)
def __iter__(self): def __iter__(self):
for name in sorted(self.__public): for name in sorted(self.__public):
yield name yield name
def __getitem__(self, key): def __getitem__(self, key):
if key in self.__public: if key in self.__public:
return getattr(self.__target, key) return getattr(self.__target, key)
raise KeyError('no proxy attribute %r' % key) raise KeyError('no proxy attribute %r' % key)
def __getattr__(self, name): def __getattr__(self, name):
if name in self.__public: if name in self.__public:
return getattr(self.__target, name) return getattr(self.__target, name)
raise AttributeError('no proxy attribute %r' % name) raise AttributeError('no proxy attribute %r' % name)
def __call__(self, *args, **kw): def __call__(self, *args, **kw):
return self['__call__'](*args, **kw) return self['__call__'](*args, **kw)
def _clone(self, name_attr): def _clone(self, name_attr):
return self.__class__(self.__base, self.__target, name_attr) return self.__class__(self.__base, self.__target, name_attr)
def __repr__(self): def __repr__(self):
return '%s(%s, %r, %r)' % ( return '%s(%s, %r, %r)' % (
self.__class__.__name__, self.__class__.__name__,
self.__base.__name__, self.__base.__name__,
self.__target, self.__target,
self.__name_attr, self.__name_attr,
) )
class NameSpace(ReadOnly): class NameSpace(ReadOnly):
""" """
A read-only namespace of (key, value) pairs that can be accessed A read-only namespace of (key, value) pairs that can be accessed
both as instance attributes and as dictionary items. both as instance attributes and as dictionary items.
""" """
def __init__(self, proxies): def __init__(self, proxies):
""" """
NameSpace NameSpace
""" """
object.__setattr__(self, '_NameSpace__proxies', tuple(proxies)) object.__setattr__(self, '_NameSpace__proxies', tuple(proxies))
object.__setattr__(self, '_NameSpace__d', dict()) object.__setattr__(self, '_NameSpace__d', dict())
for proxy in self.__proxies: for proxy in self.__proxies:
assert isinstance(proxy, Proxy) assert isinstance(proxy, Proxy)
assert proxy.name not in self.__d assert proxy.name not in self.__d
self.__d[proxy.name] = proxy self.__d[proxy.name] = proxy
assert not hasattr(self, proxy.name) assert not hasattr(self, proxy.name)
object.__setattr__(self, proxy.name, proxy) object.__setattr__(self, proxy.name, proxy)
def __iter__(self): def __iter__(self):
""" """
Iterates through the proxies in this NameSpace in the same order they Iterates through the proxies in this NameSpace in the same order they
were passed in the contructor. were passed in the contructor.
""" """
for proxy in self.__proxies: for proxy in self.__proxies:
yield proxy yield proxy
def __len__(self): def __len__(self):
""" """
Returns number of proxies in this NameSpace. Returns number of proxies in this NameSpace.
""" """
return len(self.__proxies) return len(self.__proxies)
def __contains__(self, key): def __contains__(self, key):
""" """
Returns True if a proxy named `key` is in this NameSpace. Returns True if a proxy named `key` is in this NameSpace.
""" """
return key in self.__d return key in self.__d
def __getitem__(self, key): def __getitem__(self, key):
""" """
Returns proxy named `key`; otherwise raises KeyError. Returns proxy named `key`; otherwise raises KeyError.
""" """
if key in self.__d: if key in self.__d:
return self.__d[key] return self.__d[key]
raise KeyError('NameSpace has no item for key %r' % key) raise KeyError('NameSpace has no item for key %r' % key)
def __repr__(self): def __repr__(self):
return '%s(<%d proxies>)' % (self.__class__.__name__, len(self)) return '%s(<%d proxies>)' % (self.__class__.__name__, len(self))
class Registrar(object): class Registrar(object):
def __init__(self, *allowed): def __init__(self, *allowed):
""" """
`*allowed` is a list of the base classes plugins can be subclassed `*allowed` is a list of the base classes plugins can be subclassed
from. from.
""" """
self.__allowed = frozenset(allowed) self.__allowed = frozenset(allowed)
self.__d = {} self.__d = {}
self.__registered = set() self.__registered = set()
assert len(self.__allowed) == len(allowed) assert len(self.__allowed) == len(allowed)
for base in self.__allowed: for base in self.__allowed:
assert inspect.isclass(base) assert inspect.isclass(base)
assert base.__name__ not in self.__d assert base.__name__ not in self.__d
self.__d[base.__name__] = {} self.__d[base.__name__] = {}
def __findbase(self, cls): def __findbase(self, cls):
""" """
If `cls` is a subclass of a base in self.__allowed, returns that If `cls` is a subclass of a base in self.__allowed, returns that
base; otherwise raises SubclassError. base; otherwise raises SubclassError.
""" """
assert inspect.isclass(cls) assert inspect.isclass(cls)
found = False found = False
for base in self.__allowed: for base in self.__allowed:
if issubclass(cls, base): if issubclass(cls, base):
found = True found = True
yield base yield base
if not found: if not found:
raise errors.SubclassError(cls, self.__allowed) raise errors.SubclassError(cls, self.__allowed)
def __call__(self, cls, override=False): def __call__(self, cls, override=False):
""" """
Register the plugin `cls`. Register the plugin `cls`.
""" """
if not inspect.isclass(cls): if not inspect.isclass(cls):
raise TypeError('plugin must be a class: %r' % cls) raise TypeError('plugin must be a class: %r' % cls)
# Raise DuplicateError if this exact class was already registered: # Raise DuplicateError if this exact class was already registered:
if cls in self.__registered: if cls in self.__registered:
raise errors.DuplicateError(cls) raise errors.DuplicateError(cls)
# Find the base class or raise SubclassError: # Find the base class or raise SubclassError:
for base in self.__findbase(cls): for base in self.__findbase(cls):
sub_d = self.__d[base.__name__] sub_d = self.__d[base.__name__]
# Check override: # Check override:
if cls.__name__ in sub_d: if cls.__name__ in sub_d:
# Must use override=True to override: # Must use override=True to override:
if not override: if not override:
raise errors.OverrideError(base, cls) raise errors.OverrideError(base, cls)
else: else:
# There was nothing already registered to override: # There was nothing already registered to override:
if override: if override:
raise errors.MissingOverrideError(base, cls) raise errors.MissingOverrideError(base, cls)
# The plugin is okay, add to sub_d: # The plugin is okay, add to sub_d:
sub_d[cls.__name__] = cls sub_d[cls.__name__] = cls
# The plugin is okay, add to __registered: # The plugin is okay, add to __registered:
self.__registered.add(cls) self.__registered.add(cls)
def __getitem__(self, item): def __getitem__(self, item):
""" """
Returns a copy of the namespace dict of the base class named `name`. Returns a copy of the namespace dict of the base class named `name`.
""" """
if inspect.isclass(item): if inspect.isclass(item):
if item not in self.__allowed: if item not in self.__allowed:
raise KeyError(repr(item)) raise KeyError(repr(item))
key = item.__name__ key = item.__name__
else: else:
key = item key = item
return dict(self.__d[key]) return dict(self.__d[key])
def __contains__(self, item): def __contains__(self, item):
""" """
Returns True if a base class named `name` is in this Registrar. Returns True if a base class named `name` is in this Registrar.
""" """
if inspect.isclass(item): if inspect.isclass(item):
return item in self.__allowed return item in self.__allowed
return item in self.__d return item in self.__d
def __iter__(self): def __iter__(self):
""" """
Iterates through a (base, registered_plugins) tuple for each allowed Iterates through a (base, registered_plugins) tuple for each allowed
base. base.
""" """
for base in self.__allowed: for base in self.__allowed:
sub_d = self.__d[base.__name__] sub_d = self.__d[base.__name__]
yield (base, tuple(sub_d[k] for k in sorted(sub_d))) yield (base, tuple(sub_d[k] for k in sorted(sub_d)))
class API(ReadOnly): class API(ReadOnly):
def __init__(self, *allowed): def __init__(self, *allowed):
keys = tuple(b.__name__ for b in allowed) keys = tuple(b.__name__ for b in allowed)
object.__setattr__(self, '_API__keys', keys) object.__setattr__(self, '_API__keys', keys)
object.__setattr__(self, 'register', Registrar(*allowed)) object.__setattr__(self, 'register', Registrar(*allowed))
def __call__(self): def __call__(self):
""" """
Finalize the registration, instantiate the plugins. Finalize the registration, instantiate the plugins.
""" """
d = {} d = {}
def plugin_iter(base, classes): def plugin_iter(base, classes):
for cls in classes: for cls in classes:
if cls not in d: if cls not in d:
d[cls] = cls() d[cls] = cls()
plugin = d[cls] plugin = d[cls]
yield Proxy(base, plugin) yield Proxy(base, plugin)
for (base, classes) in self.register: for (base, classes) in self.register:
ns = NameSpace(plugin_iter(base, classes)) ns = NameSpace(plugin_iter(base, classes))
assert not hasattr(self, base.__name__) assert not hasattr(self, base.__name__)
object.__setattr__(self, base.__name__, ns) object.__setattr__(self, base.__name__, ns)
for plugin in d.values(): for plugin in d.values():
plugin.finalize(self) plugin.finalize(self)
assert plugin.api is self assert plugin.api is self
def __iter__(self): def __iter__(self):
for key in self.__keys: for key in self.__keys:
yield key yield key

View File

@ -27,109 +27,109 @@ from run import api
# Hypothetical functional commands (not associated with any object): # Hypothetical functional commands (not associated with any object):
class krbtest(public.cmd): class krbtest(public.cmd):
def get_doc(self, _): def get_doc(self, _):
return _('test your Kerberos ticket') return _('test your Kerberos ticket')
api.register(krbtest) api.register(krbtest)
class discover(public.cmd): class discover(public.cmd):
def get_doc(self, _): def get_doc(self, _):
return _('discover IPA servers on network') return _('discover IPA servers on network')
api.register(discover) api.register(discover)
# Register some methods for the 'user' object: # Register some methods for the 'user' object:
class user_add(public.mthd): class user_add(public.mthd):
def get_doc(self, _): def get_doc(self, _):
return _('add new user') return _('add new user')
api.register(user_add) api.register(user_add)
class user_del(public.mthd): class user_del(public.mthd):
def get_doc(self, _): def get_doc(self, _):
return _('delete existing user') return _('delete existing user')
api.register(user_del) api.register(user_del)
class user_mod(public.mthd): class user_mod(public.mthd):
def get_doc(self, _): def get_doc(self, _):
return _('edit existing user') return _('edit existing user')
api.register(user_mod) api.register(user_mod)
class user_find(public.mthd): class user_find(public.mthd):
def get_doc(self, _): def get_doc(self, _):
return _('search for users') return _('search for users')
api.register(user_find) api.register(user_find)
# Register some properties for the 'user' object: # Register some properties for the 'user' object:
class user_firstname(public.prop): class user_firstname(public.prop):
pass pass
api.register(user_firstname) api.register(user_firstname)
class user_lastname(public.prop): class user_lastname(public.prop):
pass pass
api.register(user_lastname) api.register(user_lastname)
class user_login(public.prop): class user_login(public.prop):
pass pass
api.register(user_login) api.register(user_login)
# Register some methods for the 'group' object: # Register some methods for the 'group' object:
class group_add(public.mthd): class group_add(public.mthd):
def get_doc(self, _): def get_doc(self, _):
return _('add new group') return _('add new group')
api.register(group_add) api.register(group_add)
class group_del(public.mthd): class group_del(public.mthd):
def get_doc(self, _): def get_doc(self, _):
return _('delete existing group') return _('delete existing group')
api.register(group_del) api.register(group_del)
class group_mod(public.mthd): class group_mod(public.mthd):
def get_doc(self, _): def get_doc(self, _):
return _('edit existing group') return _('edit existing group')
api.register(group_mod) api.register(group_mod)
class group_find(public.mthd): class group_find(public.mthd):
def get_doc(self, _): def get_doc(self, _):
return _('search for groups') return _('search for groups')
api.register(group_find) api.register(group_find)
# Register some methods for the 'service' object # Register some methods for the 'service' object
class service_add(public.mthd): class service_add(public.mthd):
def get_doc(self, _): def get_doc(self, _):
return _('add new service') return _('add new service')
api.register(service_add) api.register(service_add)
class service_del(public.mthd): class service_del(public.mthd):
def get_doc(self, _): def get_doc(self, _):
return _('delete existing service') return _('delete existing service')
api.register(service_del) api.register(service_del)
class service_mod(public.mthd): class service_mod(public.mthd):
def get_doc(self, _): def get_doc(self, _):
return _('edit existing service') return _('edit existing service')
api.register(service_mod) api.register(service_mod)
class service_find(public.mthd): class service_find(public.mthd):
def get_doc(self, _): def get_doc(self, _):
return _('search for services') return _('search for services')
api.register(service_find) api.register(service_find)
# And to emphasis that the registration order doesn't matter, # And to emphasis that the registration order doesn't matter,
# we'll register the objects last: # we'll register the objects last:
class group(public.obj): class group(public.obj):
def get_doc(self, _): def get_doc(self, _):
return _('') return _('')
api.register(group) api.register(group)
class service(public.obj): class service(public.obj):
def get_doc(self, _): def get_doc(self, _):
return _('') return _('')
api.register(service) api.register(service)
class user(public.obj): class user(public.obj):
def get_doc(self, _): def get_doc(self, _):
return _('') return _('')
api.register(user) api.register(user)

View File

@ -30,255 +30,255 @@ import errors
RULE_FLAG = 'validation_rule' RULE_FLAG = 'validation_rule'
def rule(obj): def rule(obj):
assert not hasattr(obj, RULE_FLAG) assert not hasattr(obj, RULE_FLAG)
setattr(obj, RULE_FLAG, True) setattr(obj, RULE_FLAG, True)
return obj return obj
def is_rule(obj): def is_rule(obj):
return callable(obj) and getattr(obj, RULE_FLAG, False) is True return callable(obj) and getattr(obj, RULE_FLAG, False) is True
class option(object): class option(object):
""" """
The option class represents a kw argument from a command. The option class represents a kw argument from a command.
""" """
__public__ = frozenset(( __public__ = frozenset((
'normalize', 'normalize',
'validate', 'validate',
'default', 'default',
'required', 'required',
'type', 'type',
)) ))
__rules = None __rules = None
# type = unicode, int, float # Set in subclass # type = unicode, int, float # Set in subclass
def normalize(self, value): def normalize(self, value):
""" """
Returns the normalized form of `value`. If `value` cannot be Returns the normalized form of `value`. If `value` cannot be
normalized, NormalizationError is raised, which is a subclass of normalized, NormalizationError is raised, which is a subclass of
ValidationError. ValidationError.
The base class implementation only does type coercion, but subclasses The base class implementation only does type coercion, but subclasses
might do other normalization (e.g., a unicode option might strip might do other normalization (e.g., a unicode option might strip
leading and trailing white-space). leading and trailing white-space).
""" """
try: try:
return self.type(value) return self.type(value)
except (TypeError, ValueError): except (TypeError, ValueError):
raise errors.NormalizationError( raise errors.NormalizationError(
self.__class__.__name__, value, self.type self.__class__.__name__, value, self.type
) )
def validate(self, value): def validate(self, value):
""" """
Calls each validation rule and if any rule fails, raises RuleError, Calls each validation rule and if any rule fails, raises RuleError,
which is a subclass of ValidationError. which is a subclass of ValidationError.
""" """
for rule in self.rules: for rule in self.rules:
msg = rule(value) msg = rule(value)
if msg is not None: if msg is not None:
raise errors.RuleError( raise errors.RuleError(
self.__class__.__name__, self.__class__.__name__,
value, value,
rule, rule,
msg, msg,
) )
def __get_rules(self): def __get_rules(self):
""" """
Returns the tuple of rule methods used for input validation. This Returns the tuple of rule methods used for input validation. This
tuple is lazily initialized the first time the property is accessed. tuple is lazily initialized the first time the property is accessed.
""" """
if self.__rules is None: if self.__rules is None:
self.__rules = tuple(sorted( self.__rules = tuple(sorted(
self.__rules_iter(), self.__rules_iter(),
key=lambda f: getattr(f, '__name__'), key=lambda f: getattr(f, '__name__'),
)) ))
return self.__rules return self.__rules
rules = property(__get_rules) rules = property(__get_rules)
def __rules_iter(self): def __rules_iter(self):
""" """
Iterates through the attributes in this instance to retrieve the Iterates through the attributes in this instance to retrieve the
methods implemented validation rules. methods implemented validation rules.
""" """
for name in dir(self.__class__): for name in dir(self.__class__):
if name.startswith('_'): if name.startswith('_'):
continue continue
base_attr = getattr(self.__class__, name) base_attr = getattr(self.__class__, name)
if is_rule(base_attr): if is_rule(base_attr):
attr = getattr(self, name) attr = getattr(self, name)
if is_rule(attr): if is_rule(attr):
yield attr yield attr
def default(self, **kw): def default(self, **kw):
""" """
Returns a default or auto-completed value for this option. If no Returns a default or auto-completed value for this option. If no
default is available, this method should return None. default is available, this method should return None.
""" """
return None return None
class cmd(plugable.Plugin): class cmd(plugable.Plugin):
__public__ = frozenset(( __public__ = frozenset((
'normalize', 'normalize',
'autofill', 'autofill',
'__call__', '__call__',
'get_doc', 'get_doc',
'opt', 'opt',
)) ))
__opt = None __opt = None
def get_doc(self, _): def get_doc(self, _):
""" """
Returns the gettext translated doc-string for this command. Returns the gettext translated doc-string for this command.
For example: For example:
>>> def get_doc(self, _): >>> def get_doc(self, _):
>>> return _('add new user') >>> return _('add new user')
""" """
raise NotImplementedError('%s.get_doc()' % self.name) raise NotImplementedError('%s.get_doc()' % self.name)
def get_options(self): def get_options(self):
""" """
Returns iterable with opt_proxy objects used to create the opt Returns iterable with opt_proxy objects used to create the opt
NameSpace when __get_opt() is called. NameSpace when __get_opt() is called.
""" """
raise NotImplementedError('%s.get_options()' % self.name) raise NotImplementedError('%s.get_options()' % self.name)
def __get_opt(self): def __get_opt(self):
""" """
Returns the NameSpace containing opt_proxy objects. Returns the NameSpace containing opt_proxy objects.
""" """
if self.__opt is None: if self.__opt is None:
self.__opt = plugable.NameSpace(self.get_options()) self.__opt = plugable.NameSpace(self.get_options())
return self.__opt return self.__opt
opt = property(__get_opt) opt = property(__get_opt)
def normalize_iter(self, kw): def normalize_iter(self, kw):
for (key, value) in kw.items(): for (key, value) in kw.items():
if key in self.options: if key in self.options:
yield ( yield (
key, self.options[key].normalize(value) key, self.options[key].normalize(value)
) )
else: else:
yield (key, value) yield (key, value)
def normalize(self, **kw): def normalize(self, **kw):
return dict(self.normalize_iter(kw)) return dict(self.normalize_iter(kw))
def validate(self, **kw): def validate(self, **kw):
for (key, value) in kw.items(): for (key, value) in kw.items():
if key in self.options: if key in self.options:
self.options.validate(value) self.options.validate(value)
def default(self, **kw): def default(self, **kw):
for opt in self.options: for opt in self.options:
if opt.name not in kw: if opt.name not in kw:
value = opt.default(**kw) value = opt.default(**kw)
if value is not None: if value is not None:
kw[opt.name] = value kw[opt.name] = value
def __call__(self, **kw): def __call__(self, **kw):
(args, kw) = self.normalize(*args, **kw) (args, kw) = self.normalize(*args, **kw)
(args, kw) = self.autofill(*args, **kw) (args, kw) = self.autofill(*args, **kw)
self.validate(*args, **kw) self.validate(*args, **kw)
self.execute(*args, **kw) self.execute(*args, **kw)
class obj(plugable.Plugin): class obj(plugable.Plugin):
__public__ = frozenset(( __public__ = frozenset((
'mthd', 'mthd',
'prop', 'prop',
)) ))
__mthd = None __mthd = None
__prop = None __prop = None
def __get_mthd(self): def __get_mthd(self):
return self.__mthd return self.__mthd
mthd = property(__get_mthd) mthd = property(__get_mthd)
def __get_prop(self): def __get_prop(self):
return self.__prop return self.__prop
prop = property(__get_prop) prop = property(__get_prop)
def finalize(self, api): def finalize(self, api):
super(obj, self).finalize(api) super(obj, self).finalize(api)
self.__mthd = self.__create_ns('mthd') self.__mthd = self.__create_ns('mthd')
self.__prop = self.__create_ns('prop') self.__prop = self.__create_ns('prop')
def __create_ns(self, name): def __create_ns(self, name):
return plugable.NameSpace(self.__filter(name)) return plugable.NameSpace(self.__filter(name))
def __filter(self, name): def __filter(self, name):
for i in getattr(self.api, name): for i in getattr(self.api, name):
if i.obj_name == self.name: if i.obj_name == self.name:
yield i._clone('attr_name') yield i._clone('attr_name')
class attr(plugable.Plugin): class attr(plugable.Plugin):
__obj = None __obj = None
def __init__(self): def __init__(self):
m = re.match('^([a-z]+)_([a-z]+)$', self.__class__.__name__) m = re.match('^([a-z]+)_([a-z]+)$', self.__class__.__name__)
assert m assert m
self.__obj_name = m.group(1) self.__obj_name = m.group(1)
self.__attr_name = m.group(2) self.__attr_name = m.group(2)
def __get_obj_name(self): def __get_obj_name(self):
return self.__obj_name return self.__obj_name
obj_name = property(__get_obj_name) obj_name = property(__get_obj_name)
def __get_attr_name(self): def __get_attr_name(self):
return self.__attr_name return self.__attr_name
attr_name = property(__get_attr_name) attr_name = property(__get_attr_name)
def __get_obj(self): def __get_obj(self):
""" """
Returns the obj instance this attribute is associated with, or None Returns the obj instance this attribute is associated with, or None
if no association has been set. if no association has been set.
""" """
return self.__obj return self.__obj
obj = property(__get_obj) obj = property(__get_obj)
def finalize(self, api): def finalize(self, api):
super(attr, self).finalize(api) super(attr, self).finalize(api)
self.__obj = api.obj[self.obj_name] self.__obj = api.obj[self.obj_name]
class mthd(attr, cmd): class mthd(attr, cmd):
__public__ = frozenset(( __public__ = frozenset((
'obj', 'obj',
'obj_name', 'obj_name',
)) ))
class prop(attr): class prop(attr):
__public__ = frozenset(( __public__ = frozenset((
'obj', 'obj',
'obj_name', 'obj_name',
)) ))
def get_doc(self, _): def get_doc(self, _):
return _('prop doc') return _('prop doc')
class PublicAPI(plugable.API): class PublicAPI(plugable.API):
__max_cmd_len = None __max_cmd_len = None
def __init__(self): def __init__(self):
super(PublicAPI, self).__init__(cmd, obj, mthd, prop) super(PublicAPI, self).__init__(cmd, obj, mthd, prop)
def __get_max_cmd_len(self): def __get_max_cmd_len(self):
if self.__max_cmd_len is None: if self.__max_cmd_len is None:
if not hasattr(self, 'cmd'): if not hasattr(self, 'cmd'):
return None return None
max_cmd_len = max(len(str(cmd)) for cmd in self.cmd) max_cmd_len = max(len(str(cmd)) for cmd in self.cmd)
object.__setattr__(self, '_PublicAPI__max_cmd_len', max_cmd_len) object.__setattr__(self, '_PublicAPI__max_cmd_len', max_cmd_len)
return self.__max_cmd_len return self.__max_cmd_len
max_cmd_len = property(__get_max_cmd_len) max_cmd_len = property(__get_max_cmd_len)

View File

@ -26,396 +26,396 @@ from ipalib import plugable, errors
def test_to_cli(): def test_to_cli():
f = plugable.to_cli f = plugable.to_cli
assert f('initialize') == 'initialize' assert f('initialize') == 'initialize'
assert f('user_add') == 'user-add' assert f('user_add') == 'user-add'
def test_from_cli(): def test_from_cli():
f = plugable.from_cli f = plugable.from_cli
assert f('initialize') == 'initialize' assert f('initialize') == 'initialize'
assert f('user-add') == 'user_add' assert f('user-add') == 'user_add'
def test_valid_identifier(): def test_valid_identifier():
f = plugable.check_identifier f = plugable.check_identifier
okay = [ okay = [
'user_add', 'user_add',
'stuff2junk', 'stuff2junk',
'sixty9', 'sixty9',
] ]
nope = [ nope = [
'_user_add', '_user_add',
'__user_add', '__user_add',
'user_add_', 'user_add_',
'user_add__', 'user_add__',
'_user_add_', '_user_add_',
'__user_add__', '__user_add__',
'60nine', '60nine',
] ]
for name in okay: for name in okay:
f(name) f(name)
for name in nope: for name in nope:
raises(errors.NameSpaceError, f, name) raises(errors.NameSpaceError, f, name)
for name in okay: for name in okay:
raises(errors.NameSpaceError, f, name.upper()) raises(errors.NameSpaceError, f, name.upper())
def test_Plugin(): def test_Plugin():
cls = plugable.Plugin cls = plugable.Plugin
assert type(cls.name) is property assert type(cls.name) is property
api = 'the api instance' api = 'the api instance'
p = plugable.Plugin() p = plugable.Plugin()
assert read_only(p, 'name') == 'Plugin' assert read_only(p, 'name') == 'Plugin'
assert repr(p) == '%s.Plugin()' % plugable.__name__ assert repr(p) == '%s.Plugin()' % plugable.__name__
assert read_only(p, 'api') is None assert read_only(p, 'api') is None
raises(AssertionError, p.finalize, None) raises(AssertionError, p.finalize, None)
p.finalize(api) p.finalize(api)
assert read_only(p, 'api') is api assert read_only(p, 'api') is api
raises(AssertionError, p.finalize, api) raises(AssertionError, p.finalize, api)
class some_plugin(plugable.Plugin): class some_plugin(plugable.Plugin):
pass pass
p = some_plugin() p = some_plugin()
assert read_only(p, 'name') == 'some_plugin' assert read_only(p, 'name') == 'some_plugin'
assert repr(p) == '%s.some_plugin()' % __name__ assert repr(p) == '%s.some_plugin()' % __name__
assert read_only(p, 'api') is None assert read_only(p, 'api') is None
raises(AssertionError, p.finalize, None) raises(AssertionError, p.finalize, None)
p.finalize(api) p.finalize(api)
assert read_only(p, 'api') is api assert read_only(p, 'api') is api
raises(AssertionError, p.finalize, api) raises(AssertionError, p.finalize, api)
def test_ReadOnly(): def test_ReadOnly():
obj = plugable.ReadOnly() obj = plugable.ReadOnly()
names = ['not_an_attribute', 'an_attribute'] names = ['not_an_attribute', 'an_attribute']
for name in names: for name in names:
no_set(obj, name) no_set(obj, name)
no_del(obj, name) no_del(obj, name)
class some_ro_class(plugable.ReadOnly): class some_ro_class(plugable.ReadOnly):
def __init__(self): def __init__(self):
object.__setattr__(self, 'an_attribute', 'Hello world!') object.__setattr__(self, 'an_attribute', 'Hello world!')
obj = some_ro_class() obj = some_ro_class()
for name in names: for name in names:
no_set(obj, name) no_set(obj, name)
no_del(obj, name) no_del(obj, name)
assert read_only(obj, 'an_attribute') == 'Hello world!' assert read_only(obj, 'an_attribute') == 'Hello world!'
def test_Proxy(): def test_Proxy():
cls = plugable.Proxy cls = plugable.Proxy
assert issubclass(cls, plugable.ReadOnly) assert issubclass(cls, plugable.ReadOnly)
# Setup: # Setup:
class base(object): class base(object):
__public__ = frozenset(( __public__ = frozenset((
'public_0', 'public_0',
'public_1', 'public_1',
'__call__', '__call__',
)) ))
def public_0(self): def public_0(self):
return 'public_0' return 'public_0'
def public_1(self): def public_1(self):
return 'public_1' return 'public_1'
def __call__(self, caller): def __call__(self, caller):
return 'ya called it, %s.' % caller return 'ya called it, %s.' % caller
def private_0(self): def private_0(self):
return 'private_0' return 'private_0'
def private_1(self): def private_1(self):
return 'private_1' return 'private_1'
class plugin(base): class plugin(base):
name = 'user_add' name = 'user_add'
attr_name = 'add' attr_name = 'add'
# Test that TypeError is raised when base is not a class: # Test that TypeError is raised when base is not a class:
raises(TypeError, cls, base(), None) raises(TypeError, cls, base(), None)
# Test that ValueError is raised when target is not instance of base: # Test that ValueError is raised when target is not instance of base:
raises(ValueError, cls, base, object()) raises(ValueError, cls, base, object())
# Test with correct arguments: # Test with correct arguments:
i = plugin() i = plugin()
p = cls(base, i) p = cls(base, i)
assert read_only(p, 'name') == 'user_add' assert read_only(p, 'name') == 'user_add'
assert list(p) == sorted(base.__public__) assert list(p) == sorted(base.__public__)
# Test normal methods: # Test normal methods:
for n in xrange(2): for n in xrange(2):
pub = 'public_%d' % n pub = 'public_%d' % n
priv = 'private_%d' % n priv = 'private_%d' % n
assert getattr(i, pub)() == pub assert getattr(i, pub)() == pub
assert getattr(p, pub)() == pub assert getattr(p, pub)() == pub
assert hasattr(p, pub) assert hasattr(p, pub)
assert getattr(i, priv)() == priv assert getattr(i, priv)() == priv
assert not hasattr(p, priv) assert not hasattr(p, priv)
# Test __call__: # Test __call__:
value = 'ya called it, dude.' value = 'ya called it, dude.'
assert i('dude') == value assert i('dude') == value
assert p('dude') == value assert p('dude') == value
assert callable(p) assert callable(p)
# Test name_attr='name' kw arg # Test name_attr='name' kw arg
i = plugin() i = plugin()
p = cls(base, i, 'attr_name') p = cls(base, i, 'attr_name')
assert read_only(p, 'name') == 'add' assert read_only(p, 'name') == 'add'
# Test _clone(): # Test _clone():
i = plugin() i = plugin()
p = cls(base, i) p = cls(base, i)
assert read_only(p, 'name') == 'user_add' assert read_only(p, 'name') == 'user_add'
c = p._clone('attr_name') c = p._clone('attr_name')
assert isinstance(c, cls) assert isinstance(c, cls)
assert read_only(c, 'name') == 'add' assert read_only(c, 'name') == 'add'
assert c is not p assert c is not p
assert c('whoever') == p('whoever') assert c('whoever') == p('whoever')
def test_NameSpace(): def test_NameSpace():
cls = plugable.NameSpace cls = plugable.NameSpace
assert issubclass(cls, plugable.ReadOnly) assert issubclass(cls, plugable.ReadOnly)
class base(object): class base(object):
__public__ = frozenset(( __public__ = frozenset((
'plusplus', 'plusplus',
)) ))
def plusplus(self, n): def plusplus(self, n):
return n + 1 return n + 1
class plugin(base): class plugin(base):
def __init__(self, name): def __init__(self, name):
self.name = name self.name = name
def get_name(i): def get_name(i):
return 'noun_verb%d' % i return 'noun_verb%d' % i
def get_proxies(n): def get_proxies(n):
for i in xrange(n): for i in xrange(n):
yield plugable.Proxy(base, plugin(get_name(i))) yield plugable.Proxy(base, plugin(get_name(i)))
cnt = 20 cnt = 20
ns = cls(get_proxies(cnt)) ns = cls(get_proxies(cnt))
# Test __len__ # Test __len__
assert len(ns) == cnt assert len(ns) == cnt
# Test __iter__ # Test __iter__
i = None i = None
for (i, proxy) in enumerate(ns): for (i, proxy) in enumerate(ns):
assert type(proxy) is plugable.Proxy assert type(proxy) is plugable.Proxy
assert proxy.name == get_name(i) assert proxy.name == get_name(i)
assert i == cnt - 1 assert i == cnt - 1
# Test __contains__, __getitem__, getattr(): # Test __contains__, __getitem__, getattr():
proxies = frozenset(ns) proxies = frozenset(ns)
for i in xrange(cnt): for i in xrange(cnt):
name = get_name(i) name = get_name(i)
assert name in ns assert name in ns
proxy = ns[name] proxy = ns[name]
assert proxy.name == name assert proxy.name == name
assert type(proxy) is plugable.Proxy assert type(proxy) is plugable.Proxy
assert proxy in proxies assert proxy in proxies
assert read_only(ns, name) is proxy assert read_only(ns, name) is proxy
# Test dir(): # Test dir():
assert set(get_name(i) for i in xrange(cnt)).issubset(set(dir(ns))) assert set(get_name(i) for i in xrange(cnt)).issubset(set(dir(ns)))
# Test that KeyError, AttributeError is raised: # Test that KeyError, AttributeError is raised:
name = get_name(cnt) name = get_name(cnt)
assert name not in ns assert name not in ns
raises(KeyError, getitem, ns, name) raises(KeyError, getitem, ns, name)
raises(AttributeError, getattr, ns, name) raises(AttributeError, getattr, ns, name)
no_set(ns, name) no_set(ns, name)
def test_Registrar(): def test_Registrar():
class Base1(object): class Base1(object):
pass pass
class Base2(object): class Base2(object):
pass pass
class Base3(object): class Base3(object):
pass pass
class plugin1(Base1): class plugin1(Base1):
pass pass
class plugin2(Base2): class plugin2(Base2):
pass pass
class plugin3(Base3): class plugin3(Base3):
pass pass
# Test creation of Registrar: # Test creation of Registrar:
r = plugable.Registrar(Base1, Base2) r = plugable.Registrar(Base1, Base2)
# Test __hasitem__, __getitem__: # Test __hasitem__, __getitem__:
for base in [Base1, Base2]: for base in [Base1, Base2]:
assert base in r assert base in r
assert base.__name__ in r assert base.__name__ in r
assert r[base] == {} assert r[base] == {}
assert r[base.__name__] == {} assert r[base.__name__] == {}
# Check that TypeError is raised trying to register something that isn't # Check that TypeError is raised trying to register something that isn't
# a class: # a class:
raises(TypeError, r, plugin1()) raises(TypeError, r, plugin1())
# Check that SubclassError is raised trying to register a class that is # Check that SubclassError is raised trying to register a class that is
# not a subclass of an allowed base: # not a subclass of an allowed base:
raises(errors.SubclassError, r, plugin3) raises(errors.SubclassError, r, plugin3)
# Check that registration works # Check that registration works
r(plugin1) r(plugin1)
sub_d = r['Base1'] sub_d = r['Base1']
assert len(sub_d) == 1 assert len(sub_d) == 1
assert sub_d['plugin1'] is plugin1 assert sub_d['plugin1'] is plugin1
# Check that a copy is returned # Check that a copy is returned
assert sub_d is not r['Base1'] assert sub_d is not r['Base1']
assert sub_d == r['Base1'] assert sub_d == r['Base1']
# Check that DuplicateError is raised trying to register exact class # Check that DuplicateError is raised trying to register exact class
# again: # again:
raises(errors.DuplicateError, r, plugin1) raises(errors.DuplicateError, r, plugin1)
# Check that OverrideError is raised trying to register class with same # Check that OverrideError is raised trying to register class with same
# name and same base: # name and same base:
orig1 = plugin1 orig1 = plugin1
class base1_extended(Base1): class base1_extended(Base1):
pass pass
class plugin1(base1_extended): class plugin1(base1_extended):
pass pass
raises(errors.OverrideError, r, plugin1) raises(errors.OverrideError, r, plugin1)
# Check that overriding works # Check that overriding works
r(plugin1, override=True) r(plugin1, override=True)
sub_d = r['Base1'] sub_d = r['Base1']
assert len(sub_d) == 1 assert len(sub_d) == 1
assert sub_d['plugin1'] is plugin1 assert sub_d['plugin1'] is plugin1
assert sub_d['plugin1'] is not orig1 assert sub_d['plugin1'] is not orig1
# Check that MissingOverrideError is raised trying to override a name # Check that MissingOverrideError is raised trying to override a name
# not yet registerd: # not yet registerd:
raises(errors.MissingOverrideError, r, plugin2, override=True) raises(errors.MissingOverrideError, r, plugin2, override=True)
# Check that additional plugin can be registered: # Check that additional plugin can be registered:
r(plugin2) r(plugin2)
sub_d = r['Base2'] sub_d = r['Base2']
assert len(sub_d) == 1 assert len(sub_d) == 1
assert sub_d['plugin2'] is plugin2 assert sub_d['plugin2'] is plugin2
# Setup to test __iter__: # Setup to test __iter__:
class plugin1a(Base1): class plugin1a(Base1):
pass pass
r(plugin1a) r(plugin1a)
class plugin1b(Base1): class plugin1b(Base1):
pass pass
r(plugin1b) r(plugin1b)
class plugin2a(Base2): class plugin2a(Base2):
pass pass
r(plugin2a) r(plugin2a)
class plugin2b(Base2): class plugin2b(Base2):
pass pass
r(plugin2b) r(plugin2b)
m = { m = {
'Base1': set([plugin1, plugin1a, plugin1b]), 'Base1': set([plugin1, plugin1a, plugin1b]),
'Base2': set([plugin2, plugin2a, plugin2b]), 'Base2': set([plugin2, plugin2a, plugin2b]),
} }
# Now test __iter__: # Now test __iter__:
for (base, plugins) in r: for (base, plugins) in r:
assert base in [Base1, Base2] assert base in [Base1, Base2]
assert set(plugins) == m[base.__name__] assert set(plugins) == m[base.__name__]
assert len(list(r)) == 2 assert len(list(r)) == 2
# Again test __hasitem__, __getitem__: # Again test __hasitem__, __getitem__:
for base in [Base1, Base2]: for base in [Base1, Base2]:
assert base in r assert base in r
assert base.__name__ in r assert base.__name__ in r
d = dict((p.__name__, p) for p in m[base.__name__]) d = dict((p.__name__, p) for p in m[base.__name__])
assert len(d) == 3 assert len(d) == 3
assert r[base] == d assert r[base] == d
assert r[base.__name__] == d assert r[base.__name__] == d
def test_API(): def test_API():
assert issubclass(plugable.API, plugable.ReadOnly) assert issubclass(plugable.API, plugable.ReadOnly)
# Setup the test bases, create the API: # Setup the test bases, create the API:
class base0(plugable.Plugin): class base0(plugable.Plugin):
__public__ = frozenset(( __public__ = frozenset((
'method', 'method',
)) ))
def method(self, n): def method(self, n):
return n return n
class base1(plugable.Plugin): class base1(plugable.Plugin):
__public__ = frozenset(( __public__ = frozenset((
'method', 'method',
)) ))
def method(self, n): def method(self, n):
return n + 1 return n + 1
api = plugable.API(base0, base1) api = plugable.API(base0, base1)
r = api.register r = api.register
assert isinstance(r, plugable.Registrar) assert isinstance(r, plugable.Registrar)
assert read_only(api, 'register') is r assert read_only(api, 'register') is r
class base0_plugin0(base0): class base0_plugin0(base0):
pass pass
r(base0_plugin0) r(base0_plugin0)
class base0_plugin1(base0): class base0_plugin1(base0):
pass pass
r(base0_plugin1) r(base0_plugin1)
class base0_plugin2(base0): class base0_plugin2(base0):
pass pass
r(base0_plugin2) r(base0_plugin2)
class base1_plugin0(base1): class base1_plugin0(base1):
pass pass
r(base1_plugin0) r(base1_plugin0)
class base1_plugin1(base1): class base1_plugin1(base1):
pass pass
r(base1_plugin1) r(base1_plugin1)
class base1_plugin2(base1): class base1_plugin2(base1):
pass pass
r(base1_plugin2) r(base1_plugin2)
# Test API instance: # Test API instance:
api() # Calling instance performs finalization api() # Calling instance performs finalization
def get_base(b): def get_base(b):
return 'base%d' % b return 'base%d' % b
def get_plugin(b, p): def get_plugin(b, p):
return 'base%d_plugin%d' % (b, p) return 'base%d_plugin%d' % (b, p)
for b in xrange(2): for b in xrange(2):
base_name = get_base(b) base_name = get_base(b)
ns = getattr(api, base_name) ns = getattr(api, base_name)
assert isinstance(ns, plugable.NameSpace) assert isinstance(ns, plugable.NameSpace)
assert read_only(api, base_name) is ns assert read_only(api, base_name) is ns
assert len(ns) == 3 assert len(ns) == 3
for p in xrange(3): for p in xrange(3):
plugin_name = get_plugin(b, p) plugin_name = get_plugin(b, p)
proxy = ns[plugin_name] proxy = ns[plugin_name]
assert isinstance(proxy, plugable.Proxy) assert isinstance(proxy, plugable.Proxy)
assert proxy.name == plugin_name assert proxy.name == plugin_name
assert read_only(ns, plugin_name) is proxy assert read_only(ns, plugin_name) is proxy
assert read_only(proxy, 'method')(7) == 7 + b assert read_only(proxy, 'method')(7) == 7 + b

View File

@ -26,179 +26,179 @@ from ipalib import public, plugable, errors
def test_RULE_FLAG(): def test_RULE_FLAG():
assert public.RULE_FLAG == 'validation_rule' assert public.RULE_FLAG == 'validation_rule'
def test_rule(): def test_rule():
flag = public.RULE_FLAG flag = public.RULE_FLAG
rule = public.rule rule = public.rule
def my_func(): def my_func():
pass pass
assert not hasattr(my_func, flag) assert not hasattr(my_func, flag)
rule(my_func) rule(my_func)
assert getattr(my_func, flag) is True assert getattr(my_func, flag) is True
@rule @rule
def my_func2(): def my_func2():
pass pass
assert getattr(my_func2, flag) is True assert getattr(my_func2, flag) is True
def test_is_rule(): def test_is_rule():
is_rule = public.is_rule is_rule = public.is_rule
flag = public.RULE_FLAG flag = public.RULE_FLAG
class no_call(object): class no_call(object):
def __init__(self, value): def __init__(self, value):
if value is not None: if value is not None:
assert value in (True, False) assert value in (True, False)
setattr(self, flag, value) setattr(self, flag, value)
class call(no_call): class call(no_call):
def __call__(self): def __call__(self):
pass pass
assert is_rule(call(True)) assert is_rule(call(True))
assert not is_rule(no_call(True)) assert not is_rule(no_call(True))
assert not is_rule(call(False)) assert not is_rule(call(False))
assert not is_rule(call(None)) assert not is_rule(call(None))
class test_option(): class test_option():
def cls(self): def cls(self):
return public.option return public.option
def sub(self): def sub(self):
rule = public.rule rule = public.rule
class int_opt(self.cls()): class int_opt(self.cls()):
type = int type = int
@rule @rule
def rule_0(self, value): def rule_0(self, value):
if value == 0: if value == 0:
return 'cannot be 0' return 'cannot be 0'
@rule @rule
def rule_1(self, value): def rule_1(self, value):
if value == 1: if value == 1:
return 'cannot be 1' return 'cannot be 1'
@rule @rule
def rule_2(self, value): def rule_2(self, value):
if value == 2: if value == 2:
return 'cannot be 2' return 'cannot be 2'
return int_opt return int_opt
def test_class(self): def test_class(self):
""" """
Perform some tests on the class (not an instance). Perform some tests on the class (not an instance).
""" """
cls = self.cls() cls = self.cls()
#assert issubclass(cls, plugable.ReadOnly) #assert issubclass(cls, plugable.ReadOnly)
assert type(cls.rules) is property assert type(cls.rules) is property
def test_normalize(self): def test_normalize(self):
sub = self.sub() sub = self.sub()
i = sub() i = sub()
# Test with values that can't be converted: # Test with values that can't be converted:
nope = ( nope = (
'7.0' '7.0'
'whatever', 'whatever',
object, object,
None, None,
) )
for val in nope: for val in nope:
e = raises(errors.NormalizationError, i.normalize, val) e = raises(errors.NormalizationError, i.normalize, val)
assert isinstance(e, errors.ValidationError) assert isinstance(e, errors.ValidationError)
assert e.name == 'int_opt' assert e.name == 'int_opt'
assert e.value == val assert e.value == val
assert e.error == "not <type 'int'>" assert e.error == "not <type 'int'>"
assert e.type is int assert e.type is int
# Test with values that can be converted: # Test with values that can be converted:
okay = ( okay = (
7, 7,
7.0, 7.0,
7.2, 7.2,
7L, 7L,
'7', '7',
' 7 ', ' 7 ',
) )
for val in okay: for val in okay:
assert i.normalize(val) == 7 assert i.normalize(val) == 7
def test_rules(self): def test_rules(self):
""" """
Test the rules property. Test the rules property.
""" """
o = self.sub()() o = self.sub()()
assert len(o.rules) == 3 assert len(o.rules) == 3
def get_rule(i): def get_rule(i):
return getattr(o, 'rule_%d' % i) return getattr(o, 'rule_%d' % i)
rules = tuple(get_rule(i) for i in xrange(3)) rules = tuple(get_rule(i) for i in xrange(3))
assert o.rules == rules assert o.rules == rules
def test_validation(self): def test_validation(self):
""" """
Test the validation method. Test the validation method.
""" """
o = self.sub()() o = self.sub()()
o.validate(9) o.validate(9)
for i in xrange(3): for i in xrange(3):
e = raises(errors.RuleError, o.validate, i) e = raises(errors.RuleError, o.validate, i)
assert e.error == 'cannot be %d' % i assert e.error == 'cannot be %d' % i
assert e.value == i assert e.value == i
def test_cmd(): def test_cmd():
cls = public.cmd cls = public.cmd
assert issubclass(cls, plugable.Plugin) assert issubclass(cls, plugable.Plugin)
def test_obj(): def test_obj():
cls = public.obj cls = public.obj
assert issubclass(cls, plugable.Plugin) assert issubclass(cls, plugable.Plugin)
def test_attr(): def test_attr():
cls = public.attr cls = public.attr
assert issubclass(cls, plugable.Plugin) assert issubclass(cls, plugable.Plugin)
class api(object): class api(object):
obj = dict(user='the user obj') obj = dict(user='the user obj')
class user_add(cls): class user_add(cls):
pass pass
i = user_add() i = user_add()
assert read_only(i, 'obj_name') == 'user' assert read_only(i, 'obj_name') == 'user'
assert read_only(i, 'attr_name') == 'add' assert read_only(i, 'attr_name') == 'add'
assert read_only(i, 'obj') is None assert read_only(i, 'obj') is None
i.finalize(api) i.finalize(api)
assert read_only(i, 'api') is api assert read_only(i, 'api') is api
assert read_only(i, 'obj') == 'the user obj' assert read_only(i, 'obj') == 'the user obj'
def test_mthd(): def test_mthd():
cls = public.mthd cls = public.mthd
assert issubclass(cls, public.attr) assert issubclass(cls, public.attr)
assert issubclass(cls, public.cmd) assert issubclass(cls, public.cmd)
def test_prop(): def test_prop():
cls = public.prop cls = public.prop
assert issubclass(cls, public.attr) assert issubclass(cls, public.attr)
def test_PublicAPI(): def test_PublicAPI():
cls = public.PublicAPI cls = public.PublicAPI
assert issubclass(cls, plugable.API) assert issubclass(cls, plugable.API)
api = cls() api = cls()
class cmd1(public.cmd): class cmd1(public.cmd):
pass pass
api.register(cmd1) api.register(cmd1)
class cmd2(public.cmd): class cmd2(public.cmd):
pass pass
api.register(cmd2) api.register(cmd2)
api() api()

View File

@ -25,124 +25,124 @@ import tstutil
class Prop(object): class Prop(object):
def __init__(self, *ops): def __init__(self, *ops):
self.__ops = frozenset(ops) self.__ops = frozenset(ops)
self.__prop = 'prop value' self.__prop = 'prop value'
def __get_prop(self): def __get_prop(self):
if 'get' not in self.__ops: if 'get' not in self.__ops:
raise AttributeError('get prop') raise AttributeError('get prop')
return self.__prop return self.__prop
def __set_prop(self, value): def __set_prop(self, value):
if 'set' not in self.__ops: if 'set' not in self.__ops:
raise AttributeError('set prop') raise AttributeError('set prop')
self.__prop = value self.__prop = value
def __del_prop(self): def __del_prop(self):
if 'del' not in self.__ops: if 'del' not in self.__ops:
raise AttributeError('del prop') raise AttributeError('del prop')
self.__prop = None self.__prop = None
prop = property(__get_prop, __set_prop, __del_prop) prop = property(__get_prop, __set_prop, __del_prop)
def test_yes_raised(): def test_yes_raised():
f = tstutil.raises f = tstutil.raises
class SomeError(Exception): class SomeError(Exception):
pass pass
class AnotherError(Exception): class AnotherError(Exception):
pass pass
def callback1(): def callback1():
'raises correct exception' 'raises correct exception'
raise SomeError() raise SomeError()
def callback2(): def callback2():
'raises wrong exception' 'raises wrong exception'
raise AnotherError() raise AnotherError()
def callback3(): def callback3():
'raises no exception' 'raises no exception'
f(SomeError, callback1) f(SomeError, callback1)
raised = False raised = False
try: try:
f(SomeError, callback2) f(SomeError, callback2)
except AnotherError: except AnotherError:
raised = True raised = True
assert raised assert raised
raised = False raised = False
try: try:
f(SomeError, callback3) f(SomeError, callback3)
except tstutil.ExceptionNotRaised: except tstutil.ExceptionNotRaised:
raised = True raised = True
assert raised assert raised
def test_no_set(): def test_no_set():
# Tests that it works when prop cannot be set: # Tests that it works when prop cannot be set:
tstutil.no_set(Prop('get', 'del'), 'prop') tstutil.no_set(Prop('get', 'del'), 'prop')
# Tests that ExceptionNotRaised is raised when prop *can* be set: # Tests that ExceptionNotRaised is raised when prop *can* be set:
raised = False raised = False
try: try:
tstutil.no_set(Prop('set'), 'prop') tstutil.no_set(Prop('set'), 'prop')
except tstutil.ExceptionNotRaised: except tstutil.ExceptionNotRaised:
raised = True raised = True
assert raised assert raised
def test_no_del(): def test_no_del():
# Tests that it works when prop cannot be deleted: # Tests that it works when prop cannot be deleted:
tstutil.no_del(Prop('get', 'set'), 'prop') tstutil.no_del(Prop('get', 'set'), 'prop')
# Tests that ExceptionNotRaised is raised when prop *can* be set: # Tests that ExceptionNotRaised is raised when prop *can* be set:
raised = False raised = False
try: try:
tstutil.no_del(Prop('del'), 'prop') tstutil.no_del(Prop('del'), 'prop')
except tstutil.ExceptionNotRaised: except tstutil.ExceptionNotRaised:
raised = True raised = True
assert raised assert raised
def test_read_only(): def test_read_only():
# Test that it works when prop is read only: # Test that it works when prop is read only:
assert tstutil.read_only(Prop('get'), 'prop') == 'prop value' assert tstutil.read_only(Prop('get'), 'prop') == 'prop value'
# Test that ExceptionNotRaised is raised when prop can be set: # Test that ExceptionNotRaised is raised when prop can be set:
raised = False raised = False
try: try:
tstutil.read_only(Prop('get', 'set'), 'prop') tstutil.read_only(Prop('get', 'set'), 'prop')
except tstutil.ExceptionNotRaised: except tstutil.ExceptionNotRaised:
raised = True raised = True
assert raised assert raised
# Test that ExceptionNotRaised is raised when prop can be deleted: # Test that ExceptionNotRaised is raised when prop can be deleted:
raised = False raised = False
try: try:
tstutil.read_only(Prop('get', 'del'), 'prop') tstutil.read_only(Prop('get', 'del'), 'prop')
except tstutil.ExceptionNotRaised: except tstutil.ExceptionNotRaised:
raised = True raised = True
assert raised assert raised
# Test that ExceptionNotRaised is raised when prop can be both set and # Test that ExceptionNotRaised is raised when prop can be both set and
# deleted: # deleted:
raised = False raised = False
try: try:
tstutil.read_only(Prop('get', 'del'), 'prop') tstutil.read_only(Prop('get', 'del'), 'prop')
except tstutil.ExceptionNotRaised: except tstutil.ExceptionNotRaised:
raised = True raised = True
assert raised assert raised
# Test that AttributeError is raised when prop can't be read: # Test that AttributeError is raised when prop can't be read:
raised = False raised = False
try: try:
tstutil.read_only(Prop(), 'prop') tstutil.read_only(Prop(), 'prop')
except AttributeError: except AttributeError:
raised = True raised = True
assert raised assert raised

View File

@ -22,78 +22,78 @@ Utility functions for the unit tests.
""" """
class ExceptionNotRaised(Exception): class ExceptionNotRaised(Exception):
""" """
Exception raised when an *expected* exception is *not* raised during a Exception raised when an *expected* exception is *not* raised during a
unit test. unit test.
""" """
msg = 'expected %s' msg = 'expected %s'
def __init__(self, expected): def __init__(self, expected):
self.expected = expected self.expected = expected
def __str__(self): def __str__(self):
return self.msg % self.expected.__name__ return self.msg % self.expected.__name__
def raises(exception, callback, *args, **kw): def raises(exception, callback, *args, **kw):
""" """
Tests that the expected exception is raised; raises ExceptionNotRaised Tests that the expected exception is raised; raises ExceptionNotRaised
if test fails. if test fails.
""" """
raised = False raised = False
try: try:
callback(*args, **kw) callback(*args, **kw)
except exception, e: except exception, e:
raised = True raised = True
if not raised: if not raised:
raise ExceptionNotRaised(exception) raise ExceptionNotRaised(exception)
return e return e
def getitem(obj, key): def getitem(obj, key):
""" """
Works like getattr but for dictionary interface. Uses this in combination Works like getattr but for dictionary interface. Uses this in combination
with raises() to test that, for example, KeyError is raised. with raises() to test that, for example, KeyError is raised.
""" """
return obj[key] return obj[key]
def no_set(obj, name, value='some_new_obj'): def no_set(obj, name, value='some_new_obj'):
""" """
Tests that attribute cannot be set. Tests that attribute cannot be set.
""" """
raises(AttributeError, setattr, obj, name, value) raises(AttributeError, setattr, obj, name, value)
def no_del(obj, name): def no_del(obj, name):
""" """
Tests that attribute cannot be deleted. Tests that attribute cannot be deleted.
""" """
raises(AttributeError, delattr, obj, name) raises(AttributeError, delattr, obj, name)
def read_only(obj, name, value='some_new_obj'): def read_only(obj, name, value='some_new_obj'):
""" """
Tests that attribute is read-only. Returns attribute. Tests that attribute is read-only. Returns attribute.
""" """
# Test that it cannot be set: # Test that it cannot be set:
no_set(obj, name, value) no_set(obj, name, value)
# Test that it cannot be deleted: # Test that it cannot be deleted:
no_del(obj, name) no_del(obj, name)
# Return the attribute # Return the attribute
return getattr(obj, name) return getattr(obj, name)
def is_prop(prop): def is_prop(prop):
return type(prop) is property return type(prop) is property
class ClassChecker(object): class ClassChecker(object):
def new(self, *args, **kw): def new(self, *args, **kw):
return self.cls(*args, **kw) return self.cls(*args, **kw)
def get_sub(self): def get_sub(self):
raise NotImplementedError('get_sub()') raise NotImplementedError('get_sub()')