Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

data: add support of anydata into dict_to_dnode #115

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cffi/cdefs.h
Original file line number Diff line number Diff line change
Expand Up @@ -1172,6 +1172,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 @@ -1131,3 +1133,16 @@ def test_merge_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)
Loading