From 91adc9c2d060b65d96a8515d08fc7192be79da83 Mon Sep 17 00:00:00 2001 From: Jason Gerard DeRose Date: Sat, 19 Jul 2008 07:43:48 +0000 Subject: 6: Fleshed out API.register_command, made correpsonding unit tests much more rigorous --- ipalib/base.py | 45 ++++++++++++++++++++----------------- ipalib/exceptions.py | 12 ++++++---- ipalib/tests/test_base.py | 57 ++++++++++++++++++++++++++++++++++++----------- 3 files changed, 77 insertions(+), 37 deletions(-) (limited to 'ipalib') diff --git a/ipalib/base.py b/ipalib/base.py index c7a0cf99f..97fb7c90c 100644 --- a/ipalib/base.py +++ b/ipalib/base.py @@ -28,12 +28,8 @@ import exceptions class Named(object): prefix = None - @classmethod - def clsname(cls): - return cls.__name__ - def __init__(self): - clsname = self.clsname() + clsname = self.__class__.__name__ assert type(self.prefix) is str prefix = self.prefix + '_' if not clsname.startswith(prefix): @@ -50,7 +46,8 @@ class Named(object): name_cli = property(__get_name_cli) -class Command(object): +class Command(Named): + prefix = 'cmd' def normalize(self, kw): raise NotImplementedError @@ -92,7 +89,7 @@ class NameSpace(object): For example, setting an attribute the normal way will raise an exception: >>> ns.my_message = 'some new value' - (raises exceptions.SetAttributeError) + (raises exceptions.SetError) But a programmer could still set the attribute like this: @@ -123,7 +120,7 @@ class NameSpace(object): NameSpace has been locked; otherwise calls object.__setattr__(). """ if self.__locked: - raise exceptions.SetAttributeError(name) + raise exceptions.SetError(name) super(NameSpace, self).__setattr__(name, value) def __getitem__(self, key): @@ -166,8 +163,9 @@ class API(object): __locked = False def __init__(self): - self.__c = {} # Proposed commands - self.__o = {} # Proposed objects + self.__classes = set() + self.__names = set() + self.__stage = {} def __get_objects(self): return self.__objects @@ -177,19 +175,26 @@ class API(object): return self.__commands commands = property(__get_commands) - def __merge(self, target, base, cls, override): - assert type(target) is dict - assert inspect.isclass(base) - assert inspect.isclass(cls) + def __merge(self, base, cls, override): + assert issubclass(base, Named) assert type(override) is bool - if not issubclass(cls, base): - raise exceptions.RegistrationError( - cls, - '%s.%s' % (base.__module__, base.__name__) - ) + if not (inspect.isclass(cls) and issubclass(cls, base)): + raise exceptions.RegistrationError(cls, base.__name__) + if cls in self.__classes: + raise exceptions.DuplicateError(cls.__name__, id(cls)) + if cls.__name__ in self.__names and not override: + raise exceptions.OverrideError(cls.__name__) + self.__classes.add(cls) + self.__names.add(cls.__name__) + if base not in self.__stage: + self.__stage[base.prefix] = {} + self.__stage[base.prefix][cls.__name__] = cls + def register_command(self, cls, override=False): - self.__merge(self.__c, Command, cls, override) + self.__merge(Command, cls, override) def finalize(self): pass + #i = cls() + #assert cls.__name__ == (base.prefix + '_' + i.name) diff --git a/ipalib/exceptions.py b/ipalib/exceptions.py index 4150d7124..4584c1eea 100644 --- a/ipalib/exceptions.py +++ b/ipalib/exceptions.py @@ -45,16 +45,20 @@ class IPAError(Exception): return self.msg % self.kw -class SetAttributeError(IPAError): - msg = 'Cannot set %r: NameSpace does not allow attribute setting' +class SetError(IPAError): + msg = 'setting %r, but NameSpace does not allow attribute setting' class OverrideError(IPAError): - msg = 'Unexpected override of %r; use override=True if intended' + msg = 'unexpected override of %r (use override=True if intended)' + + +class DuplicateError(IPAError): + msg = 'class %r at %d was already registered' class RegistrationError(IPAError): - msg = '%r is not a subclass of %s' + msg = '%r must be a subclass of %s' class PrefixError(IPAError): diff --git a/ipalib/tests/test_base.py b/ipalib/tests/test_base.py index e0e2d8e88..bf727ed6f 100644 --- a/ipalib/tests/test_base.py +++ b/ipalib/tests/test_base.py @@ -184,7 +184,7 @@ class test_NameSpace: raised = False try: setattr(ns, key, value) - except exceptions.SetAttributeError: + except exceptions.SetError: raised = True assert raised assert getattr(ns, key, None) != value @@ -238,13 +238,17 @@ class test_NameSpace: assert len(kw) == len(ns) == 3 -class test_Command: - def new(self): - return base.Command() +class test_Command(ClassChecker): + class cmd_some_command(base.Command): + pass + cls = cmd_some_command def test_fresh(self): c = self.new() - + assert isinstance(c, base.Named) + assert c.name == 'some_command' + assert c.name_cli == 'some-command' + assert callable(c) class test_API: @@ -267,19 +271,46 @@ class test_API: assert read_only(api, 'objects') is None def test_register_command(self): - class my_command(base.Command): + api = self.new() + + class cmd_my_command(base.Command): pass - class another_command(base.Command): + class cmd_another_command(base.Command): pass - api = self.new() - api.register_command(my_command) + # Check that RegistrationError is raised when registering anything + # other than a subclass of Command: + for obj in [object, cmd_my_command()]: + raised = False + try: + api.register_command(obj) + except exceptions.RegistrationError: + raised = True + assert raised - # Check that RegistrationError is raised passing something not - # sub-classed from Command: + # Check that command registration works: + api.register_command(cmd_my_command) + api.register_command(cmd_another_command) + + # Check that DuplicateError is raised when registering the same class + # twice: + raised = False + try: + api.register_command(cmd_my_command) + except exceptions.DuplicateError: + raised = True + assert raised + + # Check that OverrideError is raised when registering same name + # without override = True: + class cmd_my_command(base.Command): + pass raised = False try: - api.register_command(object) - except exceptions.RegistrationError: + api.register_command(cmd_my_command) + except exceptions.OverrideError: raised = True assert raised + + # Check that override=True works: + api.register_command(cmd_my_command, override=True) -- cgit