summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJason Gerard DeRose <jderose@redhat.com>2008-12-23 01:11:03 -0700
committerJason Gerard DeRose <jderose@redhat.com>2008-12-23 01:11:03 -0700
commit16526142f36e81f4d8a767f339c559188485f756 (patch)
treefbbaf113d3ebe4548b4ea0d49901414a42034297
parentfd43b39145382b96cd2e0d0da3d5dcbe0d3a4a2a (diff)
downloadfreeipa-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.py159
-rw-r--r--tests/test_ipalib/test_config.py122
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)