From c5ceffc7acb282c3ba5742b3a0f8e1e4f1598af2 Mon Sep 17 00:00:00 2001 From: Sergei Markov Date: Thu, 25 Jan 2024 13:57:28 +0800 Subject: [PATCH 1/2] enable LYD_VALIDATE_MULTI_ERROR flag Do not stop validation on the first error but generate all the detected errors --- cffi/cdefs.h | 1 + libyang/context.py | 9 ++++++++- libyang/data.py | 3 +++ tests/test_data.py | 3 ++- 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/cffi/cdefs.h b/cffi/cdefs.h index c1eaee39..9738f370 100644 --- a/cffi/cdefs.h +++ b/cffi/cdefs.h @@ -311,6 +311,7 @@ LY_ERR lyd_print_all(struct ly_out *, const struct lyd_node *, LYD_FORMAT, uint3 #define LYD_VALIDATE_NO_STATE ... #define LYD_VALIDATE_PRESENT ... #define LYD_VALIDATE_OPTS_MASK ... +#define LYD_VALIDATE_MULTI_ERROR ... LY_ERR lyd_parse_data_mem(const struct ly_ctx *, const char *, LYD_FORMAT, uint32_t, uint32_t, struct lyd_node **); diff --git a/libyang/context.py b/libyang/context.py index 57432941..de590b85 100644 --- a/libyang/context.py +++ b/libyang/context.py @@ -326,6 +326,7 @@ def parse_data( ordered: bool = False, strict: bool = False, validate_present: bool = False, + validate_multi_error: bool = False, ) -> Optional[DNode]: if self.cdata is None: raise RuntimeError("context already destroyed") @@ -338,7 +339,9 @@ def parse_data( strict=strict, ) validation_flgs = validation_flags( - no_state=no_state, validate_present=validate_present + no_state=no_state, + validate_present=validate_present, + validate_multi_error=validate_multi_error, ) fmt = data_format(fmt) encode = True @@ -390,6 +393,7 @@ def parse_data_mem( ordered: bool = False, strict: bool = False, validate_present: bool = False, + validate_multi_error: bool = False, ) -> Optional[DNode]: return self.parse_data( fmt, @@ -403,6 +407,7 @@ def parse_data_mem( ordered=ordered, strict=strict, validate_present=validate_present, + validate_multi_error=validate_multi_error ) def parse_data_file( @@ -417,6 +422,7 @@ def parse_data_file( ordered: bool = False, strict: bool = False, validate_present: bool = False, + validate_multi_error: bool = False, ) -> Optional[DNode]: return self.parse_data( fmt, @@ -430,6 +436,7 @@ def parse_data_file( ordered=ordered, strict=strict, validate_present=validate_present, + validate_multi_error=validate_multi_error, ) def __iter__(self) -> Iterator[Module]: diff --git a/libyang/data.py b/libyang/data.py index 69649eba..7e917b92 100644 --- a/libyang/data.py +++ b/libyang/data.py @@ -171,12 +171,15 @@ def data_type(dtype): def validation_flags( no_state: bool = False, validate_present: bool = False, + validate_multi_error: bool = False, ) -> int: flags = 0 if no_state: flags |= lib.LYD_VALIDATE_NO_STATE if validate_present: flags |= lib.LYD_VALIDATE_PRESENT + if validate_multi_error: + flags |= lib.LYD_VALIDATE_MULTI_ERROR return flags diff --git a/tests/test_data.py b/tests/test_data.py index becb5d0c..433177c1 100644 --- a/tests/test_data.py +++ b/tests/test_data.py @@ -248,7 +248,8 @@ def test_data_parse_state_json(self): """ def test_data_parse_config_xml(self): - dnode = self.ctx.parse_data_mem(self.XML_CONFIG, "xml", validate_present=True) + dnode = self.ctx.parse_data_mem(self.XML_CONFIG, "xml", validate_present=True, + validate_multi_error=True) self.assertIsInstance(dnode, DContainer) try: xml = dnode.print_mem("xml", with_siblings=True, trim_default_values=True) From 59b1ee3129afd8e098321b380ae21df69e82a78a Mon Sep 17 00:00:00 2001 From: Sergei Markov Date: Fri, 26 Jan 2024 12:16:30 +0800 Subject: [PATCH 2/2] test: add test for validate_multi_error option --- tests/test_data.py | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/tests/test_data.py b/tests/test_data.py index 433177c1..4c3987e0 100644 --- a/tests/test_data.py +++ b/tests/test_data.py @@ -248,8 +248,7 @@ def test_data_parse_state_json(self): """ def test_data_parse_config_xml(self): - dnode = self.ctx.parse_data_mem(self.XML_CONFIG, "xml", validate_present=True, - validate_multi_error=True) + dnode = self.ctx.parse_data_mem(self.XML_CONFIG, "xml", validate_present=True) self.assertIsInstance(dnode, DContainer) try: xml = dnode.print_mem("xml", with_siblings=True, trim_default_values=True) @@ -861,3 +860,25 @@ def test_add_defaults(self): node = dnode.find_path("/yolo-system:conf/speed") self.assertIsInstance(node, DLeaf) self.assertEqual(node.value(), 4321) + + XML_CONFIG_MULTI_ERROR = """ + foo + + https + /CESNET/libyang-python + abcd + + 2000 + +""" + + def test_data_parse_config_xml_multi_error(self): + with self.assertRaises(Exception) as cm: + self.ctx.parse_data_mem(self.XML_CONFIG_MULTI_ERROR, "xml", + validate_present=True, + validate_multi_error=True) + self.assertEqual( + str(cm.exception), + 'failed to parse data tree: Invalid boolean value "abcd".: ' + 'List instance is missing its key "host".' + )