diff --git a/cffi/cdefs.h b/cffi/cdefs.h index aa75004..05a24bd 100644 --- a/cffi/cdefs.h +++ b/cffi/cdefs.h @@ -215,6 +215,7 @@ struct lys_module* ly_ctx_get_module_latest(const struct ly_ctx *, const char *) LY_ERR ly_ctx_compile(struct ly_ctx *); LY_ERR lys_find_xpath(const struct ly_ctx *, const struct lysc_node *, const char *, uint32_t, struct ly_set **); +LY_ERR lys_find_xpath_atoms(const struct ly_ctx *, const struct lysc_node *, const char *, uint32_t, struct ly_set **); void ly_set_free(struct ly_set *, void(*)(void *obj)); struct ly_set { diff --git a/libyang/context.py b/libyang/context.py index f9bd5a5..03be1ca 100644 --- a/libyang/context.py +++ b/libyang/context.py @@ -401,6 +401,38 @@ def find_path( finally: lib.ly_set_free(node_set, ffi.NULL) + def find_xpath_atoms( + self, + path: str, + output: bool = False, + root_node: Optional["libyang.SNode"] = None, + ) -> Iterator[SNode]: + if self.cdata is None: + raise RuntimeError("context already destroyed") + + if root_node is not None: + ctx_node = root_node.cdata + else: + ctx_node = ffi.NULL + + flags = lib.LYS_FIND_XP_OUTPUT if output else 0 + + node_set = ffi.new("struct ly_set **") + if ( + lib.lys_find_xpath_atoms(self.cdata, ctx_node, str2c(path), flags, node_set) + != lib.LY_SUCCESS + ): + raise self.error("cannot find path") + + node_set = node_set[0] + if node_set.count == 0: + raise self.error("cannot find path") + try: + for i in range(node_set.count): + yield SNode.new(self, node_set.snodes[i]) + finally: + lib.ly_set_free(node_set, ffi.NULL) + def find_jsonpath( self, path: str, diff --git a/tests/test_context.py b/tests/test_context.py index db03c32..61c0ae8 100644 --- a/tests/test_context.py +++ b/tests/test_context.py @@ -4,7 +4,7 @@ import os import unittest -from libyang import Context, LibyangError, Module, SLeaf, SLeafList +from libyang import Context, LibyangError, Module, SContainer, SLeaf, SLeafList from libyang.util import c2str @@ -95,6 +95,22 @@ def test_ctx_find_path(self): node2 = next(ctx.find_path("../number", root_node=node)) self.assertIsInstance(node2, SLeafList) + def test_ctx_find_xpath_atoms(self): + with Context(YANG_DIR) as ctx: + ctx.load_module("yolo-system") + node_iter = ctx.find_xpath_atoms("/yolo-system:conf/offline") + node = next(node_iter) + self.assertIsInstance(node, SContainer) + node = next(node_iter) + self.assertIsInstance(node, SLeaf) + node_iter = ctx.find_xpath_atoms("../number", root_node=node) + node = next(node_iter) + self.assertIsInstance(node, SLeaf) + node = next(node_iter) + self.assertIsInstance(node, SContainer) + node = next(node_iter) + self.assertIsInstance(node, SLeafList) + def test_ctx_iter_modules(self): with Context(YANG_DIR) as ctx: ctx.load_module("yolo-system")