mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
4: Got basics of API.register_command() working; added corresponding unit tests
This commit is contained in:
@@ -20,3 +20,7 @@
|
|||||||
"""
|
"""
|
||||||
IPA library.
|
IPA library.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import base
|
||||||
|
|
||||||
|
api = base.API()
|
||||||
|
|||||||
@@ -21,10 +21,24 @@
|
|||||||
Base classes for plug-in architecture and generative API.
|
Base classes for plug-in architecture and generative API.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from exceptions import SetAttributeError
|
import inspect
|
||||||
|
import exceptions
|
||||||
|
|
||||||
|
|
||||||
class Command(object):
|
class Named(object):
|
||||||
|
#def __init__(self, prefix):
|
||||||
|
# clsname = self.__class__.__name__
|
||||||
|
def __get_name(self):
|
||||||
|
return self.__class__.__name__
|
||||||
|
name = property(__get_name)
|
||||||
|
|
||||||
|
def __get_cli_name(self):
|
||||||
|
return self.name.replace('_', '-')
|
||||||
|
cli_name = property(__get_cli_name)
|
||||||
|
|
||||||
|
|
||||||
|
class Command(Named):
|
||||||
|
|
||||||
def normalize(self, kw):
|
def normalize(self, kw):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
@@ -35,11 +49,11 @@ class Command(object):
|
|||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def __call__(self, **kw):
|
def __call__(self, **kw):
|
||||||
kw = self.normalize(kw)
|
normalized = self.normalize(kw)
|
||||||
invalid = self.validate(kw)
|
invalid = self.validate(normalized)
|
||||||
if invalid:
|
if invalid:
|
||||||
return invalid
|
return invalid
|
||||||
return self.execute(kw)
|
return self.execute(normalize)
|
||||||
|
|
||||||
|
|
||||||
class Argument(object):
|
class Argument(object):
|
||||||
@@ -65,7 +79,7 @@ class NameSpace(object):
|
|||||||
For example, setting an attribute the normal way will raise an exception:
|
For example, setting an attribute the normal way will raise an exception:
|
||||||
|
|
||||||
>>> ns.my_message = 'some new value'
|
>>> ns.my_message = 'some new value'
|
||||||
(raises ipalib.exceptions.SetAttributeError)
|
(raises exceptions.SetAttributeError)
|
||||||
|
|
||||||
But a programmer could still set the attribute like this:
|
But a programmer could still set the attribute like this:
|
||||||
|
|
||||||
@@ -96,7 +110,7 @@ class NameSpace(object):
|
|||||||
NameSpace has been locked; otherwise calls object.__setattr__().
|
NameSpace has been locked; otherwise calls object.__setattr__().
|
||||||
"""
|
"""
|
||||||
if self.__locked:
|
if self.__locked:
|
||||||
raise SetAttributeError(name)
|
raise exceptions.SetAttributeError(name)
|
||||||
super(NameSpace, self).__setattr__(name, value)
|
super(NameSpace, self).__setattr__(name, value)
|
||||||
|
|
||||||
def __getitem__(self, key):
|
def __getitem__(self, key):
|
||||||
@@ -134,17 +148,35 @@ class NameSpace(object):
|
|||||||
|
|
||||||
|
|
||||||
class API(object):
|
class API(object):
|
||||||
|
__commands = None
|
||||||
|
__objects = None
|
||||||
|
__locked = False
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.__c = object()
|
self.__c = {} # Proposed commands
|
||||||
self.__o = object()
|
self.__o = {} # Proposed objects
|
||||||
|
|
||||||
def __get_c(self):
|
def __get_objects(self):
|
||||||
return self.__c
|
return self.__objects
|
||||||
c = property(__get_c)
|
objects = property(__get_objects)
|
||||||
|
|
||||||
def __get_o(self):
|
def __get_commands(self):
|
||||||
return self.__o
|
return self.__commands
|
||||||
o = property(__get_o)
|
commands = property(__get_commands)
|
||||||
|
|
||||||
def register_command(self, name, callback, override=False):
|
def __merge(self, target, base, cls, override):
|
||||||
|
assert type(target) is dict
|
||||||
|
assert inspect.isclass(base)
|
||||||
|
assert inspect.isclass(cls)
|
||||||
|
assert type(override) is bool
|
||||||
|
if not issubclass(cls, base):
|
||||||
|
raise exceptions.RegistrationError(
|
||||||
|
cls,
|
||||||
|
'%s.%s' % (base.__module__, base.__name__)
|
||||||
|
)
|
||||||
|
|
||||||
|
def register_command(self, cls, override=False):
|
||||||
|
self.__merge(self.__c, Command, cls, override)
|
||||||
|
|
||||||
|
def finalize(self):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -47,3 +47,11 @@ class IPAError(Exception):
|
|||||||
|
|
||||||
class SetAttributeError(IPAError):
|
class SetAttributeError(IPAError):
|
||||||
msg = 'Cannot set %r: NameSpace does not allow attribute setting'
|
msg = 'Cannot set %r: NameSpace does not allow attribute setting'
|
||||||
|
|
||||||
|
|
||||||
|
class OverrideError(IPAError):
|
||||||
|
msg = 'Unexpected override of %r; use override=True if intended'
|
||||||
|
|
||||||
|
|
||||||
|
class RegistrationError(IPAError):
|
||||||
|
msg = '%r is not a subclass of %s'
|
||||||
|
|||||||
@@ -24,7 +24,23 @@ Unit tests for `ipalib.base` module.
|
|||||||
from ipalib import base, exceptions
|
from ipalib import base, exceptions
|
||||||
|
|
||||||
|
|
||||||
class test_NameSpace():
|
def read_only(obj, name):
|
||||||
|
"""
|
||||||
|
Check that a given property is read-only.
|
||||||
|
Returns the value of the property.
|
||||||
|
"""
|
||||||
|
assert isinstance(obj, object)
|
||||||
|
assert hasattr(obj, name)
|
||||||
|
raised = False
|
||||||
|
try:
|
||||||
|
setattr(obj, name, 'some new obj')
|
||||||
|
except AttributeError:
|
||||||
|
raised = True
|
||||||
|
assert raised
|
||||||
|
return getattr(obj, name)
|
||||||
|
|
||||||
|
|
||||||
|
class test_NameSpace:
|
||||||
"""
|
"""
|
||||||
Unit tests for `NameSpace` class.
|
Unit tests for `NameSpace` class.
|
||||||
"""
|
"""
|
||||||
@@ -149,3 +165,50 @@ class test_NameSpace():
|
|||||||
"""
|
"""
|
||||||
(kw, ns) = self.std()
|
(kw, ns) = self.std()
|
||||||
assert len(kw) == len(ns) == 3
|
assert len(kw) == len(ns) == 3
|
||||||
|
|
||||||
|
|
||||||
|
class test_Command:
|
||||||
|
def new(self):
|
||||||
|
return base.Command()
|
||||||
|
|
||||||
|
def test_fresh(self):
|
||||||
|
c = self.new()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class test_API:
|
||||||
|
"""
|
||||||
|
Unit tests for `API` class.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def new(self):
|
||||||
|
"""
|
||||||
|
Returns a new API instance.
|
||||||
|
"""
|
||||||
|
return base.API()
|
||||||
|
|
||||||
|
def test_fresh(self):
|
||||||
|
"""
|
||||||
|
Test expectations of a fresh API instance.
|
||||||
|
"""
|
||||||
|
api = self.new()
|
||||||
|
assert read_only(api, 'objects') is None
|
||||||
|
assert read_only(api, 'objects') is None
|
||||||
|
|
||||||
|
def test_register_command(self):
|
||||||
|
class my_command(base.Command):
|
||||||
|
pass
|
||||||
|
class another_command(base.Command):
|
||||||
|
pass
|
||||||
|
api = self.new()
|
||||||
|
|
||||||
|
api.register_command(my_command)
|
||||||
|
|
||||||
|
# Check that RegistrationError is raised passing something not
|
||||||
|
# sub-classed from Command:
|
||||||
|
raised = False
|
||||||
|
try:
|
||||||
|
api.register_command(object)
|
||||||
|
except exceptions.RegistrationError:
|
||||||
|
raised = True
|
||||||
|
assert raised
|
||||||
|
|||||||
Reference in New Issue
Block a user