From 8c246567c03471e20cd9574f9c95a179712c3e3f Mon Sep 17 00:00:00 2001
From: Stefan Gula <steweg@gmail.com>
Date: Sun, 2 Nov 2025 21:26:55 +0100
Subject: [PATCH] cffi: allows to usage of libyang v4.2.2

This patch adjust cffi definitions and also all associated functions
based on libyang v4.2.2.

Signed-off-by: Stefan Gula <steweg@gmail.com>
---
 cffi/cdefs.h          | 61 ++++++++++++++++++++++-----------------
 cffi/source.c         |  4 +--
 libyang/context.py    | 25 ++++++++++------
 libyang/data.py       |  7 ++---
 libyang/extension.py  | 24 +++++++++++-----
 libyang/schema.py     | 66 +++++++++++++++++++++++++++++++++---------
 tests/test_context.py |  6 +++-
 tests/test_data.py    | 67 ++++++++++++++++++++++++-------------------
 tests/test_diff.py    |  4 ++-
 tests/test_schema.py  |  4 +--
 10 files changed, 172 insertions(+), 96 deletions(-)

diff --git a/cffi/cdefs.h b/cffi/cdefs.h
index 8b6d426..82ec974 100644
--- a/cffi/cdefs.h
+++ b/cffi/cdefs.h
@@ -17,6 +17,7 @@ struct ly_ctx;
 #define LY_CTX_LEAFREF_EXTENDED ...
 #define LY_CTX_LEAFREF_LINKING ...
 #define LY_CTX_BUILTIN_PLUGINS_ONLY ...
