diff options
-rw-r--r-- | ipalib/__init__.py | 1 | ||||
-rw-r--r-- | ipalib/cli.py | 7 | ||||
-rw-r--r-- | ipalib/config.py | 40 | ||||
-rw-r--r-- | ipalib/plugable.py | 54 | ||||
-rw-r--r-- | ipalib/tests/test_crud.py | 2 | ||||
-rw-r--r-- | ipalib/tests/test_frontend.py | 10 | ||||
-rw-r--r-- | ipalib/tests/test_plugable.py | 83 | ||||
-rw-r--r-- | ipalib/tests/tstutil.py | 6 |
8 files changed, 152 insertions, 51 deletions
diff --git a/ipalib/__init__.py b/ipalib/__init__.py index f0d43aadd..956e46101 100644 --- a/ipalib/__init__.py +++ b/ipalib/__init__.py @@ -61,7 +61,6 @@ import backend import config api = plugable.API( - config.default_environment(), frontend.Command, frontend.Object, frontend.Method, diff --git a/ipalib/cli.py b/ipalib/cli.py index 1a08cef41..d66e1e2eb 100644 --- a/ipalib/cli.py +++ b/ipalib/cli.py @@ -29,8 +29,7 @@ import frontend import errors import plugable import ipa_types - -from ipalib import config +import config def exit_error(error): sys.exit('ipa: ERROR: %s' % error) @@ -257,9 +256,7 @@ class CLI(object): self.print_commands() print 'Usage: ipa COMMAND' sys.exit(2) - # do parsing here, read the conf - conf_dict = config.read_config(self.api.env.conf) - self.api.env.update(conf_dict) + self.api.env.update(config.generate_env()) key = sys.argv[1] if key not in self: self.print_commands() diff --git a/ipalib/config.py b/ipalib/config.py index bb345661c..73d23c8e0 100644 --- a/ipalib/config.py +++ b/ipalib/config.py @@ -17,22 +17,31 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +import types -def default_environment(): +DEFAULT_CONF='/etc/ipa/ipa.conf' + +def generate_env(d={}): default = dict( - conf = '/etc/ipa/ipa.conf', server_context = True, query_dns = True, verbose = False, - servers = LazyIter(myservers), - realm = LazyProp(myrealm), - domain = LazyProp(mydomain), + servers = LazyIter(get_servers), + realm = LazyProp(get_realm), + domain = LazyProp(get_domain), ) + for key, value in d.iteritems(): + if key in default and type(default[key]) in (LazyIter, LazyProp): + default[key].set_value(value) + else: + default[key] = value + return default class LazyProp(object): def __init__(self, func, value=None): + assert isinstance(func, types.FunctionType) self._func = func self._value = value @@ -40,26 +49,26 @@ class LazyProp(object): self._value = value def get_value(self): - if self._value is None: + if self._value == None: return self._func() else: return self._value -# FIXME: make sure to eliminate duplicates class LazyIter(LazyProp): def get_value(self): - if self._value is not None: - if type(self._value) is tuple: + if self._value != None: + if type(self._value) == tuple: for item in self._value: yield item else: yield self._value for item in self._func(): - yield item + if not self._value or item not in self._value: + yield item -def read_config(file): +def read_config(file=DEFAULT_CONF): assert isinstance(file, basestring) # open the file and read configuration, return a dict # for now, these are here just for testing purposes @@ -67,18 +76,15 @@ def read_config(file): # these functions are here just to "emulate" dns resolving for now -def mydomain(): +def get_domain(): return "ipatest.com" -def myrealm(): +def get_realm(): return "IPATEST.COM" -def myservers(): - # print is here to demonstrate that the querying will occur only when it is - # really needed - print "Querying DNS" +def get_servers(): yield "server.ipatest.com" yield "backup.ipatest.com" yield "fake.ipatest.com" diff --git a/ipalib/plugable.py b/ipalib/plugable.py index 98a74dfa7..ffe4a11f1 100644 --- a/ipalib/plugable.py +++ b/ipalib/plugable.py @@ -692,32 +692,50 @@ class Registrar(DictProxy): self.__registered.add(klass) -class Environment(dict): +class Environment(object): + def __init__(self): + object.__setattr__(self, '_Environment__map', {}) + + def __setattr__(self, name, value): + self[name] = value + + def __getattr__(self, name): + return self[name] + + def __delattr__(self, name): + del self[name] + def __getitem__(self, key): - val = super(Environment, self).__getitem__(key) + val = self.__map[key] if hasattr(val, 'get_value'): return val.get_value() else: return val def __setitem__(self, key, value): - if key in self: - super_value = super(Environment, self).__getitem__(key) - - if key in self and hasattr(super_value, 'set_value'): - super_value.set_value(value) - else: - super(Environment, self).__setitem__(key, value) + if key in self or hasattr(self, key): + raise AttributeError('cannot overwrite %s.%s' % + (self.__class__.__name__, key) + ) + self.__map[key] = value + + def __delitem__(self, key): + raise AttributeError('read-only: cannot del %s.%s' % + (self.__class__.__name__, key) + ) - def __getattr__(self, name): - return self[name] + def __contains__(self, key): + return key in self.__map - def __setattr__(self, name, value): - self[name] = value + def __iter__(self): + for key in self.__map: + yield key - def update(self, d): - assert isinstance(d, dict) - for key, value in d.iteritems(): + def update(self, new_vals, ignore_errors = False): + assert type(new_vals) == dict + for key, value in new_vals.iteritems(): + if key in self and ignore_errors: + continue self[key] = value @@ -727,10 +745,10 @@ class API(DictProxy): """ __finalized = False - def __init__(self, default_env, *allowed): + def __init__(self, *allowed): self.__d = dict() self.register = Registrar(*allowed) - self.env = Environment(default_env) + self.env = Environment() super(API, self).__init__(self.__d) def finalize(self): diff --git a/ipalib/tests/test_crud.py b/ipalib/tests/test_crud.py index df85253b8..9355f237e 100644 --- a/ipalib/tests/test_crud.py +++ b/ipalib/tests/test_crud.py @@ -26,11 +26,11 @@ from ipalib import crud, frontend, plugable, config def get_api(): api = plugable.API( - config.default_environment(), frontend.Object, frontend.Method, frontend.Property, ) + api.env.update(config.generate_env()) class user(frontend.Object): takes_params = ( 'givenname', diff --git a/ipalib/tests/test_frontend.py b/ipalib/tests/test_frontend.py index e3dd04fac..c70cc00d7 100644 --- a/ipalib/tests/test_frontend.py +++ b/ipalib/tests/test_frontend.py @@ -732,7 +732,8 @@ class test_Command(ClassChecker): kw = dict(how_are='you', on_this='fine day?') # Test in server context: - api = plugable.API(dict(server_context=True), self.cls) + api = plugable.API(self.cls) + api.env.update(dict(server_context=True)) api.finalize() o = my_cmd() o.set_api(api) @@ -741,7 +742,8 @@ class test_Command(ClassChecker): assert o.run.im_func is my_cmd.execute.im_func # Test in non-server context - api = plugable.API(dict(server_context=False), self.cls) + api = plugable.API(self.cls) + api.env.update(dict(server_context=False)) api.finalize() o = my_cmd() o.set_api(api) @@ -868,10 +870,10 @@ class test_Object(ClassChecker): Test the `frontend.Object.primary_key` attribute. """ api = plugable.API( - config.default_environment(), frontend.Method, frontend.Property, ) + api.env.update(config.generate_env()) api.finalize() # Test with no primary keys: @@ -923,12 +925,12 @@ class test_Object(ClassChecker): Test the `frontend.Object.backend` attribute. """ api = plugable.API( - config.default_environment(), frontend.Object, frontend.Method, frontend.Property, backend.Backend, ) + api.env.update(config.generate_env()) class ldap(backend.Backend): whatever = 'It worked!' api.register(ldap) diff --git a/ipalib/tests/test_plugable.py b/ipalib/tests/test_plugable.py index 9be6b343e..fd3c3c887 100644 --- a/ipalib/tests/test_plugable.py +++ b/ipalib/tests/test_plugable.py @@ -620,6 +620,84 @@ class test_NameSpace(ClassChecker): 'NameSpace(<%d members>, sort=%r)' % (cnt, sort) +def test_Environment(): + """ + Tests the `plugable.Environment` class. + """ + # This has to be the same as iter_cnt + control_cnt = 0 + class prop_class: + def __init__(self, val): + self._val = val + def get_value(self): + return self._val + + class iter_class(prop_class): + # Increment this for each time iter_class yields + iter_cnt = 0 + def get_value(self): + for item in self._val: + self.__class__.iter_cnt += 1 + yield item + + # Tests for basic functionality + basic_tests = ( + ('a', 1), + ('b', 'basic_foo'), + ('c', ('basic_bar', 'basic_baz')), + ) + # Tests with prop classes + prop_tests = ( + ('d', prop_class(2), 2), + ('e', prop_class('prop_foo'), 'prop_foo'), + ('f', prop_class(('prop_bar', 'prop_baz')), ('prop_bar', 'prop_baz')), + ) + # Tests with iter classes + iter_tests = ( + ('g', iter_class((3, 4, 5)), (3, 4, 5)), + ('h', iter_class(('iter_foo', 'iter_bar', 'iter_baz')), + ('iter_foo', 'iter_bar', 'iter_baz') + ), + ) + + # Set all the values + env = plugable.Environment() + for name, val in basic_tests: + env[name] = val + for name, val, dummy in prop_tests: + env[name] = val + for name, val, dummy in iter_tests: + env[name] = val + + # Test if the values are correct + for name, val in basic_tests: + assert env[name] == val + for name, dummy, val in prop_tests: + assert env[name] == val + # Test if the get_value() function is called only when needed + for name, dummy, correct_values in iter_tests: + values_in_env = [] + for val in env[name]: + control_cnt += 1 + assert iter_class.iter_cnt == control_cnt + values_in_env.append(val) + assert tuple(values_in_env) == correct_values + + # Test __setattr__() + env.spam = 'ham' + assert env.spam == 'ham' + + # Test if we throw AttributeError exception when trying to overwrite + # existing value, or delete it + raises(AttributeError, setitem, env, 'a', 1) + raises(AttributeError, setattr, env, 'a', 1) + raises(AttributeError, delitem, env, 'a') + raises(AttributeError, delattr, env, 'a') + raises(AttributeError, plugable.Environment.update, env, dict(a=1000)) + # This should be silently ignored + env.update(dict(a=1000), True) + assert env.a != 1000 + def test_Registrar(): class Base1(object): pass @@ -722,6 +800,7 @@ def test_Registrar(): assert issubclass(klass, base) + def test_API(): assert issubclass(plugable.API, plugable.ReadOnly) @@ -742,7 +821,7 @@ def test_API(): def method(self, n): return n + 1 - api = plugable.API(dict(), base0, base1) + api = plugable.API(base0, base1) r = api.register assert isinstance(r, plugable.Registrar) assert read_only(api, 'register') is r @@ -800,7 +879,7 @@ def test_API(): # Test with base class that doesn't request a proxy class NoProxy(plugable.Plugin): __proxy__ = False - api = plugable.API(dict(), NoProxy) + api = plugable.API(NoProxy) class plugin0(NoProxy): pass api.register(plugin0) diff --git a/ipalib/tests/tstutil.py b/ipalib/tests/tstutil.py index 7586d08c5..743716a08 100644 --- a/ipalib/tests/tstutil.py +++ b/ipalib/tests/tstutil.py @@ -55,7 +55,7 @@ def raises(exception, callback, *args, **kw): def getitem(obj, key): """ - Works like getattr but for dictionary interface. Uses this in combination + Works like getattr but for dictionary interface. Use this in combination with raises() to test that, for example, KeyError is raised. """ return obj[key] @@ -63,7 +63,7 @@ def getitem(obj, key): def setitem(obj, key, value): """ - Works like setattr but for dictionary interface. Uses this in combination + Works like setattr but for dictionary interface. Use this in combination with raises() to test that, for example, TypeError is raised. """ obj[key] = value @@ -71,7 +71,7 @@ def setitem(obj, key, value): def delitem(obj, key): """ - Works like delattr but for dictionary interface. Uses this in combination + Works like delattr but for dictionary interface. Use this in combination with raises() to test that, for example, TypeError is raised. """ del obj[key] |