Skip to content

Commit e657a6f

Browse files
author
Pepa Hajek
committed
cffi: adapt bindings for libyang 5.x API changes
libyang 5.x introduced several breaking changes compared to 4.x: - Bump minimum required soversion to 5.3.0. - Add ly_yang_module_dir() and ly_ctx_set_options() to cdefs.h. - In Context.__init__, always add ly_yang_module_dir() to the search path so that internal modules (no longer embedded in 5.x) are found at context creation time. When disable_searchdirs=True, apply LY_CTX_DISABLE_SEARCHDIRS via ly_ctx_set_options() after context creation rather than passing it to ly_ctx_new(), which would prevent loading of internal modules. - ly_log_options(): int -> uint32_t. - lyd_node.parent and related fields: lyd_node_inner * -> lyd_node *. - Remove LYD_NEW_VAL_BIN and LYS_GETNEXT_WITHSCHEMAMOUNT (removed in 5.x). - lys_find_child(): extended signature with ly_ctx and name filter params. - lyd_node_any: replace lyd_any_value union and LYD_ANYDATA_VALUETYPE with child/children_ht/value/hints fields matching the new struct layout. - lyd_any_value_str(): add LYD_FORMAT parameter and pass data_format(fmt) from the Python callers. - lyd_dup_siblings/single(): lyd_node_inner * -> lyd_node *. - Update tests for libyang 5.x behavioral changes: module import callbacks are also called for transitive imports, and error data paths may omit intermediate containers. Signed-off-by: Pepa Hajek <hajekpepa@gmail.com>
1 parent a69eb4a commit e657a6f

7 files changed

Lines changed: 56 additions & 58 deletions

File tree

cffi/cdefs.h

Lines changed: 17 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
* SPDX-License-Identifier: MIT
44
*/
55
struct ly_ctx;
6+
struct ly_ht;
67

