mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-01-23 14:53:12 -06:00
Added ipalib/constants.py; added Env._load_config() method along with comprehensive unit tests for same
This commit is contained in:
parent
2ec0312eb6
commit
f80beb948b
@ -25,12 +25,13 @@ It will also take care of settings that can be discovered by different
|
||||
methods, such as DNS.
|
||||
"""
|
||||
|
||||
from ConfigParser import SafeConfigParser, ParsingError
|
||||
from ConfigParser import SafeConfigParser, ParsingError, RawConfigParser
|
||||
import types
|
||||
import os
|
||||
from os import path
|
||||
import sys
|
||||
|
||||
from errors import check_isinstance, raise_TypeError
|
||||
import constants
|
||||
|
||||
DEFAULT_CONF='/etc/ipa/ipa.conf'
|
||||
|
||||
@ -136,6 +137,71 @@ class Env(object):
|
||||
|
||||
def __init__(self):
|
||||
object.__setattr__(self, '_Env__d', {})
|
||||
self.ipalib = path.dirname(path.abspath(__file__))
|
||||
self.site_packages = path.dirname(self.ipalib)
|
||||
self.script = path.abspath(sys.argv[0])
|
||||
self.bin = path.dirname(self.script)
|
||||
self.home = path.abspath(os.environ['HOME'])
|
||||
self.dot_ipa = path.join(self.home, '.ipa')
|
||||
|
||||
def _bootstrap(self, **overrides):
|
||||
"""
|
||||
Initialize basic environment.
|
||||
|
||||
This method will initialize only enough environment information to
|
||||
determine whether ipa is running in-tree, what the context is,
|
||||
and the location of the configuration file.
|
||||
|
||||
This method should be called before any plugins are loaded.
|
||||
"""
|
||||
for (key, value) in overrides.items():
|
||||
self[key] = value
|
||||
if 'in_tree' not in self:
|
||||
if self.bin == self.site_packages and \
|
||||
path.isfile(path.join(self.bin, 'setup.py')):
|
||||
self.in_tree = True
|
||||
else:
|
||||
self.in_tree = False
|
||||
if 'context' not in self:
|
||||
self.context = 'default'
|
||||
if 'conf' not in self:
|
||||
name = '%s.conf' % self.context
|
||||
if self.in_tree:
|
||||
self.conf = path.join(self.dot_ipa, name)
|
||||
else:
|
||||
self.conf = path.join('/', 'etc', 'ipa', name)
|
||||
|
||||
def _load_config(self, conf_file):
|
||||
"""
|
||||
Merge in values from ``conf_file`` into this `Env`.
|
||||
"""
|
||||
section = constants.CONFIG_SECTION
|
||||
if not path.isfile(conf_file):
|
||||
return
|
||||
parser = RawConfigParser()
|
||||
try:
|
||||
parser.read(conf_file)
|
||||
except ParsingError:
|
||||
return
|
||||
if not parser.has_section(section):
|
||||
parser.add_section(section)
|
||||
items = parser.items(section)
|
||||
if len(items) == 0:
|
||||
return
|
||||
i = 0
|
||||
for (key, value) in items:
|
||||
if key not in self:
|
||||
self[key] = value
|
||||
i += 1
|
||||
return (i, len(items))
|
||||
|
||||
def _finalize(self, **defaults):
|
||||
"""
|
||||
Finalize and lock environment.
|
||||
|
||||
This method should be called after all plugins have bean loaded and
|
||||
after `plugable.API.finalize()` has been called.
|
||||
"""
|
||||
|
||||
def __lock__(self):
|
||||
"""
|
||||
@ -186,6 +252,7 @@ class Env(object):
|
||||
"""
|
||||
Set ``key`` to ``value``.
|
||||
"""
|
||||
# FIXME: the key should be checked with check_name()
|
||||
if self.__locked:
|
||||
raise AttributeError('locked: cannot set %s.%s to %r' %
|
||||
(self.__class__.__name__, key, value)
|
||||
@ -194,10 +261,18 @@ class Env(object):
|
||||
raise AttributeError('cannot overwrite %s.%s with %r' %
|
||||
(self.__class__.__name__, key, value)
|
||||
)
|
||||
self.__d[key] = value
|
||||
if not callable(value):
|
||||
if isinstance(value, basestring):
|
||||
value = str(value.strip())
|
||||
if value.lower() == 'true':
|
||||
value = True
|
||||
elif value.lower() == 'false':
|
||||
value = False
|
||||
elif value.isdigit():
|
||||
value = int(value)
|
||||
assert type(value) in (str, int, bool)
|
||||
object.__setattr__(self, key, value)
|
||||
self.__d[key] = value
|
||||
|
||||
def __contains__(self, key):
|
||||
"""
|
||||
|
25
ipalib/constants.py
Normal file
25
ipalib/constants.py
Normal file
@ -0,0 +1,25 @@
|
||||
# Authors:
|
||||
# Jason Gerard DeRose <jderose@redhat.com>
|
||||
#
|
||||
# Copyright (C) 2008 Red Hat
|
||||
# see file 'COPYING' for use and warranty information
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License as
|
||||
# published by the Free Software Foundation; version 2 only
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
"""
|
||||
Constants centralized in one file.
|
||||
"""
|
||||
|
||||
# The section read in config files, i.e. [global]
|
||||
CONFIG_SECTION = 'global'
|
@ -3,6 +3,7 @@
|
||||
# Script to run nosetests under multiple versions of Python
|
||||
|
||||
versions="python2.4 python2.5 python2.6"
|
||||
versions="python2.5 python2.6"
|
||||
|
||||
for name in $versions
|
||||
do
|
||||
|
@ -22,9 +22,12 @@ Test the `ipalib.config` module.
|
||||
"""
|
||||
|
||||
import types
|
||||
|
||||
import os
|
||||
from os import path
|
||||
import sys
|
||||
from tests.util import raises, setitem, delitem, ClassChecker
|
||||
from tests.util import getitem, setitem, delitem
|
||||
from tests.util import TempDir
|
||||
from ipalib import config
|
||||
|
||||
|
||||
@ -112,6 +115,65 @@ def test_Environment():
|
||||
assert env.a != 1000
|
||||
|
||||
|
||||
# Random base64-encoded data to simulate a misbehaving config file.
|
||||
config_bad = """
|
||||
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
|
||||
"""
|
||||
|
||||
# A config file that tries to override some standard vars:
|
||||
config_override = """
|
||||
[global]
|
||||
key0 = var0
|
||||
home = /home/sweet/home
|
||||
key1 = var1
|
||||
site_packages = planet
|
||||
key2 = var2
|
||||
key3 = var3
|
||||
"""
|
||||
|
||||
# A config file that test the automatic type conversion
|
||||
config_good = """
|
||||
[global]
|
||||
yes = TRUE
|
||||
no = False
|
||||
number = 42
|
||||
"""
|
||||
|
||||
|
||||
class test_Env(ClassChecker):
|
||||
"""
|
||||
Test the `ipalib.config.Env` class.
|
||||
@ -124,6 +186,113 @@ class test_Env(ClassChecker):
|
||||
Test the `ipalib.config.Env.__init__` method.
|
||||
"""
|
||||
o = self.cls()
|
||||
ipalib = path.dirname(path.abspath(config.__file__))
|
||||
assert o.ipalib == ipalib
|
||||
assert o.site_packages == path.dirname(ipalib)
|
||||
assert o.script == path.abspath(sys.argv[0])
|
||||
assert o.bin == path.dirname(path.abspath(sys.argv[0]))
|
||||
assert o.home == os.environ['HOME']
|
||||
assert o.dot_ipa == path.join(os.environ['HOME'], '.ipa')
|
||||
|
||||
def bootstrap(self, **overrides):
|
||||
o = self.cls()
|
||||
o._bootstrap(**overrides)
|
||||
return o
|
||||
|
||||
def test_bootstrap(self):
|
||||
"""
|
||||
Test the `ipalib.config.Env._bootstrap` method.
|
||||
"""
|
||||
dot_ipa = path.join(os.environ['HOME'], '.ipa')
|
||||
|
||||
# Test defaults created by _bootstrap():
|
||||
o = self.cls()
|
||||
assert 'in_tree' not in o
|
||||
assert 'context' not in o
|
||||
assert 'conf' not in o
|
||||
o._bootstrap()
|
||||
assert o.in_tree is False
|
||||
assert o.context == 'default'
|
||||
assert o.conf == '/etc/ipa/default.conf'
|
||||
|
||||
# Test overriding values created by _bootstrap()
|
||||
o = self.bootstrap(in_tree='true', context='server')
|
||||
assert o.in_tree is True
|
||||
assert o.context == 'server'
|
||||
assert o.conf == path.join(dot_ipa, 'server.conf')
|
||||
o = self.bootstrap(conf='/my/wacky/whatever.conf')
|
||||
assert o.in_tree is False
|
||||
assert o.context == 'default'
|
||||
assert o.conf == '/my/wacky/whatever.conf'
|
||||
|
||||
# Test various overrides and types conversion
|
||||
kw = dict(
|
||||
yes=True,
|
||||
no=False,
|
||||
num=42,
|
||||
msg='Hello, world!',
|
||||
)
|
||||
override = dict(
|
||||
(k, u' %s ' % v) for (k, v) in kw.items()
|
||||
)
|
||||
o = self.cls()
|
||||
for key in kw:
|
||||
assert key not in o
|
||||
o._bootstrap(**override)
|
||||
for (key, value) in kw.items():
|
||||
assert getattr(o, key) == value
|
||||
assert o[key] == value
|
||||
|
||||
def test_load_config(self):
|
||||
"""
|
||||
Test the `ipalib.config.Env._load_config` method.
|
||||
"""
|
||||
tmp = TempDir()
|
||||
assert callable(tmp.join)
|
||||
|
||||
# Test a config file that doesn't exist
|
||||
no_exist = tmp.join('no_exist.conf')
|
||||
assert not path.exists(no_exist)
|
||||
o = self.cls()
|
||||
keys = tuple(o)
|
||||
orig = dict((k, o[k]) for k in o)
|
||||
assert o._load_config(no_exist) is None
|
||||
assert tuple(o) == keys
|
||||
|
||||
# Test an empty config file
|
||||
empty = tmp.touch('empty.conf')
|
||||
assert path.isfile(empty)
|
||||
assert o._load_config(empty) is None
|
||||
assert tuple(o) == keys
|
||||
|
||||
# Test a mal-formed config file:
|
||||
bad = tmp.join('bad.conf')
|
||||
open(bad, 'w').write(config_bad)
|
||||
assert path.isfile(bad)
|
||||
assert o._load_config(bad) is None
|
||||
assert tuple(o) == keys
|
||||
|
||||
# Test a valid config file that tries to override
|
||||
override = tmp.join('override.conf')
|
||||
open(override, 'w').write(config_override)
|
||||
assert path.isfile(override)
|
||||
assert o._load_config(override) == (4, 6)
|
||||
for (k, v) in orig.items():
|
||||
assert o[k] is v
|
||||
assert list(o) == sorted(keys + ('key0', 'key1', 'key2', 'key3'))
|
||||
for i in xrange(4):
|
||||
assert o['key%d' % i] == ('var%d' % i)
|
||||
keys = tuple(o)
|
||||
|
||||
# Test a valid config file with type conversion
|
||||
good = tmp.join('good.conf')
|
||||
open(good, 'w').write(config_good)
|
||||
assert path.isfile(good)
|
||||
assert o._load_config(good) == (3, 3)
|
||||
assert list(o) == sorted(keys + ('yes', 'no', 'number'))
|
||||
assert o.yes is True
|
||||
assert o.no is False
|
||||
assert o.number == 42
|
||||
|
||||
def test_lock(self):
|
||||
"""
|
||||
@ -186,6 +355,16 @@ class test_Env(ClassChecker):
|
||||
e = raises(AttributeError, setvar, o, name, value)
|
||||
assert str(e) == \
|
||||
'locked: cannot set Env.%s to %r' % (name, value)
|
||||
o = self.cls()
|
||||
setvar(o, 'yes', ' true ')
|
||||
assert o.yes is True
|
||||
setvar(o, 'no', ' false ')
|
||||
assert o.no is False
|
||||
setvar(o, 'msg', u' Hello, world! ')
|
||||
assert o.msg == 'Hello, world!'
|
||||
assert type(o.msg) is str
|
||||
setvar(o, 'num', ' 42 ')
|
||||
assert o.num == 42
|
||||
|
||||
def test_delattr(self):
|
||||
"""
|
||||
@ -223,11 +402,11 @@ class test_Env(ClassChecker):
|
||||
Test the `ipalib.config.Env.__iter__` method.
|
||||
"""
|
||||
o = self.cls()
|
||||
assert list(o) == []
|
||||
default_keys = tuple(o)
|
||||
keys = ('one', 'two', 'three', 'four', 'five')
|
||||
for key in keys:
|
||||
o[key] = 'the value'
|
||||
assert list(o) == sorted(keys)
|
||||
assert list(o) == sorted(keys + default_keys)
|
||||
|
||||
|
||||
def test_set_default_env():
|
||||
|
@ -22,8 +22,51 @@ Common utility functions and classes for unit tests.
|
||||
"""
|
||||
|
||||
import inspect
|
||||
import os
|
||||
from os import path
|
||||
import tempfile
|
||||
import shutil
|
||||
from ipalib import errors
|
||||
|
||||
|
||||
class TempDir(object):
|
||||
def __init__(self):
|
||||
self.__path = tempfile.mkdtemp(prefix='ipa.tests.')
|
||||
assert self.path == self.__path
|
||||
|
||||
def __get_path(self):
|
||||
assert path.abspath(self.__path) == self.__path
|
||||
assert self.__path.startswith('/tmp/ipa.tests.')
|
||||
assert path.isdir(self.__path) and not path.islink(self.__path)
|
||||
return self.__path
|
||||
path = property(__get_path)
|
||||
|
||||
def rmtree(self):
|
||||
shutil.rmtree(self.path)
|
||||
self.__path = None
|
||||
|
||||
def makedirs(self, *parts):
|
||||
d = self.join(*parts)
|
||||
if not path.exists(d):
|
||||
os.makedirs(d)
|
||||
assert path.isdir(d) and not path.islink(d)
|
||||
return d
|
||||
|
||||
def touch(self, *parts):
|
||||
d = self.makedirs(*parts[:-1])
|
||||
f = path.join(d, parts[-1])
|
||||
assert not path.exists(f)
|
||||
open(f, 'w').close()
|
||||
assert path.isfile(f) and not path.islink(f)
|
||||
return f
|
||||
|
||||
def join(self, *parts):
|
||||
return path.join(self.path, *parts)
|
||||
|
||||
def __del__(self):
|
||||
self.rmtree()
|
||||
|
||||
|
||||
class ExceptionNotRaised(Exception):
|
||||
"""
|
||||
Exception raised when an *expected* exception is *not* raised during a
|
||||
|
Loading…
Reference in New Issue
Block a user