Skip to content

Commit 9927dfa

Browse files
committed
schema: adds Identity and PIdentity classes
This patch introduces Identity and PIdentity classes. It also adds identities() API to get list of identities from Module Signed-off-by: Stefan Gula <[email protected]>
1 parent 7c451a5 commit 9927dfa

File tree

5 files changed

+249
-14
lines changed

5 files changed

+249
-14
lines changed

cffi/cdefs.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,16 @@ struct lysp_ext_instance {
470470
char rev[LY_REV_SIZE];
471471
};
472472

473+
struct lysp_ident {
474+
const char *name;
475+
struct lysp_qname *iffeatures;
476+
const char **bases;
477+
const char *dsc;
478+
const char *ref;
479+
struct lysp_ext_instance *exts;
480+
uint16_t flags;
481+
};
482+
473483
struct lysp_feature {
474484
const char *name;
475485
struct lysp_qname *iffeatures;
@@ -939,6 +949,16 @@ struct lysp_restr {
939949
struct lysp_ext_instance *exts;
940950
};
941951

952+
struct lysc_ident {
953+
const char *name;
954+
const char *dsc;
955+
const char *ref;
956+
struct lys_module *module;
957+
struct lysc_ident **derived;
958+
struct lysc_ext_instance *exts;
959+
uint16_t flags;
960+
};
961+
942962
struct lysc_type_num {
943963
const char *name;
944964
struct lysc_ext_instance *exts;

libyang/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171
ExtensionCompiled,
7272
ExtensionParsed,
7373
Feature,
74+
Identity,
7475
IfAndFeatures,
7576
IfFeature,
7677
IfFeatureExpr,
@@ -89,6 +90,7 @@
8990
PContainer,
9091
PEnum,
9192
PGrouping,
93+
PIdentity,
9294
PLeaf,
9395
PLeafList,
9496
PList,
@@ -150,6 +152,7 @@
150152
"ExtensionPlugin",
151153
"ExtensionRemoved",
152154
"Feature",
155+
"Identity",
153156
"IfAndFeatures",
154157
"IfFeature",
155158
"IfFeatureExpr",
@@ -183,6 +186,7 @@
183186
"PContainer",
184187
"PEnum",
185188
"PGrouping",
189+
"PIdentity",
186190
"PLeaf",
187191
"PLeafList",
188192
"PList",

libyang/schema.py

Lines changed: 150 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,16 @@ def notifications(self) -> Iterator["PNotif"]:
184184
for n in ly_list_iter(self.cdata.parsed.notifs):
185185
yield PNotif(self.context, n, self)
186186

187+
def identities(
188+
self, parsed: bool = False
189+
) -> Union[Iterator["Identity"], Iterator["PIdentity"]]:
190+
if parsed:
191+
for i in ly_array_iter(self.cdata.parsed.identities):
192+
yield PIdentity(self.context, i, self)
193+
else:
194+
for i in ly_array_iter(self.cdata.identities):
195+
yield Identity(self.context, i)
196+
187197
def __str__(self) -> str:
188198
return self.name()
189199

@@ -423,13 +433,16 @@ def name(self) -> str:
423433
def module(self) -> Module:
424434
return self._module_from_parsed()
425435

426-
def parent_node(self) -> Optional["PNode"]:
427-
if not bool(self.cdata.parent_stmt & lib.LY_STMT_NODE_MASK):
428-
return None
429-
try:
430-
return PNode.new(self.context, self.cdata.parent, self.module_parent)
431-
except LibyangError:
432-
return None
436+
def parent_node(self) -> Optional[Union["PNode", "PIdentity"]]:
437+
if self.cdata.parent_stmt == lib.LY_STMT_IDENTITY:
438+
cdata = ffi.cast("struct lysp_ident *", self.cdata.parent)
439+
return PIdentity(self.context, cdata, self.module_parent)
440+
if bool(self.cdata.parent_stmt & lib.LY_STMT_NODE_MASK):
441+
try:
442+
return PNode.new(self.context, self.cdata.parent, self.module_parent)
443+
except LibyangError:
444+
return None
445+
return None
433446

434447

435448
# -------------------------------------------------------------------------------------
@@ -448,13 +461,16 @@ def module(self) -> Module:
448461
raise self.context.error("cannot get module")
449462
return Module(self.context, self.cdata_def.module)
450463

451-
def parent_node(self) -> Optional["SNode"]:
452-
if not bool(self.cdata.parent_stmt & lib.LY_STMT_NODE_MASK):
453-
return None
454-
try:
455-
return SNode.new(self.context, self.cdata.parent)
456-
except LibyangError:
457-
return None
464+
def parent_node(self) -> Optional[Union["SNode", "Identity"]]:
465+
if self.cdata.parent_stmt == lib.LY_STMT_IDENTITY:
466+
cdata = ffi.cast("struct lysc_ident *", self.cdata.parent)
467+
return Identity(self.context, cdata)
468+
if bool(self.cdata.parent_stmt & lib.LY_STMT_NODE_MASK):
469+
try:
470+
return SNode.new(self.context, self.cdata.parent)
471+
except LibyangError:
472+
return None
473+
return None
458474

459475

460476
# -------------------------------------------------------------------------------------
@@ -632,6 +648,13 @@ def leafref_path(self) -> Optional["str"]:
632648
lr = ffi.cast("struct lysc_type_leafref *", self.cdata)
633649
return c2str(lib.lyxp_get_expr(lr.path))
634650

651+
def identity_bases(self) -> Iterator["Identity"]:
652+
if self.cdata.basetype != lib.LY_TYPE_IDENT:
653+
return
654+
ident = ffi.cast("struct lysc_type_identityref *", self.cdata)
655+
for b in ly_array_iter(ident.bases):
656+
yield Identity(self.context, b)
657+
635658
def typedef(self) -> "Typedef":
636659
if ":" in self.name():
637660
module_prefix, type_name = self.name().split(":")
@@ -878,6 +901,68 @@ def __str__(self):
878901
return self.name()
879902

880903

904+
# -------------------------------------------------------------------------------------
905+
class Identity:
906+
__slots__ = ("context", "cdata")
907+
908+
def __init__(self, context: "libyang.Context", cdata):
909+
self.context = context
910+
self.cdata = cdata # C type: "struct lysc_ident *"
911+
912+
def name(self) -> str:
913+
return c2str(self.cdata.name)
914+
915+
def description(self) -> Optional[str]:
916+
return c2str(self.cdata.dsc)
917+
918+
def reference(self) -> Optional[str]:
919+
return c2str(self.cdata.ref)
920+
921+
def module(self) -> Module:
922+
return Module(self.context, self.cdata.module)
923+
924+
def derived(self) -> Iterator["Identity"]:
925+
for i in ly_array_iter(self.cdata.derived):
926+
yield Identity(self.context, i)
927+
928+
def extensions(self) -> Iterator[ExtensionCompiled]:
929+
for ext in ly_array_iter(self.cdata.exts):
930+
yield ExtensionCompiled(self.context, ext)
931+
932+
def get_extension(
933+
self, name: str, prefix: Optional[str] = None, arg_value: Optional[str] = None
934+
) -> Optional[ExtensionCompiled]:
935+
for ext in self.extensions():
936+
if ext.name() != name:
937+
continue
938+
if prefix is not None and ext.module().name() != prefix:
939+
continue
940+
if arg_value is not None and ext.argument() != arg_value:
941+
continue
942+
return ext
943+
return None
944+
945+
def deprecated(self) -> bool:
946+
return bool(self.cdata.flags & lib.LYS_STATUS_DEPRC)
947+
948+
def obsolete(self) -> bool:
949+
return bool(self.cdata.flags & lib.LYS_STATUS_OBSLT)
950+
951+
def status(self) -> str:
952+
if self.cdata.flags & lib.LYS_STATUS_OBSLT:
953+
return "obsolete"
954+
if self.cdata.flags & lib.LYS_STATUS_DEPRC:
955+
return "deprecated"
956+
return "current"
957+
958+
def __repr__(self):
959+
cls = self.__class__
960+
return "<%s.%s: %s>" % (cls.__module__, cls.__name__, str(self))
961+
962+
def __str__(self):
963+
return self.name()
964+
965+
881966
# -------------------------------------------------------------------------------------
882967
class Feature:
883968
__slots__ = ("context", "cdata", "__dict__")
@@ -1899,6 +1984,57 @@ def extensions(self) -> Iterator["ExtensionParsed"]:
18991984
yield ExtensionParsed(self.context, ext, self.module)
19001985

19011986

1987+
# -------------------------------------------------------------------------------------
1988+
class PIdentity:
1989+
__slots__ = ("context", "cdata", "module")
1990+
1991+
def __init__(self, context: "libyang.Context", cdata, module: Module) -> None:
1992+
self.context = context
1993+
self.cdata = cdata # C type: "struct lysp_ident *"
1994+
self.module = module
1995+
1996+
def name(self) -> str:
1997+
return c2str(self.cdata.name)
1998+
1999+
def if_features(self) -> Iterator[IfFeatureExpr]:
2000+
for f in ly_array_iter(self.cdata.iffeatures):
2001+
yield IfFeatureExpr(self.context, f, list(self.module.features()))
2002+
2003+
def bases(self) -> Iterator[str]:
2004+
for b in ly_array_iter(self.cdata.bases):
2005+
yield c2str(b)
2006+
2007+
def description(self) -> Optional[str]:
2008+
return c2str(self.cdata.dsc)
2009+
2010+
def reference(self) -> Optional[str]:
2011+
return c2str(self.cdata.ref)
2012+
2013+
def extensions(self) -> Iterator[ExtensionParsed]:
2014+
for ext in ly_array_iter(self.cdata.exts):
2015+
yield ExtensionParsed(self.context, ext, self.module)
2016+
2017+
def deprecated(self) -> bool:
2018+
return bool(self.cdata.flags & lib.LYS_STATUS_DEPRC)
2019+
2020+
def obsolete(self) -> bool:
2021+
return bool(self.cdata.flags & lib.LYS_STATUS_OBSLT)
2022+
2023+
def status(self) -> str:
2024+
if self.cdata.flags & lib.LYS_STATUS_OBSLT:
2025+
return "obsolete"
2026+
if self.cdata.flags & lib.LYS_STATUS_DEPRC:
2027+
return "deprecated"
2028+
return "current"
2029+
2030+
def __repr__(self):
2031+
cls = self.__class__
2032+
return "<%s.%s: %s>" % (cls.__module__, cls.__name__, str(self))
2033+
2034+
def __str__(self):
2035+
return self.name()
2036+
2037+
19022038
# -------------------------------------------------------------------------------------
19032039
class PNode:
19042040
CONTAINER = lib.LYS_CONTAINER

tests/test_schema.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from libyang import (
88
Context,
99
Extension,
10+
Identity,
1011
IfFeature,
1112
IfOrFeatures,
1213
IOType,
@@ -22,6 +23,7 @@
2223
PChoice,
2324
PContainer,
2425
PGrouping,
26+
PIdentity,
2527
PLeaf,
2628
PLeafList,
2729
PList,
@@ -854,3 +856,44 @@ def test_notification_parsed(self):
854856
self.assertIsNone(next(pnode.typedefs(), None))
855857
self.assertIsNone(next(pnode.groupings(), None))
856858
self.assertIsNotNone(next(iter(pnode)))
859+
860+
861+
# -------------------------------------------------------------------------------------
862+
class IdentityTest(unittest.TestCase):
863+
def setUp(self):
864+
self.ctx = Context(YANG_DIR)
865+
self.module = self.ctx.load_module("yolo-nodetypes")
866+
867+
def tearDown(self):
868+
self.ctx.destroy()
869+
self.ctx = None
870+
871+
def test_identity_compiled(self):
872+
snode = next(self.module.identities(parsed=False))
873+
self.assertIsInstance(snode, Identity)
874+
self.assertEqual("<libyang.schema.Identity: base1>", repr(snode))
875+
self.assertIsNone(snode.description())
876+
self.assertIsNone(snode.reference())
877+
self.assertIsInstance(snode.module(), Module)
878+
derived = list(snode.derived())
879+
self.assertEqual(2, len(derived))
880+
for i in derived:
881+
self.assertIsInstance(i, Identity)
882+
self.assertIsNone(next(snode.extensions(), None))
883+
self.assertIsNone(snode.get_extension('ext1'))
884+
self.assertFalse(snode.deprecated())
885+
self.assertFalse(snode.obsolete())
886+
self.assertEqual("current", snode.status())
887+
888+
def test_identity_parsed(self):
889+
pnode = next(self.module.identities(parsed=True))
890+
self.assertIsInstance(pnode, PIdentity)
891+
self.assertEqual("<libyang.schema.PIdentity: base1>", repr(pnode))
892+
self.assertIsNone(next(pnode.if_features(), None))
893+
self.assertIsNone(next(pnode.bases(), None))
894+
self.assertIsNone(pnode.description())
895+
self.assertIsNone(pnode.reference())
896+
self.assertIsNone(next(pnode.extensions(), None))
897+
self.assertFalse(pnode.deprecated())
898+
self.assertFalse(pnode.obsolete())
899+
self.assertEqual("current", pnode.status())

tests/yang/yolo/yolo-nodetypes.yang

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,4 +112,36 @@ module yolo-nodetypes {
112112
anydata any1 {
113113
when "../cont2";
114114
}
115+
116+
identity base1;
117+
identity base2;
118+
119+
identity derived1 {
120+
base base1;
121+
}
122+
123+
identity derived2 {
124+
base base1;
125+
}
126+
127+
identity derived3 {
128+
base derived1;
129+
}
130+
131+
identity derived4 {
132+
base base2;
133+
}
134+
135+
leaf identity_ref1 {
136+
type identityref {
137+
base base1;
138+
}
139+
}
140+
141+
leaf identity_ref2 {
142+
type identityref {
143+
base base1;
144+
base base2;
145+
}
146+
}
115147
}

0 commit comments

Comments
 (0)