Some more reorganization in Env and added class docstring to Env with lots of examples

This commit is contained in:
Jason Gerard DeRose 2008-12-22 21:02:43 -07:00
parent 6b055b435f
commit 01cae56e0a
3 changed files with 137 additions and 51 deletions

View File

@ -31,13 +31,95 @@ import os
from os import path from os import path
import sys import sys
from constants import CONFIG_SECTION from constants import CONFIG_SECTION
from constants import TYPE_ERROR, OVERRIDE_ERROR, LOCK_ERROR from constants import TYPE_ERROR, OVERRIDE_ERROR, SET_ERROR, DEL_ERROR
class Env(object): class Env(object):
""" """
A mapping object used to store the environment variables. 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.
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
'I was set as an attribute.'
>>> env['attr'] # Also retrieve as a dictionary item
'I was set as an attribute.'
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
'I was set as a dictionary item.'
>>> env.item # Also retrieve as an attribute
'I was set as a dictionary item.'
The variable values can be ``str`` or ``int`` instances, or the ``True``,
``False``, or ``None`` constants. When the value provided is an ``str``
instance, some limited automatic type conversion is performed, which allows
values of specific types to be set easily from configuration files and from
command-line options.
The ``True``, ``False``, and ``None`` constants can be specified with a
string that matches what ``repr()`` would return. For example:
>>> env.true = 'True'
>>> env.true
True
Note that the automatic type conversion is case sensitive. For example:
>>> env.false = 'false' # Doesn't match repr(False)
>>> env.false
'false'
If an ``str`` value looks like an integer, it's automatically converted to
the ``int`` type. For example:
>>> env.lucky = '7'
>>> env.lucky
7
Also, 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
>>> env.actually_false = ' False' # Still matches repr(False)
>>> env.actually_false
False
`Env` is set-once, first-one-wins. Once a variable has been set, trying to
override it will raise an ``AttributeError``. For example:
>>> env.my_var = 'first'
>>> env.my_var = 'second'
Traceback (most recent call last):
...
AttributeError: cannot override Env.my_var 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 ``AttributeError``. For example:
>>> env = Env()
>>> env.var1 = 'This will work.'
>>> env.__lock__()
>>> env.var2 = 'This wont work!'
Traceback (most recent call last):
...
AttributeError: locked: cannot set Env.var2 to 'This wont work!'
Finish me!
""" """
__locked = False __locked = False
@ -67,12 +149,16 @@ class Env(object):
# FIXME: the key should be checked with check_name() # FIXME: the key should be checked with check_name()
if self.__locked: if self.__locked:
raise AttributeError( raise AttributeError(
LOCK_ERROR % (self.__class__.__name__, key, value) SET_ERROR % (self.__class__.__name__, key, value)
) )
if key in self.__d: if key in self.__d:
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):
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 = {
@ -88,6 +174,20 @@ class Env(object):
object.__setattr__(self, key, value) object.__setattr__(self, key, value)
self.__d[key] = value self.__d[key] = value
def __getitem__(self, key):
"""
Return the value corresponding to ``key``.
"""
return self.__d[key]
def __delattr__(self, name):
"""
Raise AttributeError (deletion is never allowed).
"""
raise AttributeError(
DEL_ERROR % (self.__class__.__name__, name)
)
def __doing(self, name): def __doing(self, name):
if name in self.__done: if name in self.__done:
raise StandardError( raise StandardError(
@ -214,19 +314,7 @@ class Env(object):
def __delattr__(self, name):
"""
Raise AttributeError (deletion is not allowed).
"""
raise AttributeError('cannot del %s.%s' %
(self.__class__.__name__, name)
)
def __getitem__(self, key):
"""
Return the value corresponding to ``key``.
"""
return self.__d[key]

View File

@ -36,8 +36,8 @@ OVERRIDE_ERROR = 'cannot override %s.%s value %r with %r'
# Standard format for AttributeError message when a read-only attribute is # Standard format for AttributeError message when a read-only attribute is
# already locked: # already locked:
LOCK_ERROR = 'locked: cannot set %s.%s to %r' SET_ERROR = 'locked: cannot set %s.%s to %r'
DEL_ERROR = 'locked: cannot set %s.%s to %r' DEL_ERROR = 'locked: cannot del %s.%s'
# Used for a tab (or indentation level) when formatting for CLI: # Used for a tab (or indentation level) when formatting for CLI:
CLI_TAB = ' ' # Two spaces CLI_TAB = ' ' # Two spaces

View File

@ -28,7 +28,7 @@ import sys
from tests.util import raises, setitem, delitem, ClassChecker from tests.util import raises, setitem, delitem, ClassChecker
from tests.util import getitem, setitem, delitem from tests.util import getitem, setitem, delitem
from tests.util import TempDir, TempHome from tests.util import TempDir, TempHome
from ipalib.constants import TYPE_ERROR, OVERRIDE_ERROR, LOCK_ERROR from ipalib.constants import TYPE_ERROR, OVERRIDE_ERROR, SET_ERROR, DEL_ERROR
from ipalib import config, constants from ipalib import config, constants
@ -177,7 +177,7 @@ class test_Env(ClassChecker):
o.__lock__() o.__lock__()
for (name, raw, value) in good_vars: for (name, raw, value) in good_vars:
e = raises(AttributeError, setattr, o, name, raw) e = raises(AttributeError, setattr, o, name, raw)
assert str(e) == LOCK_ERROR % ('Env', name, raw) assert str(e) == SET_ERROR % ('Env', name, raw)
def test_setitem(self): def test_setitem(self):
""" """
@ -201,7 +201,35 @@ class test_Env(ClassChecker):
o.__lock__() o.__lock__()
for (key, raw, value) in good_vars: for (key, raw, value) in good_vars:
e = raises(AttributeError, o.__setitem__, key, raw) e = raises(AttributeError, o.__setitem__, key, raw)
assert str(e) == LOCK_ERROR % ('Env', key, raw) assert str(e) == SET_ERROR % ('Env', key, raw)
def test_getitem(self):
"""
Test the `ipalib.config.Env.__getitem__` method.
"""
o = self.cls()
value = 'some value'
o.key = value
assert o.key is value
assert o['key'] is value
for name in ('one', 'two'):
e = raises(KeyError, getitem, o, name)
assert str(e) == repr(name)
def test_delattr(self):
"""
Test the `ipalib.config.Env.__delattr__` method.
This also tests that ``__delitem__`` is not implemented.
"""
o = self.cls()
o.one = 1
assert o.one == 1
for key in ('one', 'two'):
e = raises(AttributeError, delattr, o, key)
assert str(e) == DEL_ERROR % ('Env', key)
e = raises(AttributeError, delitem, o, key)
assert str(e) == '__delitem__'
def bootstrap(self, **overrides): def bootstrap(self, **overrides):
(o, home) = self.new() (o, home) = self.new()
@ -445,36 +473,6 @@ class test_Env(ClassChecker):
e = raises(StandardError, o.__lock__) e = raises(StandardError, o.__lock__)
assert str(e) == 'Env.__lock__() already called' assert str(e) == 'Env.__lock__() already called'
def test_getitem(self):
"""
Test the `ipalib.config.Env.__getitem__` method.
"""
o = self.cls()
value = 'some value'
o.key = value
assert o.key is value
assert o['key'] is value
for name in ('one', 'two'):
e = raises(KeyError, getitem, o, name)
assert str(e) == repr(name)
def test_delattr(self):
"""
Test the `ipalib.config.Env.__delattr__` method.
This also tests that ``__delitem__`` is not implemented.
"""
o = self.cls()
o.one = 1
assert o.one == 1
for key in ('one', 'two'):
e = raises(AttributeError, delattr, o, key)
assert str(e) == 'cannot del Env.%s' % key
e = raises(AttributeError, delitem, o, key)
assert str(e) == '__delitem__'
def test_contains(self): def test_contains(self):
""" """
Test the `ipalib.config.Env.__contains__` method. Test the `ipalib.config.Env.__contains__` method.