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