Skip to content

Commit

Permalink
data: add support of anydata into dict_to_dnode
Browse files Browse the repository at this point in the history
This patch adds support of anydata within dict_to_dnode. To do that new
options were introduced into dict_to_dnode() API

Signed-off-by: Stefan Gula <[email protected]>
  • Loading branch information
steweg committed Sep 29, 2024
1 parent adab39c commit e7befe5
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 1 deletion.
1 change: 1 addition & 0 deletions cffi/cdefs.h
Original file line number Diff line number Diff line change
Expand Up @@ -1133,6 +1133,7 @@ struct lyd_node_any {
};

LY_ERR lyd_any_value_str(const struct lyd_node *, char **);
LY_ERR lyd_new_any(struct lyd_node *, const struct lys_module *, const char *, const void *, LYD_ANYDATA_VALUETYPE, uint32_t, struct lyd_node **);

#define LYD_MERGE_DEFAULTS ...
#define LYD_MERGE_DESTRUCT ...
Expand Down
52 changes: 51 additions & 1 deletion libyang/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@
# Copyright (c) 2021 RACOM s.r.o.
# SPDX-License-Identifier: MIT

import json
import logging
from typing import IO, Any, Dict, Iterator, Optional, Tuple, Union

from _libyang import ffi, lib
from .keyed_list import KeyedList
from .schema import (
Module,
SAnydata,
SContainer,
SLeaf,
SLeafList,
Expand Down Expand Up @@ -107,6 +109,21 @@ def newval_flags(
return flags


# -------------------------------------------------------------------------------------
def anydata_format(fmt_string: str) -> int:
if fmt_string == "datatree":
return lib.LYD_ANYDATA_DATATREE
if fmt_string == "string":
return lib.LYD_ANYDATA_STRING
if fmt_string == "xml":
return lib.LYD_ANYDATA_XML
if fmt_string == "json":
return lib.LYD_ANYDATA_JSON
if fmt_string == "lyb":
return lib.LYD_ANYDATA_LYB
raise ValueError("unknown anydata format: %r" % fmt_string)


# -------------------------------------------------------------------------------------
def parser_flags(
lyb_mod_update: bool = False,
Expand Down Expand Up @@ -1192,6 +1209,8 @@ def dict_to_dnode(
rpcreply: bool = False,
notification: bool = False,
store_only: bool = False,
types: Optional[Tuple[int, ...]] = None,
anydata_fmt: str = "json",
) -> Optional[DNode]:
"""
Convert a python dictionary to a DNode object given a YANG module object. The return
Expand Down Expand Up @@ -1301,6 +1320,34 @@ def _create_list(_parent, module, name, key_values, in_rpc_output=False):
created.append(n[0])
return n[0]

def _create_anydata(_parent, module, name, value, in_rpc_output=False):
if value is not None:
if isinstance(value, dict) and anydata_fmt == "json":
value = json.dumps(value)
elif not isinstance(value, str):
value = str(value)

n = ffi.new("struct lyd_node **")
flags = newval_flags(rpc_output=in_rpc_output, store_only=store_only)
ret = lib.lyd_new_any(
_parent,
module.cdata,
str2c(name),
str2c(value),
anydata_format(anydata_fmt),
flags,
n,
)
if ret != lib.LY_SUCCESS:
if _parent:
parent_path = repr(DNode.new(module.context, _parent).path())
else:
parent_path = "module %r" % module.name()
raise module.context.error(
"failed to create leaf %r as a child of %s", name, parent_path
)
created.append(n[0])

schema_cache = {}

def _find_schema(schema_parent, name, prefix):
Expand All @@ -1317,7 +1364,7 @@ def _find_schema(schema_parent, name, prefix):
if schema_parent is None:
# there may not be any input or any output node in the rpc
return None, None
for s in schema_parent:
for s in schema_parent.children(types=types):
if s.name() != name:
continue
mod = s.module()
Expand Down Expand Up @@ -1417,6 +1464,9 @@ def _to_dnode(_dic, _schema, _parent=ffi.NULL, in_rpc_output=False):
n = _create_container(_parent, module, name, in_rpc_output)
_to_dnode(value, s, n, in_rpc_output)

elif isinstance(s, SAnydata):
_create_anydata(_parent, module, name, value, in_rpc_output)

result = None

try:
Expand Down
15 changes: 15 additions & 0 deletions tests/test_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from _libyang import lib
from libyang import (
Context,
DAnydata,
DAnyxml,
DataType,
DContainer,
Expand All @@ -23,6 +24,7 @@
IOType,
LibyangError,
Module,
SNode,
)
from libyang.data import dict_to_dnode

Expand Down Expand Up @@ -1112,3 +1114,16 @@ def test_dnode_builtin_plugins_only(self):
self.assertIsInstance(dnode, DLeaf)
self.assertEqual(dnode.value(), "test")
dnode.free()

def test_dnode_anydata_dict_to_dnode(self):
anydata_json = """{
"yolo-nodetypes:any1": {
"key1": "val1"
}
}"""
data = json.loads(anydata_json)
module = self.ctx.load_module("yolo-nodetypes")
dnode = dict_to_dnode(
data, module, None, validate=False, types=(SNode.ANYDATA,)
)
self.assertIsInstance(dnode, DAnydata)

0 comments on commit e7befe5

Please sign in to comment.