Skip to content
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
42 changes: 31 additions & 11 deletions libyang/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,15 @@
validation_flags,
)
from .schema import Module, SNode, schema_in_format
from .util import DataType, IOType, LibyangError, c2str, data_load, str2c
from .util import (
DataType,
IOType,
LibyangError,
LibyangErrorItem,
c2str,
data_load,
str2c,
)


# -------------------------------------------------------------------------------------
Expand Down Expand Up @@ -284,23 +292,35 @@ def __exit__(self, *args, **kwargs):
self.destroy()

def error(self, msg: str, *args) -> LibyangError:
msg %= args
if args:
msg = msg % args

parts = [msg]
errors = []

if self.cdata:
err = lib.ly_err_first(self.cdata)
while err:
if err.msg:
msg += ": %s" % c2str(err.msg)
if err.data_path:
msg += ": Data path: %s" % c2str(err.data_path)
if err.schema_path:
msg += ": Schema path: %s" % c2str(err.schema_path)
if err.line != 0:
msg += " (line %u)" % err.line
m = c2str(err.msg) if err.msg else None
dp = c2str(err.data_path) if err.data_path else None
sp = c2str(err.schema_path) if err.schema_path else None
ln = int(err.line) if err.line else None
parts.extend(
tmpl.format(val)
for val, tmpl in [
(m, ": {}"),
(dp, ": Data path: {}"),
(sp, ": Schema path: {}"),
(ln, " (line {})"),
]
if val is not None
)
errors.append(LibyangErrorItem(m, dp, sp, ln))
err = err.next
lib.ly_err_clean(self.cdata, ffi.NULL)

return LibyangError(msg)
msg = "".join(parts)
return LibyangError(msg, errors=errors)

def parse_module(
self,
Expand Down
22 changes: 20 additions & 2 deletions libyang/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,34 @@
# Copyright (c) 2021 RACOM s.r.o.
# SPDX-License-Identifier: MIT

from dataclasses import dataclass
import enum
from typing import Optional
from typing import Iterable, Optional
import warnings

from _libyang import ffi, lib


# -------------------------------------------------------------------------------------
@dataclass(frozen=True)
class LibyangErrorItem:
msg: Optional[str]
data_path: Optional[str]
schema_path: Optional[str]
line: Optional[int]


# -------------------------------------------------------------------------------------
class LibyangError(Exception):
pass
def __init__(
self, message: str, *args, errors: Optional[Iterable[LibyangErrorItem]] = None
):
super().__init__(message, *args)
self.message = message
self.errors = tuple(errors or ())

def __str__(self):
return self.message


# -------------------------------------------------------------------------------------
Expand Down
12 changes: 12 additions & 0 deletions tests/test_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,18 @@ def test_data_parse_config_xml_multi_error(self):
"Data path: /yolo-system:conf/url[proto='https'] (line 7)",
)

first = cm.exception.errors[0]
self.assertEqual(first.msg, 'Invalid boolean value "abcd".')
self.assertEqual(
first.data_path, "/yolo-system:conf/url[proto='https']/enabled"
)
self.assertEqual(first.line, 6)

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

XML_STATE = """<state xmlns="urn:yang:yolo:system">
<hostname>foo</hostname>
<url>
Expand Down