mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
Finished Env class docstring; more organizational cleanup in Env and its unit tests
This commit is contained in:
parent
fd43b39145
commit
16526142f3
157
ipalib/config.py
157
ipalib/config.py
@ -39,14 +39,14 @@ class Env(object):
|
|||||||
Store and retrieve environment variables.
|
Store and retrieve environment variables.
|
||||||
|
|
||||||
First an foremost, the `Env` class provides a handy container for
|
First an foremost, the `Env` class provides a handy container for
|
||||||
environment variables. These variables can be both set and retrieved as
|
environment variables. These variables can be both set *and* retrieved
|
||||||
either attributes or as dictionary items.
|
either as attributes *or* as dictionary items.
|
||||||
|
|
||||||
For example, we can set a variable as an attribute:
|
For example, we can set a variable as an attribute:
|
||||||
|
|
||||||
>>> env = Env()
|
>>> env = Env()
|
||||||
>>> env.attr = 'I was set as an attribute.'
|
>>> env.attr = 'I was set as an attribute.'
|
||||||
>>> env.attr # Retrieve as an attribute
|
>>> env.attr
|
||||||
'I was set as an attribute.'
|
'I was set as an attribute.'
|
||||||
>>> env['attr'] # Also retrieve as a dictionary item
|
>>> env['attr'] # Also retrieve as a dictionary item
|
||||||
'I was set as an attribute.'
|
'I was set as an attribute.'
|
||||||
@ -54,7 +54,7 @@ class Env(object):
|
|||||||
Or we can set a variable as a dictionary item:
|
Or we can set a variable as a dictionary item:
|
||||||
|
|
||||||
>>> env['item'] = 'I was set 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.'
|
'I was set as a dictionary item.'
|
||||||
>>> env.item # Also retrieve as an attribute
|
>>> env.item # Also retrieve as an attribute
|
||||||
'I was set as a dictionary item.'
|
'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
|
values of specific types to be set easily from configuration files or
|
||||||
command-line options.
|
command-line options.
|
||||||
|
|
||||||
The ``True``, ``False``, and ``None`` constants can be specified with a
|
So in addition to their actual values, the ``True``, ``False``, and ``None``
|
||||||
string that matches what ``repr()`` would return. For example:
|
constants can be specified with an ``str`` equal to what ``repr()`` would
|
||||||
|
return. For example:
|
||||||
|
|
||||||
>>> env.true = True
|
>>> env.true = True
|
||||||
>>> env.also_true = 'True'
|
>>> env.also_true = 'True' # Equal to repr(True)
|
||||||
>>> env.true
|
>>> env.true
|
||||||
True
|
True
|
||||||
>>> env.also_true
|
>>> env.also_true
|
||||||
@ -77,7 +78,7 @@ class Env(object):
|
|||||||
|
|
||||||
Note that the automatic type conversion is case sensitive. For example:
|
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
|
>>> env.false
|
||||||
'false'
|
'false'
|
||||||
|
|
||||||
@ -88,8 +89,8 @@ class Env(object):
|
|||||||
>>> env.lucky
|
>>> env.lucky
|
||||||
7
|
7
|
||||||
|
|
||||||
Also, leading and trailing white-space is automatically stripped from
|
Leading and trailing white-space is automatically stripped from ``str``
|
||||||
``str`` values. For example:
|
values. For example:
|
||||||
|
|
||||||
>>> env.message = ' Hello! ' # Surrounded by double spaces
|
>>> env.message = ' Hello! ' # Surrounded by double spaces
|
||||||
>>> env.message
|
>>> env.message
|
||||||
@ -97,10 +98,16 @@ class Env(object):
|
|||||||
>>> env.number = ' 42 ' # Still converted to an int
|
>>> env.number = ' 42 ' # Still converted to an int
|
||||||
>>> env.number
|
>>> env.number
|
||||||
42
|
42
|
||||||
>>> env.actually_false = ' False' # Still matches repr(False)
|
>>> env.actually_false = ' False ' # Still equal to repr(False)
|
||||||
>>> env.actually_false
|
>>> env.actually_false
|
||||||
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
|
`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:
|
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'
|
AttributeError: cannot override Env.date value 'First' with 'Second'
|
||||||
|
|
||||||
An `Env` instance can also be *locked*, after which no further variables can
|
An `Env` instance can be *locked*, after which no further variables can be
|
||||||
be set. Trying to set variables on a locked `Env` instance will also raise
|
set. Trying to set variables on a locked `Env` instance will also raise
|
||||||
an ``AttributeError``. For example:
|
an ``AttributeError``. For example:
|
||||||
|
|
||||||
>>> env = Env()
|
>>> env = Env()
|
||||||
>>> env.var1 = 'This will work.'
|
>>> env.okay = 'This will work.'
|
||||||
>>> env.__lock__()
|
>>> env.__lock__()
|
||||||
>>> env.var2 = 'This wont work!'
|
>>> env.nope = 'This wont work!'
|
||||||
Traceback (most recent call last):
|
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
|
__locked = False
|
||||||
@ -131,6 +175,22 @@ class Env(object):
|
|||||||
object.__setattr__(self, '_Env__d', {})
|
object.__setattr__(self, '_Env__d', {})
|
||||||
object.__setattr__(self, '_Env__done', set())
|
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):
|
def __setattr__(self, name, value):
|
||||||
"""
|
"""
|
||||||
Set the attribute named ``name`` to ``value``.
|
Set the attribute named ``name`` to ``value``.
|
||||||
@ -152,16 +212,14 @@ class Env(object):
|
|||||||
raise AttributeError(OVERRIDE_ERROR %
|
raise AttributeError(OVERRIDE_ERROR %
|
||||||
(self.__class__.__name__, key, self.__d[key], value)
|
(self.__class__.__name__, key, self.__d[key], value)
|
||||||
)
|
)
|
||||||
if hasattr(self, key):
|
assert not hasattr(self, key)
|
||||||
raise AttributeError(OVERRIDE_ERROR %
|
|
||||||
(self.__class__.__name__, key, getattr(self, key), value)
|
|
||||||
)
|
|
||||||
if isinstance(value, basestring):
|
if isinstance(value, basestring):
|
||||||
value = str(value.strip())
|
value = str(value.strip())
|
||||||
m = {
|
m = {
|
||||||
'True': True,
|
'True': True,
|
||||||
'False': False,
|
'False': False,
|
||||||
'None': None,
|
'None': None,
|
||||||
|
'': None,
|
||||||
}
|
}
|
||||||
if value in m:
|
if value in m:
|
||||||
value = m[value]
|
value = m[value]
|
||||||
@ -185,6 +243,25 @@ class Env(object):
|
|||||||
DEL_ERROR % (self.__class__.__name__, name)
|
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):
|
def __doing(self, name):
|
||||||
if name in self.__done:
|
if name in self.__done:
|
||||||
raise StandardError(
|
raise StandardError(
|
||||||
@ -304,41 +381,3 @@ class Env(object):
|
|||||||
self[key] = value
|
self[key] = value
|
||||||
i += 1
|
i += 1
|
||||||
return (i, len(items))
|
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
|
|
||||||
|
@ -32,7 +32,6 @@ from ipalib.constants import TYPE_ERROR, OVERRIDE_ERROR, SET_ERROR, DEL_ERROR
|
|||||||
from ipalib import config, constants
|
from ipalib import config, constants
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Valid environment variables in (key, raw, value) tuples:
|
# Valid environment variables in (key, raw, value) tuples:
|
||||||
# key: the name of the environment variable
|
# key: the name of the environment variable
|
||||||
# raw: the value being set (possibly a string repr)
|
# raw: the value being set (possibly a string repr)
|
||||||
@ -48,6 +47,7 @@ good_vars = (
|
|||||||
('false_repr', ' False ', False),
|
('false_repr', ' False ', False),
|
||||||
('none', None, None),
|
('none', None, None),
|
||||||
('none_repr', ' None ', None),
|
('none_repr', ' None ', None),
|
||||||
|
('empty', '', None),
|
||||||
|
|
||||||
# These verify that the implied conversion is case-sensitive:
|
# These verify that the implied conversion is case-sensitive:
|
||||||
('not_true', ' true ', 'true'),
|
('not_true', ' true ', 'true'),
|
||||||
@ -56,7 +56,6 @@ good_vars = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Random base64-encoded data to simulate a misbehaving config file.
|
# Random base64-encoded data to simulate a misbehaving config file.
|
||||||
config_bad = """
|
config_bad = """
|
||||||
/9j/4AAQSkZJRgABAQEAlgCWAAD//gAIT2xpdmVy/9sAQwAQCwwODAoQDg0OEhEQExgoGhgWFhgx
|
/9j/4AAQSkZJRgABAQEAlgCWAAD//gAIT2xpdmVy/9sAQwAQCwwODAoQDg0OEhEQExgoGhgWFhgx
|
||||||
@ -96,6 +95,7 @@ i1Jmt0+5dfuOLbANB2H6MjzNzc2zv5ji1g2+5/MYnbb+Yh+T0kubUY940UUbUWtRpJN8w1CfebkK
|
|||||||
WfUu+/mDOAGOjsRo0UkIo+pPl6Rckl7ehuR1INGAj9u0kW2nXvK45YlQp1odukaICSAjgSQWf//Z
|
WfUu+/mDOAGOjsRo0UkIo+pPl6Rckl7ehuR1INGAj9u0kW2nXvK45YlQp1odukaICSAjgSQWf//Z
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
# A config file that tries to override some standard vars:
|
# A config file that tries to override some standard vars:
|
||||||
config_override = """
|
config_override = """
|
||||||
[global]
|
[global]
|
||||||
@ -108,6 +108,7 @@ key2 = var2
|
|||||||
key3 = var3
|
key3 = var3
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
# A config file that tests the automatic type conversion
|
# A config file that tests the automatic type conversion
|
||||||
config_good = """
|
config_good = """
|
||||||
[global]
|
[global]
|
||||||
@ -117,6 +118,7 @@ no = False
|
|||||||
number = 42
|
number = 42
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
# A default config file to make sure it does not overwrite the explicit one
|
# A default config file to make sure it does not overwrite the explicit one
|
||||||
config_default = """
|
config_default = """
|
||||||
[global]
|
[global]
|
||||||
@ -133,24 +135,26 @@ class test_Env(ClassChecker):
|
|||||||
|
|
||||||
_cls = config.Env
|
_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):
|
def test_init(self):
|
||||||
"""
|
"""
|
||||||
Test the `ipalib.config.Env.__init__` method.
|
Test the `ipalib.config.Env.__init__` method.
|
||||||
"""
|
"""
|
||||||
(o, home) = self.new()
|
o = self.cls()
|
||||||
assert list(o) == []
|
assert list(o) == []
|
||||||
assert len(o) == 0
|
assert len(o) == 0
|
||||||
assert o.__islocked__() is False
|
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):
|
def test_setattr(self):
|
||||||
"""
|
"""
|
||||||
Test the `ipalib.config.Env.__setattr__` method.
|
Test the `ipalib.config.Env.__setattr__` method.
|
||||||
@ -227,7 +231,60 @@ class test_Env(ClassChecker):
|
|||||||
e = raises(AttributeError, delitem, o, key)
|
e = raises(AttributeError, delitem, o, key)
|
||||||
assert str(e) == '__delitem__'
|
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):
|
def bootstrap(self, **overrides):
|
||||||
|
"""
|
||||||
|
Helper method used in testing bootstrap related methods below.
|
||||||
|
"""
|
||||||
(o, home) = self.new()
|
(o, home) = self.new()
|
||||||
assert o._isdone('_bootstrap') is False
|
assert o._isdone('_bootstrap') is False
|
||||||
o._bootstrap(**overrides)
|
o._bootstrap(**overrides)
|
||||||
@ -290,6 +347,9 @@ class test_Env(ClassChecker):
|
|||||||
assert o[key] == value
|
assert o[key] == value
|
||||||
|
|
||||||
def finalize_core(self, **defaults):
|
def finalize_core(self, **defaults):
|
||||||
|
"""
|
||||||
|
Helper method used in testing `Env._finalize_core`.
|
||||||
|
"""
|
||||||
(o, home) = self.new()
|
(o, home) = self.new()
|
||||||
assert o._isdone('_finalize_core') is False
|
assert o._isdone('_finalize_core') is False
|
||||||
o._finalize_core(**defaults)
|
o._finalize_core(**defaults)
|
||||||
@ -462,41 +522,3 @@ class test_Env(ClassChecker):
|
|||||||
assert o.yes is True
|
assert o.yes is True
|
||||||
assert o.no is False
|
assert o.no is False
|
||||||
assert o.number == 42
|
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)
|
|
||||||
|
Loading…
Reference in New Issue
Block a user