Explorar o código

Fix traceback when a YAML validation error occurs (#480, #482).

Dan Helfman %!s(int64=3) %!d(string=hai) anos
pai
achega
bec73245e9
Modificáronse 4 ficheiros con 29 adicións e 26 borrados
  1. 3 0
      NEWS
  2. 9 7
      borgmatic/config/validate.py
  3. 1 1
      setup.py
  4. 16 18
      tests/unit/config/test_validate.py

+ 3 - 0
NEWS

@@ -1,3 +1,6 @@
+1.5.23.dev0
+ * #480, #482: Fix traceback when a YAML validation error occurs.
+
 1.5.22
  * #288: Add database dump hook for MongoDB.
  * #470: Move mysqldump options to the beginning of the command due to MySQL bug 30994.

+ 9 - 7
borgmatic/config/validate.py

@@ -15,7 +15,7 @@ def schema_filename():
     return pkg_resources.resource_filename('borgmatic', 'config/schema.yaml')
 
 
-def format_error_path_element(path_element):
+def format_json_error_path_element(path_element):
     '''
     Given a path element into a JSON data structure, format it for display as a string.
     '''
@@ -25,14 +25,14 @@ def format_error_path_element(path_element):
     return str('.{}'.format(path_element))
 
 
-def format_error(error):
+def format_json_error(error):
     '''
     Given an instance of jsonschema.exceptions.ValidationError, format it for display as a string.
     '''
     if not error.path:
         return 'At the top level: {}'.format(error.message)
 
-    formatted_path = ''.join(format_error_path_element(element) for element in error.path)
+    formatted_path = ''.join(format_json_error_path_element(element) for element in error.path)
     return "At '{}': {}".format(formatted_path.lstrip('.'), error.message)
 
 
@@ -44,8 +44,8 @@ class Validation_error(ValueError):
 
     def __init__(self, config_filename, errors):
         '''
-        Given a configuration filename path and a sequence of
-        jsonschema.exceptions.ValidationError instances, create a Validation_error.
+        Given a configuration filename path and a sequence of string error messages, create a
+        Validation_error.
         '''
         self.config_filename = config_filename
         self.errors = errors
@@ -56,7 +56,7 @@ class Validation_error(ValueError):
         '''
         return 'An error occurred while parsing a configuration file at {}:\n'.format(
             self.config_filename
-        ) + '\n'.join(format_error(error) for error in self.errors)
+        ) + '\n'.join(error for error in self.errors)
 
 
 def apply_logical_validation(config_filename, parsed_configuration):
@@ -117,7 +117,9 @@ def parse_configuration(config_filename, schema_filename, overrides=None):
     validation_errors = tuple(validator.iter_errors(config))
 
     if validation_errors:
-        raise Validation_error(config_filename, validation_errors)
+        raise Validation_error(
+            config_filename, tuple(format_json_error(error) for error in validation_errors)
+        )
 
     apply_logical_validation(config_filename, config)
 

+ 1 - 1
setup.py

@@ -1,6 +1,6 @@
 from setuptools import find_packages, setup
 
-VERSION = '1.5.22'
+VERSION = '1.5.23.dev0'
 
 
 setup(

+ 16 - 18
tests/unit/config/test_validate.py

@@ -4,33 +4,31 @@ from flexmock import flexmock
 from borgmatic.config import validate as module
 
 
-def test_format_error_path_element_formats_array_index():
-    module.format_error_path_element(3) == '[3]'
+def test_format_json_error_path_element_formats_array_index():
+    module.format_json_error_path_element(3) == '[3]'
 
 
-def test_format_error_path_element_formats_property():
-    module.format_error_path_element('foo') == '.foo'
+def test_format_json_error_path_element_formats_property():
+    module.format_json_error_path_element('foo') == '.foo'
 
 
-def test_format_error_formats_error_including_path():
-    flexmock(module).format_error_path_element = lambda element: '.{}'.format(element)
+def test_format_json_error_formats_error_including_path():
+    flexmock(module).format_json_error_path_element = lambda element: '.{}'.format(element)
     error = flexmock(message='oops', path=['foo', 'bar'])
 
-    assert module.format_error(error) == "At 'foo.bar': oops"
+    assert module.format_json_error(error) == "At 'foo.bar': oops"
 
 
-def test_format_error_formats_error_without_path():
-    flexmock(module).should_receive('format_error_path_element').never()
+def test_format_json_error_formats_error_without_path():
+    flexmock(module).should_receive('format_json_error_path_element').never()
     error = flexmock(message='oops', path=[])
 
-    assert module.format_error(error) == 'At the top level: oops'
+    assert module.format_json_error(error) == 'At the top level: oops'
 
 
-def test_validation_error_string_contains_error_messages_and_config_filename():
-    flexmock(module).format_error = lambda error: error.message
-    error = module.Validation_error(
-        'config.yaml', (flexmock(message='oops', path=None), flexmock(message='uh oh'))
-    )
+def test_validation_error_string_contains_errors():
+    flexmock(module).format_json_error = lambda error: error.message
+    error = module.Validation_error('config.yaml', ('oops', 'uh oh'))
 
     result = str(error)
 
@@ -40,7 +38,7 @@ def test_validation_error_string_contains_error_messages_and_config_filename():
 
 
 def test_apply_logical_validation_raises_if_archive_name_format_present_without_prefix():
-    flexmock(module).format_error = lambda error: error.message
+    flexmock(module).format_json_error = lambda error: error.message
 
     with pytest.raises(module.Validation_error):
         module.apply_logical_validation(
@@ -53,7 +51,7 @@ def test_apply_logical_validation_raises_if_archive_name_format_present_without_
 
 
 def test_apply_logical_validation_raises_if_archive_name_format_present_without_retention_prefix():
-    flexmock(module).format_error = lambda error: error.message
+    flexmock(module).format_json_error = lambda error: error.message
 
     with pytest.raises(module.Validation_error):
         module.apply_logical_validation(
@@ -67,7 +65,7 @@ def test_apply_logical_validation_raises_if_archive_name_format_present_without_
 
 
 def test_apply_locical_validation_raises_if_unknown_repository_in_check_repositories():
-    flexmock(module).format_error = lambda error: error.message
+    flexmock(module).format_json_error = lambda error: error.message
 
     with pytest.raises(module.Validation_error):
         module.apply_logical_validation(