configglue team mailing list archive
-
configglue team
-
Mailing list archive
-
Message #00077
[Merge] lp:~ricardokirkner/configglue/environ-vars into lp:configglue
Ricardo Kirkner has proposed merging lp:~ricardokirkner/configglue/environ-vars into lp:configglue with lp:~ricardokirkner/configglue/refactor-options as a prerequisite.
Requested reviews:
Configglue developers (configglue)
For more details, see:
https://code.launchpad.net/~ricardokirkner/configglue/environ-vars/+merge/62584
This branch adds support for environment variables.
Environment variables are supported as follows:
1. In the commandline glue
Variable interpolation is done according to the precedence:
a. commandline parameter (aka --foo-bar=4)
b. environment variable (aka CONFIGGLUE_FOO_BAR=4)
c. configuration files
d. defaults
2. In the configuration files
Variable interpolation is supported for environment variables using the standard notation
$VAR
${VAR}
If the variable is undefined, it resolves to the default value.
--
https://code.launchpad.net/~ricardokirkner/configglue/environ-vars/+merge/62584
Your team Configglue developers is requested to review the proposed merge of lp:~ricardokirkner/configglue/environ-vars into lp:configglue.
=== modified file 'configglue/pyschema/glue.py'
--- configglue/pyschema/glue.py 2011-03-26 22:24:51 +0000
+++ configglue/pyschema/glue.py 2011-05-27 00:04:32 +0000
@@ -15,6 +15,7 @@
#
###############################################################################
+import os
import sys
from optparse import OptionParser
from collections import namedtuple
@@ -67,16 +68,29 @@
og.add_option('--' + long_name(option), **kwargs)
options, args = op.parse_args(argv)
+ def set_value(section, option, value):
+ # the value has been overridden by an argument;
+ # update it, but make sure it's a string, as
+ # SafeConfigParser will complain otherwise.
+ if not isinstance(value, basestring):
+ value = repr(value)
+ parser.set(section.name, option.name, value)
+
for section in schema.sections():
for option in section.options():
- value = getattr(options, opt_name(option))
- if parser.get(section.name, option.name) != value:
- # the value has been overridden by an argument;
- # update it, but make sure it's a string, as
- # SafeConfigParser will complain otherwise.
- if not isinstance(value, basestring):
- value = repr(value)
- parser.set(section.name, option.name, value)
+ # 1. op value != parser value
+ # 2. op value == parser value != env value
+ # 3. op value == parser value == env value or not env value
+
+ op_value = getattr(options, opt_name(option))
+ parser_value = parser.get(section.name, option.name)
+ env_value = os.environ.get("CONFIGGLUE_{0}".format(
+ long_name(option).upper()))
+
+ if op_value != parser_value:
+ set_value(section, option, op_value)
+ elif env_value is not None and env_value != parser_value:
+ set_value(section, option, env_value)
return op, options, args
=== modified file 'configglue/pyschema/parser.py'
--- configglue/pyschema/parser.py 2011-05-27 00:04:32 +0000
+++ configglue/pyschema/parser.py 2011-05-27 00:04:32 +0000
@@ -19,6 +19,7 @@
import collections
import copy
import os
+import re
import string
from ConfigParser import (
@@ -435,6 +436,14 @@
assert isinstance(result, basestring)
return result
+ def interpolate_environment(self, rawval, raw=False):
+ if not raw:
+ # interpolate environment variables
+ rawval = re.sub(r'\${([A-Z_]+)}', r'%(\1)s', rawval)
+ rawval = re.sub(r'\$([A-Z_]+)', r'%(\1)s', rawval)
+ rawval = rawval % os.environ
+ return rawval
+
def _get_default(self, section, option):
# mark the value as not initialized to be able to have a None default
marker = object()
@@ -511,6 +520,13 @@
self._sections[section] = {}
self.set(section, option, value)
+ # interpolate environment variables
+ try:
+ value = self.interpolate_environment(value, raw=raw)
+ except KeyError:
+ # interpolation failed, fallback to default value
+ value = self._get_default(section, option)
+
if parse:
value = self.parse(section, option, value)
return value
=== modified file 'configglue/tests/pyschema/test_parser.py'
--- configglue/tests/pyschema/test_parser.py 2011-05-27 00:04:32 +0000
+++ configglue/tests/pyschema/test_parser.py 2011-05-27 00:04:32 +0000
@@ -31,6 +31,7 @@
from mock import (
Mock,
+ patch,
patch_object,
)
@@ -251,6 +252,53 @@
self.assertRaises(InterpolationMissingOptionError, parser.get,
'foo', 'bar')
+ @patch('configglue.pyschema.parser.os')
+ def test_interpolate_environment_basic_syntax(self, mock_os):
+ mock_os.environ = {'PATH': 'foo'}
+ parser = SchemaConfigParser(Schema())
+ result = parser.interpolate_environment("$PATH")
+ self.assertEqual(result, 'foo')
+
+ @patch('configglue.pyschema.parser.os')
+ def test_interpolate_environment_extended_syntax(self, mock_os):
+ mock_os.environ = {'PATH': 'foo'}
+ parser = SchemaConfigParser(Schema())
+ result = parser.interpolate_environment("${PATH}")
+ self.assertEqual(result, 'foo')
+
+ @patch('configglue.pyschema.parser.os')
+ def test_interpolate_environment_in_config(self, mock_os):
+ mock_os.environ = {'PYTHONPATH': 'foo', 'PATH': 'bar'}
+ class MySchema(Schema):
+ pythonpath = StringOption()
+ path = StringOption()
+
+ config = StringIO("[__main__]\npythonpath=${PYTHONPATH}\npath=$PATH")
+ parser = SchemaConfigParser(MySchema())
+ parser.readfp(config)
+ self.assertEqual(parser.values('__main__'),
+ {'pythonpath': 'foo', 'path': 'bar'})
+
+ @patch('configglue.pyschema.parser.os')
+ def test_get_with_environment_var(self, mock_os):
+ mock_os.environ = {'FOO': '42'}
+ class MySchema(Schema):
+ foo = IntOption()
+
+ config = StringIO("[__main__]\nfoo=$FOO")
+ parser = SchemaConfigParser(MySchema())
+ parser.readfp(config)
+ self.assertEqual(parser.get('__main__', 'foo'), 42)
+
+ def test_get_without_environment_var(self):
+ class MySchema(Schema):
+ foo = IntOption()
+
+ config = StringIO("[__main__]\nfoo=$FOO")
+ parser = SchemaConfigParser(MySchema())
+ parser.readfp(config)
+ self.assertEqual(parser.get('__main__', 'foo'), 0)
+
def test_get_interpolation_keys_string(self):
class MySchema(Schema):
foo = StringOption()
@@ -366,7 +414,6 @@
value = parser._interpolate_value('__main__', 'foo')
self.assertEqual(value, None)
-
def test_get_with_raw_value(self):
class MySchema(Schema):
foo = StringOption(raw=True)
=== modified file 'configglue/tests/pyschema/test_schemaconfig.py'
--- configglue/tests/pyschema/test_schemaconfig.py 2011-05-27 00:04:32 +0000
+++ configglue/tests/pyschema/test_schemaconfig.py 2011-05-27 00:04:32 +0000
@@ -17,10 +17,15 @@
###############################################################################
import unittest
+import os
import sys
from StringIO import StringIO
-from mock import patch, Mock
+from mock import (
+ Mock,
+ patch,
+ patch_object,
+)
from configglue.pyschema.glue import (
configglue,
@@ -131,14 +136,13 @@
self.assertEqual(self.parser.values(),
{'foo': {'bar': 0}, '__main__': {'baz': 1}})
- _argv = sys.argv
- sys.argv = []
-
- op, options, args = schemaconfigglue(self.parser)
- self.assertEqual(self.parser.values(),
- {'foo': {'bar': 0}, '__main__': {'baz': 1}})
-
- sys.argv = _argv
+ _argv, sys.argv = sys.argv, []
+ try:
+ op, options, args = schemaconfigglue(self.parser)
+ self.assertEqual(self.parser.values(),
+ {'foo': {'bar': 0}, '__main__': {'baz': 1}})
+ finally:
+ sys.argv = _argv
def test_glue_section_option(self):
config = StringIO("[foo]\nbar=1")
@@ -151,6 +155,35 @@
self.assertEqual(self.parser.values(),
{'foo': {'bar': 2}, '__main__': {'baz': 0}})
+ @patch('configglue.pyschema.glue.os')
+ def test_glue_environ(self, mock_os):
+ mock_os.environ = {'CONFIGGLUE_FOO_BAR': '42', 'CONFIGGLUE_BAZ': 3}
+ config = StringIO("[foo]\nbar=1")
+ self.parser.readfp(config)
+
+ _argv, sys.argv = sys.argv, ['prognam']
+ try:
+ op, options, args = schemaconfigglue(self.parser)
+ self.assertEqual(self.parser.values(),
+ {'foo': {'bar': 42}, '__main__': {'baz': 3}})
+ finally:
+ sys.argv = _argv
+
+
+ def test_glue_environ_precedence(self):
+ with patch_object(os, 'environ',
+ {'CONFIGGLUE_FOO_BAR': '42', 'BAR': '1'}):
+
+ config = StringIO("[foo]\nbar=$BAR")
+ self.parser.readfp(config)
+
+ _argv, sys.argv = sys.argv, ['prognam']
+ try:
+ op, options, args = schemaconfigglue(self.parser)
+ self.assertEqual(self.parser.get('foo', 'bar'), 42)
+ finally:
+ sys.argv = _argv
+
def test_ambiguous_option(self):
class MySchema(Schema):
class foo(Section):