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