Skip to content

Commit 1e0949c

Browse files
committed
data: add support of anydata into dict_to_dnode
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]>
1 parent b35336b commit 1e0949c

File tree

3 files changed

+67
-1
lines changed

3 files changed

+67
-1
lines changed

cffi/cdefs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1189,6 +1189,7 @@ struct lyd_node_any {
11891189
};
11901190

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

11931194
#define LYD_MERGE_DEFAULTS ...
11941195
#define LYD_MERGE_DESTRUCT ...

libyang/data.py

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@
22
# Copyright (c) 2021 RACOM s.r.o.
33
# SPDX-License-Identifier: MIT
44

5+
import json
56
import logging
67
from typing import IO, Any, Dict, Iterator, Optional, Tuple, Union
78

89
from _libyang import ffi, lib
910
from .keyed_list import KeyedList
1011
from .schema import (
1112
Module,
13+
SAnydata,
1214
SContainer,
1315
SLeaf,
1416
SLeafList,
@@ -107,6 +109,21 @@ def newval_flags(
107109
return flags
108110

109111

112+
# -------------------------------------------------------------------------------------
113+
def anydata_format(fmt_string: str) -> int:
114+
if fmt_string == "datatree":
115+
return lib.LYD_ANYDATA_DATATREE
116+
if fmt_string == "string":
117+
return lib.LYD_ANYDATA_STRING
118+
if fmt_string == "xml":
119+
return lib.LYD_ANYDATA_XML
120+
if fmt_string == "json":
121+
return lib.LYD_ANYDATA_JSON
122+
if fmt_string == "lyb":
123+
return lib.LYD_ANYDATA_LYB
124+
raise ValueError("unknown anydata format: %r" % fmt_string)
125+
126+
110127
# -------------------------------------------------------------------------------------
111128
def parser_flags(
112129
lyb_mod_update: bool = False,
@@ -1207,6 +1224,8 @@ def dict_to_dnode(
12071224
rpcreply: bool = False,
12081225
notification: bool = False,
12091226
store_only: bool = False,
1227+
types: Optional[Tuple[int, ...]] = None,
1228+
anydata_fmt: str = "json",
12101229
) -> Optional[DNode]:
12111230
"""
12121231
Convert a python dictionary to a DNode object given a YANG module object. The return
@@ -1316,6 +1335,34 @@ def _create_list(_parent, module, name, key_values, in_rpc_output=False):
13161335
created.append(n[0])
13171336
return n[0]
13181337

1338+
def _create_anydata(_parent, module, name, value, in_rpc_output=False):
1339+
if value is not None:
1340+
if isinstance(value, dict) and anydata_fmt == "json":
1341+
value = json.dumps(value)
1342+
elif not isinstance(value, str):
1343+
value = str(value)
1344+
1345+
n = ffi.new("struct lyd_node **")
1346+
flags = newval_flags(rpc_output=in_rpc_output, store_only=store_only)
1347+
ret = lib.lyd_new_any(
1348+
_parent,
1349+
module.cdata,
1350+
str2c(name),
1351+
str2c(value),
1352+
anydata_format(anydata_fmt),
1353+
flags,
1354+
n,
1355+
)
1356+
if ret != lib.LY_SUCCESS:
1357+
if _parent:
1358+
parent_path = repr(DNode.new(module.context, _parent).path())
1359+
else:
1360+
parent_path = "module %r" % module.name()
1361+
raise module.context.error(
1362+
"failed to create leaf %r as a child of %s", name, parent_path
1363+
)
1364+
created.append(n[0])
1365+
13191366
schema_cache = {}
13201367

13211368
def _find_schema(schema_parent, name, prefix):
@@ -1332,7 +1379,7 @@ def _find_schema(schema_parent, name, prefix):
13321379
if schema_parent is None:
13331380
# there may not be any input or any output node in the rpc
13341381
return None, None
1335-
for s in schema_parent:
1382+
for s in schema_parent.children(types=types):
13361383
if s.name() != name:
13371384
continue
13381385
mod = s.module()
@@ -1432,6 +1479,9 @@ def _to_dnode(_dic, _schema, _parent=ffi.NULL, in_rpc_output=False):
14321479
n = _create_container(_parent, module, name, in_rpc_output)
14331480
_to_dnode(value, s, n, in_rpc_output)
14341481

1482+
elif isinstance(s, SAnydata):
1483+
_create_anydata(_parent, module, name, value, in_rpc_output)
1484+
14351485
result = None
14361486

14371487
try:

tests/test_data.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from _libyang import lib
1212
from libyang import (
1313
Context,
14+
DAnydata,
1415
DAnyxml,
1516
DataType,
1617
DContainer,
@@ -23,6 +24,7 @@
2324
IOType,
2425
LibyangError,
2526
Module,
27+
SNode,
2628
)
2729
from libyang.data import dict_to_dnode
2830

@@ -1152,3 +1154,16 @@ def test_dnode_parse_json_null(self):
11521154
dnode = self.ctx.parse_data_mem(JSON, "json", json_null=True)
11531155
dnode_names = [d.name() for d in dnode.siblings()]
11541156
self.assertFalse("ip-address" in dnode_names)
1157+
1158+
def test_dnode_anydata_dict_to_dnode(self):
1159+
anydata_json = """{
1160+
"yolo-nodetypes:any1": {
1161+
"key1": "val1"
1162+
}
1163+
}"""
1164+
data = json.loads(anydata_json)
1165+
module = self.ctx.load_module("yolo-nodetypes")
1166+
dnode = dict_to_dnode(
1167+
data, module, None, validate=False, types=(SNode.ANYDATA,)
1168+
)
1169+
self.assertIsInstance(dnode, DAnydata)

0 commit comments

Comments
 (0)