|
4 | 4 | # SPDX-License-Identifier: MIT
|
5 | 5 |
|
6 | 6 | import os
|
7 |
| -from typing import IO, Any, Callable, Iterator, Optional, Tuple, Union |
| 7 | +from typing import IO, Any, Callable, Iterator, Optional, Tuple, Union, List |
8 | 8 |
|
9 | 9 | from _libyang import ffi, lib
|
10 | 10 | from .data import (
|
@@ -646,6 +646,104 @@ def parse_data_file(
|
646 | 646 | json_null=json_null,
|
647 | 647 | )
|
648 | 648 |
|
| 649 | + def find_leafref_path_target_paths(self, leafref_path: str) -> List[str]: |
| 650 | + """ |
| 651 | + Fetch all leafref targets of the specified path |
| 652 | +
|
| 653 | + This is an enhanced version of lysc_node_lref_target() which will return |
| 654 | + a set of leafref target paths retrieved from the specified schema path. |
| 655 | + While lysc_node_lref_target() will only work on nodetype of LYS_LEAF and |
| 656 | + LYS_LEAFLIST this function will also evaluate other datatypes that may |
| 657 | + contain leafrefs such as LYS_UNION. This does not, however, search for |
| 658 | + children with leafref targets. |
| 659 | +
|
| 660 | + :arg self |
| 661 | + This instance on context |
| 662 | + :arg leafref_path: |
| 663 | + Path to node to search for leafref targets |
| 664 | + :returns List of target paths that the leafrefs of the specified node |
| 665 | + point to. |
| 666 | + """ |
| 667 | + if self.cdata is None: |
| 668 | + raise RuntimeError("context already destroyed") |
| 669 | + if leafref_path is None: |
| 670 | + raise RuntimeError("leafref_path must be defined") |
| 671 | + |
| 672 | + out = [] |
| 673 | + |
| 674 | + node = lib.lys_find_path(self.cdata, ffi.NULL, str2c(leafref_path), 0) |
| 675 | + if node == ffi.NULL: |
| 676 | + raise self.error("leafref_path not found") |
| 677 | + |
| 678 | + node_set = ffi.new("struct ly_set **") |
| 679 | + if (lib.lysc_node_lref_targets(node, node_set) != lib.LY_SUCCESS or |
| 680 | + node_set[0] == ffi.NULL or node_set[0].count == 0): |
| 681 | + raise self.error("leafref_path does not contain any leafref targets") |
| 682 | + |
| 683 | + node_set = node_set[0] |
| 684 | + for i in range(node_set.count): |
| 685 | + path = lib.lysc_path(node_set.snodes[i], lib.LYSC_PATH_DATA, ffi.NULL, 0); |
| 686 | + out.append(c2str(path)) |
| 687 | + lib.free(path) |
| 688 | + |
| 689 | + lib.ly_set_free(node_set, ffi.NULL) |
| 690 | + |
| 691 | + return out |
| 692 | + |
| 693 | + |
| 694 | + def find_backlinks_paths(self, match_path: str = None, match_ancestors: bool = False) -> List[str]: |
| 695 | + """ |
| 696 | + Search entire schema for nodes that contain leafrefs and return as a |
| 697 | + list of schema node paths. |
| 698 | +
|
| 699 | + Perform a complete scan of the schema tree looking for nodes that |
| 700 | + contain leafref entries. When a node contains a leafref entry, and |
| 701 | + match_path is specified, determine if reference points to match_path, |
| 702 | + if so add the node's path to returned list. If no match_path is |
| 703 | + specified, the node containing the leafref is always added to the |
| 704 | + returned set. When match_ancestors is true, will evaluate if match_path |
| 705 | + is self or an ansestor of self. |
| 706 | +
|
| 707 | + This does not return the leafref targets, but the actual node that |
| 708 | + contains a leafref. |
| 709 | +
|
| 710 | + :arg self |
| 711 | + This instance on context |
| 712 | + :arg match_path: |
| 713 | + Target path to use for matching |
| 714 | + :arg match_ancestors: |
| 715 | + Whether match_path is a base ancestor or an exact node |
| 716 | + :returns List of paths. Exception of match_path is not found or if no |
| 717 | + backlinks are found. |
| 718 | + """ |
| 719 | + if self.cdata is None: |
| 720 | + raise RuntimeError("context already destroyed") |
| 721 | + out = [] |
| 722 | + |
| 723 | + match_node = ffi.NULL |
| 724 | + if match_path is not None and match_path == "/" or match_path == "": |
| 725 | + match_path = None |
| 726 | + |
| 727 | + if match_path: |
| 728 | + match_node = lib.lys_find_path(self.cdata, ffi.NULL, str2c(match_path), 0) |
| 729 | + if match_node == ffi.NULL: |
| 730 | + raise self.error("match_path not found") |
| 731 | + |
| 732 | + node_set = ffi.new("struct ly_set **") |
| 733 | + if (lib.lysc_node_lref_backlinks(self.cdata, match_node, match_ancestors, node_set) |
| 734 | + != lib.LY_SUCCESS or node_set[0] == ffi.NULL or node_set[0].count == 0): |
| 735 | + raise self.error("backlinks not found") |
| 736 | + |
| 737 | + node_set = node_set[0] |
| 738 | + for i in range(node_set.count): |
| 739 | + path = lib.lysc_path(node_set.snodes[i], lib.LYSC_PATH_DATA, ffi.NULL, 0); |
| 740 | + out.append(c2str(path)) |
| 741 | + lib.free(path) |
| 742 | + |
| 743 | + lib.ly_set_free(node_set, ffi.NULL) |
| 744 | + |
| 745 | + return out |
| 746 | + |
649 | 747 | def __iter__(self) -> Iterator[Module]:
|
650 | 748 | """
|
651 | 749 | Return an iterator that yields all implemented modules from the context
|
|
0 commit comments