78
#define LY_CTX_ALL_IMPLEMENTED ...
89
#define LY_CTX_REF_IMPLEMENTED ...
@@ -40,6 +41,8 @@ typedef enum {
4041
LY_ERR ly_ctx_new(const char *, uint16_t, struct ly_ctx **);
4142
void ly_ctx_destroy(struct ly_ctx *);
4243
int ly_ctx_set_searchdir(struct ly_ctx *, const char *);
44+
const char *ly_yang_module_dir(void);
45+
LY_ERR ly_ctx_set_options(struct ly_ctx *, uint32_t);
4346

4447
typedef enum
4548
{
@@ -182,7 +185,7 @@ enum ly_stmt {
182185
#define LY_LOLOG ...
183186
#define LY_LOSTORE ...
184187
#define LY_LOSTORE_LAST ...
185-
int ly_log_options(int);
188+
uint32_t ly_log_options(uint32_t);
186189
uint32_t *ly_temp_log_options(uint32_t *);
187190

188191
LY_LOG_LEVEL ly_log_level(LY_LOG_LEVEL);
@@ -262,7 +265,7 @@ struct lyd_node {
262265
uint32_t hash;
263266
uint32_t flags;
264267
const struct lysc_node *schema;
265-
struct lyd_node_inner *parent;
268+
struct lyd_node *parent;
266269
struct lyd_node *next;
267270
struct lyd_node *prev;
268271
struct lyd_meta *meta;
@@ -273,7 +276,6 @@ LY_ERR lys_set_implemented(struct lys_module *, const char **);
273276

274277
#define LYD_NEW_VAL_OUTPUT ...
275278
#define LYD_NEW_VAL_STORE_ONLY ...
276-
#define LYD_NEW_VAL_BIN ...
277279
#define LYD_NEW_VAL_CANON ...
278280
#define LYD_NEW_META_CLEAR_DFLT ...
279281
#define LYD_NEW_PATH_UPDATE ...
@@ -876,9 +878,8 @@ struct lysc_ext {
876878
#define LYS_GETNEXT_WITHCASE ...
877879
#define LYS_GETNEXT_INTONPCONT ...
878880
#define LYS_GETNEXT_OUTPUT ...
879-
#define LYS_GETNEXT_WITHSCHEMAMOUNT ...
880881

881-
const struct lysc_node* lys_find_child(const struct lysc_node *, const struct lys_module *, const char *, size_t, uint16_t, uint32_t);
882+
const struct lysc_node* lys_find_child(const struct ly_ctx *, const struct lysc_node *, const struct lys_module *, const char *, uint32_t, const char *, uint32_t, uint32_t);
882883
const struct lysc_node* lysc_node_child(const struct lysc_node *);
883884
const struct lysc_node_action* lysc_node_actions(const struct lysc_node *);
884885
const struct lysc_node_notif* lysc_node_notifs(const struct lysc_node *);
@@ -902,7 +903,7 @@ struct lyd_node_inner {
902903
uint32_t hash;
903904
uint32_t flags;
904905
const struct lysc_node *schema;
905-
struct lyd_node_inner *parent;
906+
struct lyd_node *parent;
906907
struct lyd_node *next;
907908
struct lyd_node *prev;
908909
struct lyd_meta *meta;
@@ -924,7 +925,7 @@ struct lyd_node_term {
924925
uint32_t hash;
925926
uint32_t flags;
926927
const struct lysc_node *schema;
927-
struct lyd_node_inner *parent;
928+
struct lyd_node *parent;
928929
struct lyd_node *next;
929930
struct lyd_node *prev;
930931
struct lyd_meta *meta;
@@ -1161,39 +1162,27 @@ struct lyd_meta {
11611162
struct lyd_value value;
11621163
};
11631164

1164-
typedef enum {
1165-
LYD_ANYDATA_DATATREE,
1166-
LYD_ANYDATA_STRING,
1167-
LYD_ANYDATA_XML,
1168-
LYD_ANYDATA_JSON
1169-
} LYD_ANYDATA_VALUETYPE;
1170-
1171-
union lyd_any_value {
1172-
struct lyd_node *tree;
1173-
const char *str;
1174-
const char *xml;
1175-
const char *json;
1176-
};
1177-
11781165
struct lyd_node_any {
11791166
union {
11801167
struct lyd_node node;
11811168
struct {
11821169
uint32_t hash;
11831170
uint32_t flags;
11841171
const struct lysc_node *schema;
1185-
struct lyd_node_inner *parent;
1172+
struct lyd_node *parent;
11861173
struct lyd_node *next;
11871174
struct lyd_node *prev;
11881175
struct lyd_meta *meta;
11891176
void *priv;
11901177
};
11911178
};
1192-
union lyd_any_value value;
1193-
LYD_ANYDATA_VALUETYPE value_type;
1179+
struct lyd_node *child;
1180+
struct ly_ht *children_ht;
1181+
const char *value;
1182+
uint32_t hints;
11941183
};
11951184

1196-
LY_ERR lyd_any_value_str(const struct lyd_node *, char **);
1185+
LY_ERR lyd_any_value_str(const struct lyd_node *, LYD_FORMAT, char **);
11971186

11981187
#define LYD_MERGE_DEFAULTS ...
11991188
#define LYD_MERGE_DESTRUCT ...
@@ -1212,8 +1201,8 @@ LY_ERR lyd_diff_apply_all(struct lyd_node **, const struct lyd_node *);
12121201
#define LYD_DUP_WITH_FLAGS ...
12131202
#define LYD_DUP_WITH_PARENTS ...
12141203

1215-
LY_ERR lyd_dup_siblings(const struct lyd_node *, struct lyd_node_inner *, uint32_t, struct lyd_node **);
1216-
LY_ERR lyd_dup_single(const struct lyd_node *, struct lyd_node_inner *, uint32_t, struct lyd_node **);
1204+
LY_ERR lyd_dup_siblings(const struct lyd_node *, struct lyd_node *, uint32_t, struct lyd_node **);
1205+
LY_ERR lyd_dup_single(const struct lyd_node *, struct lyd_node *, uint32_t, struct lyd_node **);
12171206
void lyd_free_meta_single(struct lyd_meta *);
12181207

12191208
struct lysp_tpdf {
@@ -1291,7 +1280,7 @@ struct lyd_node_opaq {
12911280
uint32_t hash;
12921281
uint32_t flags;
12931282
const struct lysc_node *schema;
1294-
struct lyd_node_inner *parent;
1283+
struct lyd_node *parent;
12951284
struct lyd_node *next;
12961285
struct lyd_node *prev;
12971286
struct lyd_meta *meta;

cffi/source.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@
66
#include <libyang/libyang.h>
77
#include <libyang/version.h>
88

9-
#if LY_VERSION_MAJOR * 10000 + LY_VERSION_MINOR * 100 + LY_VERSION_MICRO < 40202
10-
#error "This version of libyang bindings only works with libyang soversion 4.2.2+"
9+
#if LY_VERSION_MAJOR * 10000 + LY_VERSION_MINOR * 100 + LY_VERSION_MICRO < 50300
10+
#error "This version of libyang bindings only works with libyang soversion 5.3.0+"
1111
#endif

libyang/context.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -216,8 +216,6 @@ def __init__(
216216
return # already initialized
217217

218218
options = 0
219-
if disable_searchdirs:
220-
options |= lib.LY_CTX_DISABLE_SEARCHDIRS
221219
if disable_searchdir_cwd:
222220
options |= lib.LY_CTX_DISABLE_SEARCHDIR_CWD
223221
if explicit_compile:
@@ -241,6 +239,9 @@ def __init__(
241239
ctx = ffi.new("struct ly_ctx **")
242240

243241
search_paths = []
242+
yang_module_dir = c2str(lib.ly_yang_module_dir())
243+
if yang_module_dir:
244+
search_paths.append(yang_module_dir)
244245
if "YANGPATH" in os.environ:
245246
search_paths.extend(os.environ["YANGPATH"].strip(": \t\r\n'\"").split(":"))
246247
elif "YANG_MODPATH" in os.environ:
@@ -274,6 +275,8 @@ def __init__(
274275
)
275276
if not self.cdata:
276277
raise self.error("cannot create context")
278+
if disable_searchdirs:
279+
lib.ly_ctx_set_options(self.cdata, lib.LY_CTX_DISABLE_SEARCHDIRS)
277280
self.external_module_loader = ContextExternalModuleLoader(self.cdata)
278281

279282
def compile_schema(self):

libyang/data.py

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,6 @@ def data_format(fmt_string: str) -> int:
8080
def newval_flags(
8181
rpc_output: bool = False,
8282
store_only: bool = False,
83-
bin_value: bool = False,
8483
canon_value: bool = False,
8584
meta_clear_default: bool = False,
8685
update: bool = False,
@@ -94,8 +93,6 @@ def newval_flags(
9493
flags |= lib.LYD_NEW_VAL_OUTPUT
9594
if store_only:
9695
flags |= lib.LYD_NEW_VAL_STORE_ONLY
97-
if bin_value:
98-
flags |= lib.LYD_NEW_VAL_BIN
9996
if canon_value:
10097
flags |= lib.LYD_NEW_VAL_CANON
10198
if meta_clear_default:
@@ -391,15 +388,13 @@ def new_path(
391388
opt_update: bool = False,
392389
opt_output: bool = False,
393390
opt_opaq: bool = False,
394-
opt_bin_value: bool = False,
395391
opt_canon_value: bool = False,
396392
opt_store_only: bool = False,
397393
):
398394
flags = newval_flags(
399395
update=opt_update,
400396
rpc_output=opt_output,
401397
opaq=opt_opaq,
402-
bin_value=opt_bin_value,
403398
canon_value=opt_canon_value,
404399
store_only=opt_store_only,
405400
)
@@ -1174,7 +1169,7 @@ class DNotif(DContainer):
11741169
class DAnyxml(DNode):
11751170
def value(self, fmt: str = "xml"):
11761171
anystr = ffi.new("char **", ffi.NULL)
1177-
ret = lib.lyd_any_value_str(self.cdata, anystr)
1172+
ret = lib.lyd_any_value_str(self.cdata, data_format(fmt), anystr)
11781173
if ret != lib.LY_SUCCESS:
11791174
raise self.context.error("cannot get data")
11801175
return c2str(anystr[0])
@@ -1185,7 +1180,7 @@ def value(self, fmt: str = "xml"):
11851180
class DAnydata(DNode):
11861181
def value(self, fmt: str = "xml"):
11871182
anystr = ffi.new("char **", ffi.NULL)
1188-
ret = lib.lyd_any_value_str(self.cdata, anystr)
1183+
ret = lib.lyd_any_value_str(self.cdata, data_format(fmt), anystr)
11891184
if ret != lib.LY_SUCCESS:
11901185
raise self.context.error("cannot get data")
11911186
return c2str(anystr[0])

libyang/schema.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1872,7 +1872,6 @@ def iter_children_options(
18721872
with_case: bool = False,
18731873
into_non_presence_container: bool = False,
18741874
output: bool = False,
1875-
with_schema_mount: bool = False,
18761875
) -> int:
18771876
options = 0
18781877
if with_choice:
@@ -1885,8 +1884,6 @@ def iter_children_options(
18851884
options |= lib.LYS_GETNEXT_INTONPCONT
18861885
if output:
18871886
options |= lib.LYS_GETNEXT_OUTPUT
1888-
if with_schema_mount:
1889-
options |= lib.LYS_GETNEXT_WITHSCHEMAMOUNT
18901887
return options
18911888

18921889

@@ -1901,7 +1898,6 @@ def iter_children(
19011898
with_case: bool = False,
19021899
into_non_presence_container: bool = False,
19031900
output: bool = False,
1904-
with_schema_mount: bool = False,
19051901
) -> Iterator[SNode]:
19061902
if types is None:
19071903
types = (
@@ -1942,7 +1938,6 @@ def _skip(node) -> bool:
19421938
with_case=with_case,
19431939
into_non_presence_container=into_non_presence_container,
19441940
output=output,
1945-
with_schema_mount=with_schema_mount,
19461941
)
19471942
child = lib.lys_getnext(ffi.NULL, parent, module, options)
19481943
while child:

tests/test_context.py

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
# Copyright (c) 2018-2019 Robin Jarry
22
# SPDX-License-Identifier: MIT
33

4+
import glob
45
import os
56
import unittest
67

8+
from _libyang import lib
79
from libyang import Context, LibyangError, Module, SContainer, SLeaf, SLeafList
810
from libyang.util import c2str
911

@@ -153,12 +155,23 @@ def test_ctx_disable_searchdirs(self):
153155
ctx.load_module("yolo-nodetypes")
154156

155157
def test_ctx_using_clb(self):
156-
def get_module_valid_clb(mod_name, *_):
158+
def get_module_valid_clb(mod_name, mod_rev, *_):
157159
YOLO_NODETYPES_MOD_PATH = os.path.join(YANG_DIR, "yolo/yolo-nodetypes.yang")
158-
self.assertEqual(mod_name, "yolo-nodetypes")
159-
with open(YOLO_NODETYPES_MOD_PATH, encoding="utf-8") as f:
160-
mod_str = f.read()
161-
return "yang", mod_str
160+
if mod_name == "yolo-nodetypes":
161+
with open(YOLO_NODETYPES_MOD_PATH, encoding="utf-8") as f:
162+
return "yang", f.read()
163+
# libyang 5.x calls the callback also for imports (e.g. ietf-inet-types)
164+
# since internal modules are no longer embedded; serve them from the
165+
# yang module dir if available.
166+
yang_dir = c2str(lib.ly_yang_module_dir())
167+
if yang_dir:
168+
rev_suffix = f"@{mod_rev}" if mod_rev else ""
169+
for path in glob.glob(
170+
os.path.join(yang_dir, f"{mod_name}{rev_suffix}*.yang")
171+
):
172+
with open(path, encoding="utf-8") as f:
173+
return "yang", f.read()
174+
return None
162175

163176
def get_module_invalid_clb(mod_name, *_):
164177
return None

tests/test_data.py

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -285,24 +285,27 @@ def test_data_parse_config_xml_multi_error(self):
285285
validate_present=True,
286286
validate_multi_error=True,
287287
)
288-
self.assertEqual(
288+
self.assertIn(
289+
'Invalid boolean value "abcd".',
290+
str(cm.exception),
291+
)
292+
self.assertIn(
293+
"url[proto='https']/enabled",
294+
str(cm.exception),
295+
)
296+
self.assertIn(
297+
'List instance is missing its key "host".',
289298
str(cm.exception),
290-
'failed to parse data tree: Invalid boolean value "abcd".: '
291-
"Data path: /yolo-system:conf/url[proto='https']/enabled (line 6): "
292-
'List instance is missing its key "host".: '
293-
"Data path: /yolo-system:conf/url[proto='https'] (line 7)",
294299
)
295300

296301
first = cm.exception.errors[0]
297302
self.assertEqual(first.msg, 'Invalid boolean value "abcd".')
298-
self.assertEqual(
299-
first.data_path, "/yolo-system:conf/url[proto='https']/enabled"
300-
)
303+
self.assertIn("url[proto='https']/enabled", first.data_path)
301304
self.assertEqual(first.line, 6)
302305

303306
second = cm.exception.errors[1]
304307
self.assertEqual(second.msg, 'List instance is missing its key "host".')
305-
self.assertEqual(second.data_path, "/yolo-system:conf/url[proto='https']")
308+
self.assertIn("url[proto='https']", second.data_path)
306309
self.assertEqual(second.line, 7)
307310

308311
XML_STATE = """<state xmlns="urn:yang:yolo:system">

0 commit comments

Comments
 (0)