← Back to team overview

configglue team mailing list archive

[Merge] lp:~ricardokirkner/configglue/app-validate into lp:configglue

 

Ricardo Kirkner has proposed merging lp:~ricardokirkner/configglue/app-validate into lp:configglue.

Requested reviews:
  Ricardo Kirkner (ricardokirkner)
  John Lenton (chipaca)

For more details, see:
https://code.launchpad.net/~ricardokirkner/configglue/app-validate/+merge/68162

Allow apps inheriting from App to validate the configuration by providing a
default --validate option.

For example, calling

python myapp.py --validate

will invoke config validation, while not including it will allow configuration
errors to go unnoticed.

This allows flexibility by not enforcing strictnes on the configuration, but
leaving the option to validate the config the end user.
-- 
https://code.launchpad.net/~ricardokirkner/configglue/app-validate/+merge/68162
Your team Configglue developers is subscribed to branch lp:configglue.
=== modified file 'configglue/app/base.py'
--- configglue/app/base.py	2011-07-17 22:32:16 +0000
+++ configglue/app/base.py	2011-07-22 15:47:21 +0000
@@ -15,6 +15,7 @@
 ###############################################################################
 import os.path
 import sys
+from optparse import OptionParser
 
 from xdg.BaseDirectory import load_config_paths
 
@@ -36,9 +37,13 @@
         schemas = [app.schema] + app.plugins.schemas
         self.schema = merge(*schemas)
 
+        # create OptionParser with default options
+        parser = OptionParser()
+        parser.add_option('--validate', dest='validate', action='store_true',
+            help="validate configuration")
         # initialize config
         config_files = self.get_config_files(app)
-        self.glue = configglue(self.schema, config_files)
+        self.glue = configglue(self.schema, config_files, op=parser)
 
     def get_config_files(self, app):
         config_files = []

=== modified file 'configglue/glue.py'
--- configglue/glue.py	2011-07-19 03:15:22 +0000
+++ configglue/glue.py	2011-07-22 15:47:21 +0000
@@ -116,7 +116,7 @@
     return op, options, args
 
 
-def configglue(schema_class, configs, usage=None):
+def configglue(schema_class, configs, op=None, validate=False):
     """Parse configuration files using a provided schema.
 
     The standard workflow for configglue is to instantiate a schema class,
@@ -127,12 +127,9 @@
     """
     scp = SchemaConfigParser(schema_class())
     scp.read(configs)
-    if usage is not None:
-        op = OptionParser(usage=usage)
-    else:
-        op = None
     parser, opts, args = schemaconfigglue(scp, op=op)
-    is_valid, reasons = scp.is_valid(report=True)
-    if not is_valid:
-        parser.error('\n'.join(reasons))
+    if validate or getattr(opts, 'validate', False):
+        is_valid, reasons = scp.is_valid(report=True)
+        if not is_valid:
+            parser.error('\n'.join(reasons))
     return SchemaGlue(scp, parser, opts, args)

=== renamed directory 'configglue/app/tests' => 'configglue/tests/app'
=== modified file 'configglue/tests/app/test_base.py'
--- configglue/app/tests/test_base.py	2011-07-17 22:32:16 +0000
+++ configglue/tests/app/test_base.py	2011-07-22 15:47:21 +0000
@@ -36,12 +36,14 @@
 )
 
 
-def make_app(name=None, schema=None, plugin_manager=None):
+def make_app(name=None, schema=None, plugin_manager=None, validate=False):
     # patch sys.argv so that nose can be run with extra options
     # without conflicting with the schema validation
     # patch sys.stderr to prevent spurious output
     mock_sys = Mock()
     mock_sys.argv = ['foo.py']
+    if validate:
+        mock_sys.argv.append('--validate')
     with patch('configglue.glue.sys', mock_sys):
         with patch('configglue.app.base.sys.stderr'):
             app = App(name=name, schema=schema, plugin_manager=plugin_manager)
@@ -68,27 +70,37 @@
             os.environ.get('XDG_CONFIG_DIRS', '/etc/xdg').split(':'))
         return xdg_config_dirs
 
+    @patch('configglue.app.base.OptionParser')
     @patch('configglue.app.base.merge')
     @patch('configglue.app.base.Config.get_config_files')
     @patch('configglue.app.base.configglue')
     def test_constructor(self, mock_configglue,
-        mock_get_config_files, mock_merge):
+        mock_get_config_files, mock_merge, mock_parser):
 
         config = Config(App())
 
         self.assertEqual(config.schema, mock_merge.return_value)
         self.assertEqual(config.glue, mock_configglue.return_value)
         mock_configglue.assert_called_with(
-            mock_merge.return_value, mock_get_config_files.return_value)
+            mock_merge.return_value, mock_get_config_files.return_value,
+            op=mock_parser.return_value)
 
     def test_glue_valid_config(self):
         config = make_config()
         self.assertEqual(config.glue.schema_parser.is_valid(), True)
 
-    def test_glue_invalid_config(self):
-        class MySchema(Schema):
-            foo = IntOption(fatal=True)
-        self.assertRaises(SystemExit, make_app, schema=MySchema)
+    def test_glue_validate_invalid_config(self):
+        class MySchema(Schema):
+            foo = IntOption(fatal=True)
+
+        self.assertRaises(SystemExit, make_app, schema=MySchema, validate=True)
+
+    def test_glue_no_validate_invalid_config(self):
+        class MySchema(Schema):
+            foo = IntOption(fatal=True)
+        # no explicit assertion as we just want to verify creating the
+        # app doesn't raise any exception if validation is turned off
+        make_app(schema=MySchema)
 
     def test_get_config_files(self):
         app = make_app()