+#define LY_CTX_COMPILE_OBSOLETE ...
 
 
 typedef enum {
@@ -304,20 +305,19 @@ enum lyd_type {
     LYD_TYPE_REPLY_RESTCONF
 };
 
-#define LYD_PRINT_KEEPEMPTYCONT ...
 #define LYD_PRINT_SHRINK   ...
+#define LYD_PRINT_EMPTY_CONT ...
 #define LYD_PRINT_WD_ALL ...
 #define LYD_PRINT_WD_ALL_TAG ...
 #define LYD_PRINT_WD_EXPLICIT ...
 #define LYD_PRINT_WD_IMPL_TAG ...
 #define LYD_PRINT_WD_MASK ...
-#define LYD_PRINT_WITHSIBLINGS ...
+#define LYD_PRINT_SIBLINGS ...
 #define LYD_PRINT_WD_TRIM ...
 LY_ERR lyd_print_mem(char **, const struct lyd_node *, LYD_FORMAT, uint32_t);
 LY_ERR lyd_print_tree(struct ly_out *, const struct lyd_node *, LYD_FORMAT, uint32_t);
 LY_ERR lyd_print_all(struct ly_out *, const struct lyd_node *, LYD_FORMAT, uint32_t);
 
-#define LYD_PARSE_LYB_MOD_UPDATE ...
 #define LYD_PARSE_NO_STATE ...
 #define LYD_PARSE_STORE_ONLY ...
 #define LYD_PARSE_JSON_NULL ...
@@ -351,7 +351,7 @@ LY_ERR ly_out_new_file(FILE *, struct ly_out **);
 LY_ERR ly_out_new_fd(int, struct ly_out **);
 
 LY_ERR lyd_parse_data(const struct ly_ctx *, struct lyd_node *, struct ly_in *, LYD_FORMAT, uint32_t, uint32_t, struct lyd_node **);
-LY_ERR lyd_parse_op(const struct ly_ctx *, struct lyd_node *, struct ly_in *, LYD_FORMAT, enum lyd_type, struct lyd_node **, struct lyd_node **);
+LY_ERR lyd_parse_op(const struct ly_ctx *, struct lyd_node *, struct ly_in *, LYD_FORMAT, enum lyd_type, uint32_t, struct lyd_node **, struct lyd_node **);
 
 typedef enum {
    LYS_OUT_UNKNOWN,
@@ -369,6 +369,7 @@ LY_ERR lys_print_module(struct ly_out *, const struct lys_module *, LYS_OUTFORMA
 
 struct lysc_module {
 	struct lys_module *mod;
+	const char **features;
 	struct lysc_node *data;
 	struct lysc_node_action *rpcs;
 	struct lysc_node_notif *notifs;
@@ -388,13 +389,15 @@ struct lys_module {
     const char *ref;
     struct lysp_module *parsed;
     struct lysc_module *compiled;
+    struct lysc_ext *extensions;
     struct lysc_ident *identities;
+    struct lysc_submodule *submodules;
     struct lys_module **augmented_by;
     struct lys_module **deviated_by;
     ly_bool implemented;
     ly_bool to_compile;
-    uint8_t latest_revision;
-    ...;
+    uint8_t version : 2;
+    uint8_t latest_revision : 4;
 };
 
 struct lysp_module {
@@ -467,12 +470,11 @@ struct lysp_ext_instance {
     const char *argument;
     LY_VALUE_FORMAT format;
     void *prefix_data;
-    struct lysp_ext *def;
+    uintptr_t plugin_ref;
     void *parent;
     enum ly_stmt parent_stmt;
     uint64_t parent_stmt_index;
     uint16_t flags;
-    const struct lyplg_ext_record *record;
     struct lysp_ext_substmt *substmts;
     void *parsed;
     struct lysp_stmt *child;
@@ -610,6 +612,11 @@ struct lysp_node_container {
     ...;
 };
 
+struct lysc_value {
+    const char *str;
+    struct lysc_prefix *prefixes;
+};
+
 struct lysc_node_leaf {
     union {
         struct lysc_node node;
@@ -623,7 +630,7 @@ struct lysc_node_leaf {
     struct lysc_when **when;
     struct lysc_type *type;
     const char *units;
-    struct lyd_value *dflt;
+    struct lysc_value dflt;
     ...;
 };
 
@@ -653,7 +660,7 @@ struct lysc_node_leaflist {
     struct lysc_when **when;
     struct lysc_type *type;
     const char *units;
-    struct lyd_value **dflts;
+    struct lysc_value *dflts;
     uint32_t min;
     uint32_t max;
     ...;
@@ -783,7 +790,7 @@ struct lysp_node_augment {
 struct lysc_type {
     const char *name;
     struct lysc_ext_instance *exts;
-    struct lyplg_type *plugin;
+    uintptr_t plugin_ref;
     LY_DATA_TYPE basetype;
     uint32_t refcount;
 };
@@ -859,7 +866,7 @@ struct lysc_ext {
     const char *name;
     const char *argname;
     struct lysc_ext_instance *exts;
-    struct lyplg_ext *plugin;
+    uintptr_t plugin_ref;
     struct lys_module *module;
     uint16_t flags;
 };
@@ -982,7 +989,6 @@ typedef struct pcre2_real_code pcre2_code;
 
 struct lysc_pattern {
     const char *expr;
-    pcre2_code *code;
     const char *dsc;
     const char *ref;
     const char *emsg;
@@ -1017,7 +1023,7 @@ struct lysc_ident {
 struct lysc_type_num {
     const char *name;
     struct lysc_ext_instance *exts;
-    struct lyplg_type *plugin;
+    uintptr_t plugin_ref;
     LY_DATA_TYPE basetype;
     uint32_t refcount;
     struct lysc_range *range;
@@ -1026,7 +1032,7 @@ struct lysc_type_num {
 struct lysc_type_dec {
     const char *name;
     struct lysc_ext_instance *exts;
-    struct lyplg_type *plugin;
+    uintptr_t plugin_ref;
     LY_DATA_TYPE basetype;
     uint32_t refcount;
     uint8_t fraction_digits;
@@ -1036,7 +1042,7 @@ struct lysc_type_dec {
 struct lysc_type_str {
     const char *name;
     struct lysc_ext_instance *exts;
-    struct lyplg_type *plugin;
+    uintptr_t plugin_ref;
     LY_DATA_TYPE basetype;
     uint32_t refcount;
     struct lysc_range *length;
@@ -1058,7 +1064,7 @@ struct lysc_type_bitenum_item {
 struct lysc_type_enum {
     const char *name;
     struct lysc_ext_instance *exts;
-    struct lyplg_type *plugin;
+    uintptr_t plugin_ref;
     LY_DATA_TYPE basetype;
     uint32_t refcount;
     struct lysc_type_bitenum_item *enums;
@@ -1067,7 +1073,7 @@ struct lysc_type_enum {
 struct lysc_type_bits {
     const char *name;
     struct lysc_ext_instance *exts;
-    struct lyplg_type *plugin;
+    uintptr_t plugin_ref;
     LY_DATA_TYPE basetype;
     uint32_t refcount;
     struct lysc_type_bitenum_item *bits;
@@ -1076,7 +1082,7 @@ struct lysc_type_bits {
 struct lysc_type_leafref {
     const char *name;
     struct lysc_ext_instance *exts;
-    struct lyplg_type *plugin;
+    uintptr_t plugin_ref;
     LY_DATA_TYPE basetype;
     uint32_t refcount;
     struct lyxp_expr *path;
@@ -1088,7 +1094,7 @@ struct lysc_type_leafref {
 struct lysc_type_identityref {
     const char *name;
     struct lysc_ext_instance *exts;
-    struct lyplg_type *plugin;
+    uintptr_t plugin_ref;
     LY_DATA_TYPE basetype;
     uint32_t refcount;
     struct lysc_ident **bases;
@@ -1097,7 +1103,7 @@ struct lysc_type_identityref {
 struct lysc_type_instanceid {
     const char *name;
     struct lysc_ext_instance *exts;
-    struct lyplg_type *plugin;
+    uintptr_t plugin_ref;
     LY_DATA_TYPE basetype;
     uint32_t refcount;
     uint8_t require_instance;
@@ -1106,7 +1112,7 @@ struct lysc_type_instanceid {
 struct lysc_type_union {
     const char *name;
     struct lysc_ext_instance *exts;
-    struct lyplg_type *plugin;
+    uintptr_t plugin_ref;
     LY_DATA_TYPE basetype;
     uint32_t refcount;
     struct lysc_type **types;
@@ -1115,7 +1121,7 @@ struct lysc_type_union {
 struct lysc_type_bin {
     const char *name;
     struct lysc_ext_instance *exts;
-    struct lyplg_type *plugin;
+    uintptr_t plugin_ref;
     LY_DATA_TYPE basetype;
     uint32_t refcount;
     struct lysc_range *length;
@@ -1159,8 +1165,7 @@ typedef enum {
    LYD_ANYDATA_DATATREE,
    LYD_ANYDATA_STRING,
    LYD_ANYDATA_XML,
-   LYD_ANYDATA_JSON,
-   LYD_ANYDATA_LYB
+   LYD_ANYDATA_JSON
 } LYD_ANYDATA_VALUETYPE;
 
 union lyd_any_value {
@@ -1168,7 +1173,6 @@ union lyd_any_value {
     const char *str;
     const char *xml;
     const char *json;
-    char *mem;
 };
 
 struct lyd_node_any {
@@ -1320,6 +1324,8 @@ struct lyd_attr {
 LY_ERR lyd_new_attr(struct lyd_node *, const char *, const char *, const char *, struct lyd_attr **);
 void lyd_free_attr_single(const struct ly_ctx *ctx, struct lyd_attr *attr);
 
+LY_ERR lyd_value_validate_dflt(const struct lysc_node *, const char *, struct lysc_prefix *, const struct lyd_node *, const struct lysc_type **, const char **);
+
 struct lyd_leafref_links_rec {
     const struct lyd_node_term *node;
     const struct lyd_node_term **leafref_nodes;
@@ -1328,13 +1334,14 @@ struct lyd_leafref_links_rec {
 
 LY_ERR lyd_leafref_get_links(const struct lyd_node_term *, const struct lyd_leafref_links_rec **);
 LY_ERR lyd_leafref_link_node_tree(struct lyd_node *);
+struct lyplg_ext *lysc_get_ext_plugin(uintptr_t);
 const char *lyplg_ext_stmt2str(enum ly_stmt stmt);
 const struct lysp_module *lyplg_ext_parse_get_cur_pmod(const struct lysp_ctx *);
 struct ly_ctx *lyplg_ext_compile_get_ctx(const struct lysc_ctx *);
 void lyplg_ext_parse_log(const struct lysp_ctx *, const struct lysp_ext_instance *, LY_LOG_LEVEL, LY_ERR, const char *, ...);
 void lyplg_ext_compile_log(const struct lysc_ctx *, const struct lysc_ext_instance *, LY_LOG_LEVEL, LY_ERR, const char *, ...);
 LY_ERR lyplg_ext_parse_extension_instance(struct lysp_ctx *, struct lysp_ext_instance *);
-LY_ERR lyplg_ext_compile_extension_instance(struct lysc_ctx *, const struct lysp_ext_instance *, struct lysc_ext_instance *);
+LY_ERR lyplg_ext_compile_extension_instance(struct lysc_ctx *, const struct lysp_ext_instance *, struct lysc_ext_instance *, struct lysc_node *);
 void lyplg_ext_pfree_instance_substatements(const struct ly_ctx *ctx, struct lysp_ext_substmt *substmts);
 void lyplg_ext_cfree_instance_substatements(const struct ly_ctx *ctx, struct lysc_ext_substmt *substmts);
 typedef LY_ERR (*lyplg_ext_parse_clb)(struct lysp_ctx *, struct lysp_ext_instance *);
diff --git a/cffi/source.c b/cffi/source.c
index 34e18b0..3c76e98 100644
--- a/cffi/source.c
+++ b/cffi/source.c
@@ -6,6 +6,6 @@
 #include <libyang/libyang.h>
 #include <libyang/version.h>
 
-#if LY_VERSION_MAJOR * 10000 + LY_VERSION_MINOR * 100 + LY_VERSION_MICRO < 30801
-#error "This version of libyang bindings only works with libyang soversion 3.8.1+"
+#if LY_VERSION_MAJOR * 10000 + LY_VERSION_MINOR * 100 + LY_VERSION_MICRO < 40202
+#error "This version of libyang bindings only works with libyang soversion 4.2.2+"
 #endif
diff --git a/libyang/context.py b/libyang/context.py
index 4489680..21db162 100644
--- a/libyang/context.py
+++ b/libyang/context.py
@@ -205,6 +205,7 @@ def __init__(
         builtin_plugins_only: bool = False,
         all_implemented: bool = False,
         enable_imp_features: bool = False,
+        compile_obsolete: bool = False,
         yanglib_path: Optional[str] = None,
         yanglib_fmt: str = "json",
         cdata=None,  # C type: "struct ly_ctx *"
@@ -231,6 +232,8 @@ def __init__(
             options |= lib.LY_CTX_ALL_IMPLEMENTED
         if enable_imp_features:
             options |= lib.LY_CTX_ENABLE_IMP_FEATURES
+        if compile_obsolete:
+            options |= lib.LY_CTX_COMPILE_OBSOLETE
         # force priv parsed
         options |= lib.LY_CTX_SET_PRIV_PARSED
 
@@ -259,6 +262,7 @@ def __init__(
                 fmt = lib.LYD_JSON
             else:
                 fmt = lib.LYD_XML
+            print("steweg", search_path, yanglib_path, yanglib_fmt, options)
             ret = lib.ly_ctx_new_ylpath(
                 str2c(search_path), str2c(yanglib_path), fmt, options, ctx
             )
@@ -542,6 +546,8 @@ def parse_op(
         in_data: Union[IO, str],
         dtype: DataType,
         parent: DNode = None,
+        opaq: bool = False,
+        strict: bool = False,
     ) -> DNode:
         fmt = data_format(fmt)
         data = ffi.new("struct ly_in **")
@@ -551,13 +557,14 @@ def parse_op(
         if ret != lib.LY_SUCCESS:
             raise self.error("failed to read input data")
 
+        flags = parser_flags(opaq=opaq, strict=strict)
         tree = ffi.new("struct lyd_node **", ffi.NULL)
         op = ffi.new("struct lyd_node **", ffi.NULL)
         par = ffi.new("struct lyd_node **", ffi.NULL)
         if parent is not None:
             par[0] = parent.cdata
 
-        ret = lib.lyd_parse_op(self.cdata, par[0], data[0], fmt, dtype, tree, op)
+        ret = lib.lyd_parse_op(self.cdata, par[0], data[0], fmt, dtype, flags, tree, op)
         lib.ly_in_free(data[0], 0)
         if ret != lib.LY_SUCCESS:
             raise self.error("failed to parse input data")
@@ -570,9 +577,17 @@ def parse_op_mem(
         data: str,
         dtype: DataType = DataType.DATA_YANG,
         parent: DNode = None,
+        opaq: bool = False,
+        strict: bool = False,
     ):
         return self.parse_op(
-            fmt, in_type=IOType.MEMORY, in_data=data, dtype=dtype, parent=parent
+            fmt,
+            in_type=IOType.MEMORY,
+            in_data=data,
+            dtype=dtype,
+            parent=parent,
+            opaq=opaq,
+            strict=strict,
         )
 
     def parse_data(
@@ -581,7 +596,6 @@ def parse_data(
         in_type: IOType,
         in_data: Union[str, bytes, IO],
         parent: DNode = None,
-        lyb_mod_update: bool = False,
         no_state: bool = False,
         parse_only: bool = False,
         opaq: bool = False,
@@ -596,7 +610,6 @@ def parse_data(
         if self.cdata is None:
             raise RuntimeError("context already destroyed")
         parser_flgs = parser_flags(
-            lyb_mod_update=lyb_mod_update,
             no_state=no_state,
             parse_only=parse_only,
             opaq=opaq,
@@ -640,7 +653,6 @@ def parse_data_mem(
         data: Union[str, bytes],
         fmt: str,
         parent: DNode = None,
-        lyb_mod_update: bool = False,
         no_state: bool = False,
         parse_only: bool = False,
         opaq: bool = False,
@@ -657,7 +669,6 @@ def parse_data_mem(
             in_type=IOType.MEMORY,
             in_data=data,
             parent=parent,
-            lyb_mod_update=lyb_mod_update,
             no_state=no_state,
             parse_only=parse_only,
             opaq=opaq,
@@ -675,7 +686,6 @@ def parse_data_file(
         fileobj: IO,
         fmt: str,
         parent: DNode = None,
-        lyb_mod_update: bool = False,
         no_state: bool = False,
         parse_only: bool = False,
         opaq: bool = False,
@@ -692,7 +702,6 @@ def parse_data_file(
             in_type=IOType.FD,
             in_data=fileobj,
             parent=parent,
-            lyb_mod_update=lyb_mod_update,
             no_state=no_state,
             parse_only=parse_only,
             opaq=opaq,
diff --git a/libyang/data.py b/libyang/data.py
index 7a5887b..230c51a 100644
--- a/libyang/data.py
+++ b/libyang/data.py
@@ -53,11 +53,11 @@ def printer_flags(
 ) -> int:
     flags = 0
     if with_siblings:
-        flags |= lib.LYD_PRINT_WITHSIBLINGS
+        flags |= lib.LYD_PRINT_SIBLINGS
     if not pretty:
         flags |= lib.LYD_PRINT_SHRINK
     if keep_empty_containers:
-        flags |= lib.LYD_PRINT_KEEPEMPTYCONT
+        flags |= lib.LYD_PRINT_EMPTY_CONT
     if trim_default_values:
         flags |= lib.LYD_PRINT_WD_TRIM
     if include_implicit_defaults:
@@ -109,7 +109,6 @@ def newval_flags(
 
 # -------------------------------------------------------------------------------------
 def parser_flags(
-    lyb_mod_update: bool = False,
     no_state: bool = False,
     parse_only: bool = False,
     opaq: bool = False,
@@ -120,8 +119,6 @@ def parser_flags(
     json_string_datatypes: bool = False,
 ) -> int:
     flags = 0
-    if lyb_mod_update:
-        flags |= lib.LYD_PARSE_LYB_MOD_UPDATE
     if no_state:
         flags |= lib.LYD_PARSE_NO_STATE
     if parse_only:
diff --git a/libyang/extension.py b/libyang/extension.py
index 57f7cb2..f7e4ba9 100644
--- a/libyang/extension.py
+++ b/libyang/extension.py
@@ -8,7 +8,7 @@
 from _libyang import ffi, lib
 from .context import Context
 from .log import get_libyang_level
-from .schema import ExtensionCompiled, ExtensionParsed, Module
+from .schema import ExtensionCompiled, ExtensionParsed, Module, SNode
 from .util import LibyangError, c2str, str2c
 
 
@@ -25,7 +25,7 @@ def __init__(self, message: str, ret: int, log_level: int) -> None:
 
 @ffi.def_extern(name="lypy_lyplg_ext_parse_clb")
 def libyang_c_lyplg_ext_parse_clb(pctx, pext):
-    plugin = extensions_plugins[pext.record.plugin]
+    plugin = extensions_plugins[lib.lysc_get_ext_plugin(pext.plugin_ref)]
     module_cdata = lib.lyplg_ext_parse_get_cur_pmod(pctx).mod
     context = Context(cdata=module_cdata.ctx)
     module = Module(context, module_cdata)
@@ -46,7 +46,7 @@ def libyang_c_lyplg_ext_parse_clb(pctx, pext):
 
 @ffi.def_extern(name="lypy_lyplg_ext_compile_clb")
 def libyang_c_lyplg_ext_compile_clb(cctx, pext, cext):
-    plugin = extensions_plugins[pext.record.plugin]
+    plugin = extensions_plugins[lib.lysc_get_ext_plugin(pext.plugin_ref)]
     context = Context(cdata=lib.lyplg_ext_compile_get_ctx(cctx))
     module = Module(context, cext.module)
     parsed_ext = ExtensionParsed(context, pext, module)
@@ -67,7 +67,7 @@ def libyang_c_lyplg_ext_compile_clb(cctx, pext, cext):
 
 @ffi.def_extern(name="lypy_lyplg_ext_parse_free_clb")
 def libyang_c_lyplg_ext_parse_free_clb(ctx, pext):
-    plugin = extensions_plugins[pext.record.plugin]
+    plugin = extensions_plugins[lib.lysc_get_ext_plugin(pext.plugin_ref)]
     context = Context(cdata=ctx)
     parsed_ext = ExtensionParsed(context, pext, None)
     plugin.parse_free_clb(parsed_ext)
@@ -75,7 +75,9 @@ def libyang_c_lyplg_ext_parse_free_clb(ctx, pext):
 
 @ffi.def_extern(name="lypy_lyplg_ext_compile_free_clb")
 def libyang_c_lyplg_ext_compile_free_clb(ctx, cext):
-    plugin = extensions_plugins[getattr(cext, "def").plugin]
+    plugin = extensions_plugins[
+        lib.lysc_get_ext_plugin(getattr(cext, "def").plugin_ref)
+    ]
     context = Context(cdata=ctx)
     compiled_ext = ExtensionCompiled(context, cext)
     plugin.compile_free_clb(compiled_ext)
@@ -200,9 +202,17 @@ def set_compile_ctx(self, cctx) -> None:
     def parse_substmts(self, ext: ExtensionParsed) -> int:
         return lib.lyplg_ext_parse_extension_instance(self._pctx, ext.cdata)
 
-    def compile_substmts(self, pext: ExtensionParsed, cext: ExtensionCompiled) -> int:
+    def compile_substmts(
+        self,
+        pext: ExtensionParsed,
+        cext: ExtensionCompiled,
+        parent: Optional[SNode] = None,
+    ) -> int:
         return lib.lyplg_ext_compile_extension_instance(
-            self._cctx, pext.cdata, cext.cdata
+            self._cctx,
+            pext.cdata,
+            cext.cdata,
+            ffi.NULL if parent is None else parent.cdata,
         )
 
     def free_parse_substmts(self, ext: ExtensionParsed) -> None:
diff --git a/libyang/schema.py b/libyang/schema.py
index 23c1b1b..f104a87 100644
--- a/libyang/schema.py
+++ b/libyang/schema.py
@@ -1522,21 +1522,35 @@ def new(context: "libyang.Context", cdata) -> "SNode":
 # -------------------------------------------------------------------------------------
 @SNode.register(SNode.LEAF)
 class SLeaf(SNode):
-    __slots__ = ("cdata_leaf", "cdata_leaf_parsed")
+    __slots__ = ("cdata_leaf", "cdata_leaf_parsed", "cdata_default_realtype")
 
     def __init__(self, context: "libyang.Context", cdata):
         super().__init__(context, cdata)
         self.cdata_leaf = ffi.cast("struct lysc_node_leaf *", cdata)
         self.cdata_leaf_parsed = ffi.cast("struct lysp_node_leaf *", self.cdata_parsed)
+        self.cdata_default_realtype = None
 
     def default(self) -> Union[None, bool, int, str, float]:
-        if not self.cdata_leaf.dflt:
+        if not self.cdata_leaf.dflt.str:
             return None
-        val = lib.lyd_value_get_canonical(self.context.cdata, self.cdata_leaf.dflt)
-        if not val:
-            return None
-        val = c2str(val)
-        val_type = Type(self.context, self.cdata_leaf.dflt.realtype, None)
+
+        if self.cdata_default_realtype is None:
+            # calculate real type of default value just once
+            val_type_cdata = ffi.new("struct lysc_type **", ffi.NULL)
+            ret = lib.lyd_value_validate_dflt(
+                self.cdata,
+                self.cdata_leaf.dflt.str,
+                self.cdata_leaf.dflt.prefixes,
+                ffi.NULL,
+                val_type_cdata,
+                ffi.NULL,
+            )
+            if ret != lib.LY_SUCCESS:
+                raise self.context.error("Unable to get real type of default value")
+            self.cdata_default_realtype = Type(self.context, val_type_cdata[0], None)
+
+        val = c2str(self.cdata_leaf.dflt.str)
+        val_type = self.cdata_default_realtype
         if val_type.base() == Type.BOOL:
             return val == "true"
         if val_type.base() in Type.NUM_TYPES:
@@ -1563,7 +1577,7 @@ def __str__(self):
 # -------------------------------------------------------------------------------------
 @SNode.register(SNode.LEAFLIST)
 class SLeafList(SNode):
-    __slots__ = ("cdata_leaflist", "cdata_leaflist_parsed")
+    __slots__ = ("cdata_leaflist", "cdata_leaflist_parsed", "cdata_default_realtypes")
 
     def __init__(self, context: "libyang.Context", cdata):
         super().__init__(context, cdata)
@@ -1571,6 +1585,7 @@ def __init__(self, context: "libyang.Context", cdata):
         self.cdata_leaflist_parsed = ffi.cast(
             "struct lysp_node_leaflist *", self.cdata_parsed
         )
+        self.cdata_default_realtypes = None
 
     def ordered(self) -> bool:
         return bool(self.cdata_parsed.flags & lib.LYS_ORDBY_USER)
@@ -1586,12 +1601,37 @@ def type(self) -> Type:
     def defaults(self) -> Iterator[Union[None, bool, int, str, float]]:
         if self.cdata_leaflist.dflts == ffi.NULL:
             return
-        for dflt in ly_array_iter(self.cdata_leaflist.dflts):
-            val = lib.lyd_value_get_canonical(self.context.cdata, dflt)
-            if not val:
+
+        if self.cdata_default_realtypes is None:
+            # calculate real types of default values just once
+            val_type_cdata = ffi.new("struct lysc_type **", ffi.NULL)
+            self.cdata_default_realtypes = []
+            for dflt in ly_array_iter(self.cdata_leaflist.dflts):
+                if not dflt.str:
+                    self.cdata_default_realtypes.append(None)
+                    continue
+                val_type_cdata[0] = ffi.NULL
+                ret = lib.lyd_value_validate_dflt(
+                    self.cdata,
+                    dflt.str,
+                    dflt.prefixes,
+                    ffi.NULL,
+                    val_type_cdata,
+                    ffi.NULL,
+                )
+                if ret != lib.LY_SUCCESS:
+                    raise self.context.error("Unable to get real type of default value")
+                self.cdata_default_realtypes.append(
+                    Type(self.context, val_type_cdata[0], None)
+                )
+
+        for dflt, val_type in zip(
+            ly_array_iter(self.cdata_leaflist.dflts), self.cdata_default_realtypes
+        ):
+            if not dflt.str:
                 yield None
-            val = c2str(val)
-            val_type = Type(self.context, dflt.realtype, None)
+                continue
+            val = c2str(dflt.str)
             if val_type.base() == Type.BOOL:
                 yield val == "true"
             elif val_type.base() in Type.NUM_TYPES:
diff --git a/tests/test_context.py b/tests/test_context.py
index 61c0ae8..8ffd045 100644
--- a/tests/test_context.py
+++ b/tests/test_context.py
@@ -20,7 +20,11 @@ def test_ctx_no_dir(self):
             self.assertIsNot(ctx, None)
 
     def test_ctx_yanglib(self):
-        ctx = Context(YANG_DIR, yanglib_path=YANG_DIR + "/yang-library.json")
+        ctx = Context(
+            YANG_DIR,
+            yanglib_path=YANG_DIR + "/yang-library.json",
+            compile_obsolete=True,
+        )
         ctx.load_module("yolo-system")
         dnode = ctx.get_yanglib_data()
         j = dnode.print_mem("json", with_siblings=True)
diff --git a/tests/test_data.py b/tests/test_data.py
index 430873a..8f56dc1 100644
--- a/tests/test_data.py
+++ b/tests/test_data.py
@@ -33,7 +33,7 @@
 # -------------------------------------------------------------------------------------
 class DataTest(unittest.TestCase):
     def setUp(self):
-        self.ctx = Context(YANG_DIR)
+        self.ctx = Context(YANG_DIR, compile_obsolete=True)
         modules = [
             self.ctx.load_module("ietf-netconf"),
             self.ctx.load_module("yolo-system"),
@@ -51,18 +51,18 @@ def tearDown(self):
   "yolo-system:conf": {
     "hostname": "foo",
     "url": [
-      {
-        "proto": "https",
-        "host": "github.com",
-        "path": "/CESNET/libyang-python",
-        "enabled": false
-      },
       {
         "proto": "http",
         "host": "foobar.com",
         "port": 8080,
         "path": "/index.html",
         "enabled": true
+      },
+      {
+        "proto": "https",
+        "host": "github.com",
+        "path": "/CESNET/libyang-python",
+        "enabled": false
       }
     ],
     "number": [
@@ -76,7 +76,9 @@ def tearDown(self):
 """
 
     def test_data_parse_config_json(self):
-        dnode = self.ctx.parse_data_mem(self.JSON_CONFIG, "json", no_state=True)
+        dnode = self.ctx.parse_data_mem(
+            self.JSON_CONFIG, "json", no_state=True, ordered=True
+        )
         self.assertIsInstance(dnode, DContainer)
         try:
             j = dnode.print_mem("json", with_siblings=True)
@@ -91,18 +93,18 @@ def test_data_parse_config_json(self):
   "yolo-system:conf": {
     "hostname": "foo",
     "url": [
-      {
-        "proto": "https",
-        "host": "github.com",
-        "path": "/CESNET/libyang-python",
-        "enabled": false
-      },
       {
         "proto": "http",
         "host": "foobar.com",
         "port": 8080,
         "path": "/index.html",
         "enabled": true
+      },
+      {
+        "proto": "https",
+        "host": "github.com",
+        "path": "/CESNET/libyang-python",
+        "enabled": false
       }
     ],
     "number": [
@@ -128,12 +130,6 @@ def test_data_parse_config_json_without_yang_lib(self):
   "yolo-system:conf": {
     "hostname": "foo",
     "url": [
-      {
-        "proto": "https",
-        "host": "github.com",
-        "path": "/CESNET/libyang-python",
-        "enabled": false
-      },
       {
         "proto": "http",
         "host": "barfoo.com",
@@ -145,6 +141,12 @@ def test_data_parse_config_json_without_yang_lib(self):
         "port": 8080,
         "path": "/index.html",
         "enabled": true
+      },
+      {
+        "proto": "https",
+        "host": "github.com",
+        "path": "/CESNET/libyang-python",
+        "enabled": false
       }
     ],
     "number": [
@@ -254,7 +256,9 @@ def test_data_parse_state_json(self):
 """
 
     def test_data_parse_config_xml(self):
-        dnode = self.ctx.parse_data_mem(self.XML_CONFIG, "xml", validate_present=True)
+        dnode = self.ctx.parse_data_mem(
+            self.XML_CONFIG, "xml", validate_present=True, ordered=True
+        )
         self.assertIsInstance(dnode, DContainer)
         try:
             xml = dnode.print_mem("xml", with_siblings=True, trim_default_values=True)
@@ -509,6 +513,7 @@ def test_data_from_dict_module_free_func(self):
     }
 
     def test_data_from_dict_module_with_prefix(self):
+        self.maxDiff = None
         module = self.ctx.get_module("yolo-system")
         dnode = module.parse_data_dict(
             self.DICT_CONFIG_WITH_PREFIX, strict=True, validate_present=True
@@ -853,17 +858,17 @@ def test_data_diff(self):
     TREE = [
         "/yolo-system:conf",
         "/yolo-system:conf/hostname",
-        "/yolo-system:conf/url[proto='https'][host='github.com']",
-        "/yolo-system:conf/url[proto='https'][host='github.com']/proto",
-        "/yolo-system:conf/url[proto='https'][host='github.com']/host",
-        "/yolo-system:conf/url[proto='https'][host='github.com']/path",
-        "/yolo-system:conf/url[proto='https'][host='github.com']/enabled",
         "/yolo-system:conf/url[proto='http'][host='foobar.com']",
         "/yolo-system:conf/url[proto='http'][host='foobar.com']/proto",
         "/yolo-system:conf/url[proto='http'][host='foobar.com']/host",
         "/yolo-system:conf/url[proto='http'][host='foobar.com']/port",
         "/yolo-system:conf/url[proto='http'][host='foobar.com']/path",
         "/yolo-system:conf/url[proto='http'][host='foobar.com']/enabled",
+        "/yolo-system:conf/url[proto='https'][host='github.com']",
+        "/yolo-system:conf/url[proto='https'][host='github.com']/proto",
+        "/yolo-system:conf/url[proto='https'][host='github.com']/host",
+        "/yolo-system:conf/url[proto='https'][host='github.com']/path",
+        "/yolo-system:conf/url[proto='https'][host='github.com']/enabled",
         "/yolo-system:conf/number[.='1000']",
         "/yolo-system:conf/number[.='2000']",
         "/yolo-system:conf/number[.='3000']",
@@ -906,7 +911,7 @@ def test_find_all(self):
                     }
                 ]
             }
-            self.assertEqual(urls[0].print_dict(absolute=False), expected_url)
+            self.assertEqual(urls[1].print_dict(absolute=False), expected_url)
         finally:
             dnode.free()
 
@@ -1090,7 +1095,9 @@ def test_dnode_leafref_linking(self):
             "yolo-leafref-extended:ref1": "val1"
             }"""
         self.ctx.destroy()
-        self.ctx = Context(YANG_DIR, leafref_extended=True, leafref_linking=True)
+        self.ctx = Context(
+            YANG_DIR, leafref_extended=True, leafref_linking=True, compile_obsolete=True
+        )
         mod = self.ctx.load_module("yolo-leafref-extended")
         self.assertIsInstance(mod, Module)
         dnode1 = self.ctx.parse_data_mem(MAIN, "json", parse_only=True)
@@ -1121,7 +1128,7 @@ def test_dnode_builtin_plugins_only(self):
         MAIN = {"yolo-nodetypes:ip-address": "test"}
         self.tearDown()
         gc.collect()
-        self.ctx = Context(YANG_DIR, builtin_plugins_only=True)
+        self.ctx = Context(YANG_DIR, builtin_plugins_only=True, compile_obsolete=True)
         module = self.ctx.load_module("yolo-nodetypes")
         dnode = dict_to_dnode(MAIN, module, None, validate=False, store_only=True)
         self.assertIsInstance(dnode, DLeaf)
@@ -1140,7 +1147,7 @@ def test_merge_builtin_plugins_only(self):
         MAIN = {"yolo-nodetypes:ip-address": "test"}
         self.tearDown()
         gc.collect()
-        self.ctx = Context(YANG_DIR, builtin_plugins_only=True)
+        self.ctx = Context(YANG_DIR, builtin_plugins_only=True, compile_obsolete=True)
         module = self.ctx.load_module("yolo-nodetypes")
         dnode = module.parse_data_dict(MAIN, validate=False, store_only=True)
         self.assertIsInstance(dnode, DLeaf)
diff --git a/tests/test_diff.py b/tests/test_diff.py
index d4b7e87..2263e34 100644
--- a/tests/test_diff.py
+++ b/tests/test_diff.py
@@ -97,7 +97,9 @@ class DiffTest(unittest.TestCase):
     )
 
     def test_diff(self):
-        with Context(OLD_YANG_DIR) as ctx_old, Context(NEW_YANG_DIR) as ctx_new:
+        with Context(OLD_YANG_DIR, compile_obsolete=True) as ctx_old, Context(
+            NEW_YANG_DIR, compile_obsolete=True
+        ) as ctx_new:
             mod = ctx_old.load_module("yolo-system")
             mod.feature_enable_all()
             mod = ctx_new.load_module("yolo-system")
diff --git a/tests/test_schema.py b/tests/test_schema.py
index b1862ab..a1ca412 100644
--- a/tests/test_schema.py
+++ b/tests/test_schema.py
@@ -272,7 +272,7 @@ def test_iffeature_dump(self):
 # -------------------------------------------------------------------------------------
 class ContainerTest(unittest.TestCase):
     def setUp(self):
-        self.ctx = Context(YANG_DIR)
+        self.ctx = Context(YANG_DIR, compile_obsolete=True)
         mod = self.ctx.load_module("yolo-system")
         mod.feature_enable_all()
         self.container = next(self.ctx.find_path("/yolo-system:conf"))
@@ -549,7 +549,7 @@ def test_rpc_parsed(self):
 # -------------------------------------------------------------------------------------
 class LeafTypeTest(unittest.TestCase):
     def setUp(self):
-        self.ctx = Context(YANG_DIR)
+        self.ctx = Context(YANG_DIR, compile_obsolete=True)
         self.ctx.load_module("yolo-system")
 
     def tearDown(self):
