diff options
author | Jason Gerard DeRose <jderose@redhat.com> | 2008-12-23 01:11:03 -0700 |
---|---|---|
committer | Jason Gerard DeRose <jderose@redhat.com> | 2008-12-23 01:11:03 -0700 |
commit | 16526142f36e81f4d8a767f339c559188485f756 (patch) | |
tree | fbbaf113d3ebe4548b4ea0d49901414a42034297 | |
parent | fd43b39145382b96cd2e0d0da3d5dcbe0d3a4a2a (diff) | |
download | freeipa-16526142f36e81f4d8a767f339c559188485f756.tar.gz freeipa-16526142f36e81f4d8a767f339c559188485f756.tar.xz freeipa-16526142f36e81f4d8a767f339c559188485f756.zip |
Finished Env class docstring; more organizational cleanup in Env and its unit tests
-rw-r--r-- | ipalib/config.py | 159 | ||||
-rw-r--r-- | tests/test_ipalib/test_config.py | 122 |
2 files changed, 171 insertions, 110 deletions
diff --git a/ipalib/config.py b/ipalib/config.py index 9fe02cb33..c39d99a99 100644 --- a/ipalib/config.py +++ b/ipalib/config.py @@ -39,14 +39,14 @@ class Env(object): Store and retrieve environment variables. First an foremost, the `Env` class provides a handy container for - environment variables. These variables can be both set and retrieved as - either attributes or as dictionary items. + environment variables. These variables can be both set *and* retrieved + either as attributes *or* as dictionary items. For example, we can set a variable as an attribute: >>> env = Env() >>> env.attr = 'I was set as an attribute.' - >>> env.attr # Retrieve as an attribute + >>> env.attr 'I was set as an attribute.' >>> env['attr'] # Also retrieve as a dictionary item 'I was set as an attribute.' @@ -54,7 +54,7 @@ class Env(object): Or we can set a variable as a dictionary item: >>> env['item'] = 'I was set as a dictionary item.' - >>> env['item'] # Retrieve as a dictionary item + >>> env['item'] 'I was set as a dictionary item.' >>> env.item # Also retrieve as an attribute 'I was set as a dictionary item.' @@ -65,11 +65,12 @@ class Env(object): values of specific types to be set easily from configuration files or command-line options. - The ``True``, ``False``, and ``None`` constants can be specified with a - string that matches what ``repr()`` would return. For example: + So in addition to their actual values, the ``True``, ``False``, and ``None`` + constants can be specified with an ``str`` equal to what ``repr()`` would + return. For example: >>> env.true = True - >>> env.also_true = 'True' + >>> env.also_true = 'True' # Equal to repr(True) >>> env.true True >>> env.also_true @@ -77,7 +78,7 @@ class Env(object): Note that the automatic type conversion is case sensitive. For example: - >>> env.false = 'false' # Doesn't match repr(False) + >>> env.false = 'false' # Not equal to repr(False)! >>> env.false 'false' @@ -88,19 +89,25 @@ class Env(object): >>> env.lucky 7 - Also, leading and trailing white-space is automatically stripped from - ``str`` values. For example: + Leading and trailing white-space is automatically stripped from ``str`` + values. For example: >>> env.message = ' Hello! ' # Surrounded by double spaces >>> env.message 'Hello!' - >>> env.number = '42 ' # Still converted to an int + >>> env.number = ' 42 ' # Still converted to an int >>> env.number 42 - >>> env.actually_false = ' False' # Still matches repr(False) + >>> env.actually_false = ' False ' # Still equal to repr(False) >>> env.actually_false False + Also, empty ``str`` instances are converted to ``None``. For example: + + >>> env.empty = '' + >>> env.empty is None + True + `Env` variables are all set-once (first-one-wins). Once a variable has been set, trying to override it will raise an ``AttributeError``. For example: @@ -110,19 +117,56 @@ class Env(object): ... AttributeError: cannot override Env.date value 'First' with 'Second' - An `Env` instance can also be *locked*, after which no further variables can - be set. Trying to set variables on a locked `Env` instance will also raise + An `Env` instance can be *locked*, after which no further variables can be + set. Trying to set variables on a locked `Env` instance will also raise an ``AttributeError``. For example: >>> env = Env() - >>> env.var1 = 'This will work.' + >>> env.okay = 'This will work.' >>> env.__lock__() - >>> env.var2 = 'This wont work!' + >>> env.nope = 'This wont work!' Traceback (most recent call last): ... - AttributeError: locked: cannot set Env.var2 to 'This wont work!' + AttributeError: locked: cannot set Env.nope to 'This wont work!' - Finish me! + `Env` instances also provide standard container emulation for membership + testing, counting, and iteration. For example: + + >>> env = Env() + >>> 'key1' in env # Has key1 been set? + False + >>> env.key1 = 'value 1' + >>> 'key1' in env + True + >>> env.key2 = 'value 2' + >>> len(env) # How many variables have been set? + 2 + >>> list(env) # What variables have been set? + ['key1', 'key2'] + + Lastly, in addition to all the handy container functionality, the `Env` + class provides high-level methods for bootstraping a fresh `Env` instance + into one containing all the run-time and configuration information needed + by the built-in freeIPA plugins. + + These are the `Env` bootstraping methods, in the order they must be called: + + 1. `Env._bootstrap()` - initialize the run-time variables and then + merge-in variables specified on the command-line. + + 2. `Env._finalize_core()` - merge-in variables from the configuration + files and then merge-in variables from the internal defaults, after + which at least all the standard variables will be set. After this + method is called, the plugins will be loaded, during which 3rd-party + plugins can set additional variables they may need. + + 3. `Env._finalize()` - one last chance to merge-in variables and then + the instance is locked. After this method is called, no more + environment variables can be set during the remaining life of the + process. + + However, normally none of the above methods are called directly and only + `ipalib.plugable.API.bootstrap()` is called instead. """ __locked = False @@ -131,6 +175,22 @@ class Env(object): object.__setattr__(self, '_Env__d', {}) object.__setattr__(self, '_Env__done', set()) + def __lock__(self): + """ + Prevent further changes to environment. + """ + if self.__locked is True: + raise StandardError( + '%s.__lock__() already called' % self.__class__.__name__ + ) + object.__setattr__(self, '_Env__locked', True) + + def __islocked__(self): + """ + Return ``True`` if locked. + """ + return self.__locked + def __setattr__(self, name, value): """ Set the attribute named ``name`` to ``value``. @@ -152,16 +212,14 @@ class Env(object): raise AttributeError(OVERRIDE_ERROR % (self.__class__.__name__, key, self.__d[key], value) ) - if hasattr(self, key): - raise AttributeError(OVERRIDE_ERROR % - (self.__class__.__name__, key, getattr(self, key), value) - ) + assert not hasattr(self, key) if isinstance(value, basestring): value = str(value.strip()) m = { 'True': True, 'False': False, 'None': None, + '': None, } if value in m: value = m[value] @@ -185,6 +243,25 @@ class Env(object): DEL_ERROR % (self.__class__.__name__, name) ) + def __contains__(self, key): + """ + Return True if instance contains ``key``; otherwise return False. + """ + return key in self.__d + + def __len__(self): + """ + Return number of variables currently set. + """ + return len(self.__d) + + def __iter__(self): + """ + Iterate through keys in ascending order. + """ + for key in sorted(self.__d): + yield key + def __doing(self, name): if name in self.__done: raise StandardError( @@ -304,41 +381,3 @@ class Env(object): self[key] = value i += 1 return (i, len(items)) - - def __lock__(self): - """ - Prevent further changes to environment. - """ - if self.__locked is True: - raise StandardError( - '%s.__lock__() already called' % self.__class__.__name__ - ) - object.__setattr__(self, '_Env__locked', True) - - def __islocked__(self): - return self.__locked - - - - - - - - def __contains__(self, key): - """ - Return True if instance contains ``key``; otherwise return False. - """ - return key in self.__d - - def __len__(self): - """ - Return number of variables currently set. - """ - return len(self.__d) - - def __iter__(self): - """ - Iterate through keys in ascending order. - """ - for key in sorted(self.__d): - yield key diff --git a/tests/test_ipalib/test_config.py b/tests/test_ipalib/test_config.py index 8884697e0..acdcfaae8 100644 --- a/tests/test_ipalib/test_config.py +++ b/tests/test_ipalib/test_config.py @@ -32,7 +32,6 @@ from ipalib.constants import TYPE_ERROR, OVERRIDE_ERROR, SET_ERROR, DEL_ERROR from ipalib import config, constants - # Valid environment variables in (key, raw, value) tuples: # key: the name of the environment variable # raw: the value being set (possibly a string repr) @@ -48,6 +47,7 @@ good_vars = ( ('false_repr', ' False ', False), ('none', None, None), ('none_repr', ' None ', None), + ('empty', '', None), # These verify that the implied conversion is case-sensitive: ('not_true', ' true ', 'true'), @@ -56,7 +56,6 @@ good_vars = ( ) - # Random base64-encoded data to simulate a misbehaving config file. config_bad = """ /9j/4AAQSkZJRgABAQEAlgCWAAD//gAIT2xpdmVy/9sAQwAQCwwODAoQDg0OEhEQExgoGhgWFhgx @@ -96,6 +95,7 @@ i1Jmt0+5dfuOLbANB2H6MjzNzc2zv5ji1g2+5/MYnbb+Yh+T0kubUY940UUbUWtRpJN8w1CfebkK WfUu+/mDOAGOjsRo0UkIo+pPl6Rckl7ehuR1INGAj9u0kW2nXvK45YlQp1odukaICSAjgSQWf//Z """ + # A config file that tries to override some standard vars: config_override = """ [global] @@ -108,6 +108,7 @@ key2 = var2 key3 = var3 """ + # A config file that tests the automatic type conversion config_good = """ [global] @@ -117,6 +118,7 @@ no = False number = 42 """ + # A default config file to make sure it does not overwrite the explicit one config_default = """ [global] @@ -133,24 +135,26 @@ class test_Env(ClassChecker): _cls = config.Env - def new(self): - """ - Set os.environ['HOME'] to a tempdir. - - Returns tuple with new Env instance and the TempHome instance. - """ - home = TempHome() - return (self.cls(), home) - def test_init(self): """ Test the `ipalib.config.Env.__init__` method. """ - (o, home) = self.new() + o = self.cls() assert list(o) == [] assert len(o) == 0 assert o.__islocked__() is False + def test_lock(self): + """ + Test the `ipalib.config.Env.__lock__` method. + """ + o = self.cls() + assert o._Env__locked is False + o.__lock__() + assert o._Env__locked is True + e = raises(StandardError, o.__lock__) + assert str(e) == 'Env.__lock__() already called' + def test_setattr(self): """ Test the `ipalib.config.Env.__setattr__` method. @@ -227,7 +231,60 @@ class test_Env(ClassChecker): e = raises(AttributeError, delitem, o, key) assert str(e) == '__delitem__' + def test_contains(self): + """ + Test the `ipalib.config.Env.__contains__` method. + """ + o = self.cls() + items = [ + ('one', 1), + ('two', 2), + ('three', 3), + ('four', 4), + ] + for (key, value) in items: + assert key not in o + o[key] = value + assert key in o + + def test_len(self): + """ + Test the `ipalib.config.Env.__len__` method. + """ + o = self.cls() + assert len(o) == 0 + for i in xrange(1, 11): + key = 'key%d' % i + value = 'value %d' % i + o[key] = value + assert o[key] is value + assert len(o) == i + + def test_iter(self): + """ + Test the `ipalib.config.Env.__iter__` method. + """ + o = self.cls() + default_keys = tuple(o) + keys = ('one', 'two', 'three', 'four', 'five') + for key in keys: + o[key] = 'the value' + assert list(o) == sorted(keys + default_keys) + + def new(self): + """ + Set os.environ['HOME'] to a tempdir. + + Returns tuple with new Env instance and the TempHome instance. This + helper method is used in testing the bootstrap related methods below. + """ + home = TempHome() + return (self.cls(), home) + def bootstrap(self, **overrides): + """ + Helper method used in testing bootstrap related methods below. + """ (o, home) = self.new() assert o._isdone('_bootstrap') is False o._bootstrap(**overrides) @@ -290,6 +347,9 @@ class test_Env(ClassChecker): assert o[key] == value def finalize_core(self, **defaults): + """ + Helper method used in testing `Env._finalize_core`. + """ (o, home) = self.new() assert o._isdone('_finalize_core') is False o._finalize_core(**defaults) @@ -462,41 +522,3 @@ class test_Env(ClassChecker): assert o.yes is True assert o.no is False assert o.number == 42 - - def test_lock(self): - """ - Test the `ipalib.config.Env.__lock__` method. - """ - o = self.cls() - assert o._Env__locked is False - o.__lock__() - assert o._Env__locked is True - e = raises(StandardError, o.__lock__) - assert str(e) == 'Env.__lock__() already called' - - def test_contains(self): - """ - Test the `ipalib.config.Env.__contains__` method. - """ - o = self.cls() - items = [ - ('one', 1), - ('two', 2), - ('three', 3), - ('four', 4), - ] - for (key, value) in items: - assert key not in o - o[key] = value - assert key in o - - def test_iter(self): - """ - Test the `ipalib.config.Env.__iter__` method. - """ - o = self.cls() - default_keys = tuple(o) - keys = ('one', 'two', 'three', 'four', 'five') - for key in keys: - o[key] = 'the value' - assert list(o) == sorted(keys + default_keys) |