=== modified file 'configglue/tests/test_schemaconfig.py'
--- configglue/tests/test_schemaconfig.py	2011-07-19 03:15:22 +0000
+++ configglue/tests/test_schemaconfig.py	2011-07-22 15:47:21 +0000
@@ -19,7 +19,10 @@
 import os
 import sys
 from StringIO import StringIO
-from optparse import OptionConflictError
+from optparse import (
+    OptionConflictError,
+    OptionParser,
+)
 
 from mock import (
     Mock,
@@ -333,6 +336,13 @@
 
 
 class ConfigglueTestCase(unittest.TestCase):
+    def setUp(self):
+        self.mock_argv = patch_object(sys, 'argv', ['foo'])
+        self.mock_argv.start()
+
+    def tearDown(self):
+        self.mock_argv.stop()
+
     @patch('configglue.glue.SchemaConfigParser')
     @patch('configglue.glue.schemaconfigglue')
     def test_configglue_no_errors(self, mock_schemaconfigglue,
@@ -406,30 +416,29 @@
         self.assertEqual(glue.options, expected_options)
         self.assertEqual(glue.args, expected_args)
 
-    @patch('configglue.glue.OptionParser')
     @patch('configglue.glue.SchemaConfigParser')
     @patch('configglue.glue.schemaconfigglue')
-    def test_configglue_with_usage(self, mock_schemaconfigglue,
-        mock_schema_parser, mock_option_parser):
-        """Test configglue with the 'usage' parameter set."""
+    def test_configglue_with_options(self, mock_schemaconfigglue,
+        mock_schema_parser):
+        """Test configglue with a custom OptionParser."""
+        # define the inputs
+        class MySchema(Schema):
+            foo = IntOption()
+
+        configs = ['config.ini']
+
+        op = OptionParser(usage='foo')
+
         # prepare mocks
         expected_schema_parser = Mock()
         expected_schema_parser.is_valid.return_value = (True, None)
-        expected_option_parser = mock_option_parser.return_value
-        expected_options = Mock()
         expected_args = Mock()
-        mock_schemaconfigglue.return_value = (expected_option_parser,
-            expected_options, expected_args)
+        mock_schemaconfigglue.return_value = (op,
+            op.values, expected_args)
         mock_schema_parser.return_value = expected_schema_parser
 
-        # define the inputs
-        class MySchema(Schema):
-            foo = IntOption()
-
-        configs = ['config.ini']
-
         # call the function under test
-        glue = configglue(MySchema, configs, usage='foo')
+        glue = configglue(MySchema, configs, op=op)
 
         # schema_parse is a SchemaConfigParser, initialized with MySchema
         # and fed with the configs file list
@@ -438,8 +447,60 @@
         mock_schema_parser.return_value.read.assert_called_with(configs)
         # the other attributes are the result of calling schemaconfigglue
         mock_schemaconfigglue.assert_called_with(expected_schema_parser,
-            op=expected_option_parser)
-        mock_option_parser.assert_called_with(usage='foo')
-        self.assertEqual(glue.option_parser, expected_option_parser)
-        self.assertEqual(glue.options, expected_options)
+            op=op)
+        self.assertEqual(glue.option_parser, op)
+        self.assertEqual(glue.options, op.values)
         self.assertEqual(glue.args, expected_args)
+
+    @patch('configglue.parser.SchemaConfigParser.is_valid')
+    def test_configglue_no_validate(self, mock_is_valid):
+        """Test configglue with validation disabled."""
+        mock_is_valid.return_value = (True, [])
+
+        configglue(Schema, [], validate=False)
+
+        # validation was not invoked
+        self.assertEqual(mock_is_valid.called, False)
+
+    @patch('configglue.parser.SchemaConfigParser.is_valid')
+    def test_configglue_validate(self, mock_is_valid):
+        """Test configglue with validation enabled."""
+        mock_is_valid.return_value = (True, [])
+
+        configglue(Schema, [], validate=True)
+
+        # validation was not invoked
+        self.assertEqual(mock_is_valid.called, True)
+
+    @patch('configglue.parser.SchemaConfigParser.is_valid')
+    def test_configglue_validate_default_value(self, mock_is_valid):
+        """Test configglue validation default."""
+        mock_is_valid.return_value = (True, [])
+
+        configglue(Schema, [])
+
+        # validation was not invoked
+        self.assertEqual(mock_is_valid.called, False)
+
+    @patch('configglue.parser.SchemaConfigParser.is_valid')
+    def test_configglue_validate_from_options(self, mock_is_valid):
+        """Test configglue with validation from options."""
+        mock_is_valid.return_value = (True, [])
+
+        op = OptionParser()
+        op.add_option('--validate', dest='validate', action='store_true')
+        with patch_object(sys, 'argv', ['foo', '--validate']):
+            configglue(Schema, [], op=op)
+
+        self.assertEqual(mock_is_valid.called, True)
+
+    @patch('configglue.parser.SchemaConfigParser.is_valid')
+    def test_configglue_validate_without_option(self, mock_is_valid):
+        """Test configglue with validation from options."""
+        mock_is_valid.return_value = (True, [])
+
+        op = OptionParser()
+        with patch_object(sys, 'argv', ['foo']):
+            configglue(Schema, [], op=op)
+
+        self.assertEqual(mock_is_valid.called, False)


References