Skip to content

Commit 76504c4

Browse files
stewegsamuel-gauthier
authored andcommitted
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 Closes: #118 Signed-off-by: Stefan Gula <[email protected]> Signed-off-by: Samuel Gauthier <[email protected]>
1 parent a19cab6 commit 76504c4

File tree

5 files changed

+256
-14
lines changed

5 files changed

+256
-14
lines changed

cffi/cdefs.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,16 @@ struct lysp_ext_instance {
475475
char rev[LY_REV_SIZE];
476476
};
477477

478+
struct lysp_ident {
479+
const char *name;
480+
struct lysp_qname *iffeatures;
481+
const char **bases;
482+
const char *dsc;
483+
const char *ref;
484+
struct lysp_ext_instance *exts;
485+
uint16_t flags;
486+
};
487+
478488
struct lysp_feature {
479489
const char *name;
480490
struct lysp_qname *iffeatures;
@@ -976,6 +986,16 @@ struct lysp_restr {
976986
struct lysp_ext_instance *exts;
977987
};
978988

989+
struct lysc_ident {
990+
const char *name;
991+
const char *dsc;
992+
const char *ref;
993+
struct lys_module *module;
994+
struct lysc_ident **derived;
995+
struct lysc_ext_instance *exts;
996+
uint16_t flags;
997+
};
998+
979999
struct lysc_type_num {
9801000
const char *name;
9811001
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,
@@ -151,6 +153,7 @@
151153
"ExtensionPlugin",
152154
"ExtensionRemoved",
153155
"Feature",
156+
"Identity",
154157
"IfAndFeatures",
155158
"IfFeature",
156159
"IfFeatureExpr",
@@ -184,6 +187,7 @@
184187
"PContainer",
185188
"PEnum",
186189
"PGrouping",
190+
"PIdentity",
187191
"PLeaf",
188192
"PLeafList",
189193
"PList",

libyang/schema.py

Lines changed: 148 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,14 @@ def notifications(self) -> Iterator["PNotif"]:
172172
for n in ly_list_iter(self.cdata.parsed.notifs):
173173
yield PNotif(self.context, n, self)
174174

175+
def identities(self) -> Iterator["Identity"]:
176+
for i in ly_array_iter(self.cdata.identities):
177+
yield Identity(self.context, i)
178+
179+
def parsed_identities(self) -> Iterator["PIdentity"]:
180+
for i in ly_array_iter(self.cdata.parsed.identities):
181+
yield PIdentity(self.context, i, self)
182+
175183
def __str__(self) -> str:
176184
return self.name()
177185

@@ -415,13 +423,16 @@ def name(self) -> str:
415423
def module(self) -> Module:
416424
return self._module_from_parsed()
417425

418-
def parent_node(self) -> Optional["PNode"]:
419-
if not bool(self.cdata.parent_stmt & lib.LY_STMT_NODE_MASK):
420-
return None
421-
try:
422-
return PNode.new(self.context, self.cdata.parent, self.module_parent)
423-
except LibyangError:
424-
return None
426+
def parent_node(self) -> Optional[Union["PNode", "PIdentity"]]:
427+
if self.cdata.parent_stmt == lib.LY_STMT_IDENTITY:
428+
cdata = ffi.cast("struct lysp_ident *", self.cdata.parent)
429+
return PIdentity(self.context, cdata, self.module_parent)
430+
if bool(self.cdata.parent_stmt & lib.LY_STMT_NODE_MASK):
431+
try:
432+
return PNode.new(self.context, self.cdata.parent, self.module_parent)
433+
except LibyangError:
434+
return None
435+
return None
425436

426437

427438
# -------------------------------------------------------------------------------------
@@ -440,13 +451,16 @@ def module(self) -> Module:
440451
raise self.context.error("cannot get module")
441452
return Module(self.context, self.cdata_def.module)
442453

443-
def parent_node(self) -> Optional["SNode"]:
444-
if not bool(self.cdata.parent_stmt & lib.LY_STMT_NODE_MASK):
445-
return None
446-
try:
447-
return SNode.new(self.context, self.cdata.parent)
448-
except LibyangError:
449-
return None
454+
def parent_node(self) -> Optional[Union["SNode", "Identity"]]:
455+
if self.cdata.parent_stmt == lib.LY_STMT_IDENTITY:
456+
cdata = ffi.cast("struct lysc_ident *", self.cdata.parent)
457+
return Identity(self.context, cdata)
458+
if bool(self.cdata.parent_stmt & lib.LY_STMT_NODE_MASK):
459+
try:
460+
return SNode.new(self.context, self.cdata.parent)
461+
except LibyangError:
462+
return None
463+
return None
450464

451465

452466
# -------------------------------------------------------------------------------------
@@ -624,6 +638,13 @@ def leafref_path(self) -> Optional["str"]:
624638
lr = ffi.cast("struct lysc_type_leafref *", self.cdata)
625639
return c2str(lib.lyxp_get_expr(lr.path))
626640

641+
def identity_bases(self) -> Iterator["Identity"]:
642+
if self.cdata.basetype != lib.LY_TYPE_IDENT:
643+
return
644+
ident = ffi.cast("struct lysc_type_identityref *", self.cdata)
645+
for b in ly_array_iter(ident.bases):
646+
yield Identity(self.context, b)
647+
627648
def typedef(self) -> "Typedef":
628649
if ":" in self.name():
629650
module_prefix, type_name = self.name().split(":")
@@ -870,6 +891,68 @@ def __str__(self):
870891
return self.name()
871892

872893

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

18921975

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

tests/test_schema.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
Context,
99
Extension,
1010
ExtensionParsed,
11+
Identity,
1112
IfFeature,
1213
IfOrFeatures,
1314
IOType,
@@ -23,6 +24,7 @@
2324
PChoice,
2425
PContainer,
2526
PGrouping,
27+
PIdentity,
2628
PLeaf,
2729
PLeafList,
2830
PList,
@@ -868,3 +870,52 @@ def test_notification_parsed(self):
868870
self.assertIsNone(next(pnode.typedefs(), None))
869871
self.assertIsNone(next(pnode.groupings(), None))
870872
self.assertIsNotNone(next(iter(pnode)))
873+
874+
875+
# -------------------------------------------------------------------------------------
876+
class IdentityTest(unittest.TestCase):
877+
def setUp(self):
878+
self.ctx = Context(YANG_DIR)
879+
self.module = self.ctx.load_module("yolo-nodetypes")
880+
881+
def tearDown(self):
882+
self.ctx.destroy()
883+
self.ctx = None
884+
885+
def test_identity_compiled(self):
886+
sidentity = next(self.module.identities())
887+
self.assertIsInstance(sidentity, Identity)
888+
self.assertEqual(sidentity.name(), "base1")
889+
self.assertEqual(sidentity.description(), "Base 1.")
890+
self.assertEqual(sidentity.reference(), "Some reference.")
891+
self.assertIsInstance(sidentity.module(), Module)
892+
derived = list(sidentity.derived())
893+
self.assertEqual(2, len(derived))
894+
for i in derived:
895+
self.assertIsInstance(i, Identity)
896+
self.assertEqual(derived[0].name(), "derived1")
897+
self.assertEqual(derived[1].name(), "derived2")
898+
self.assertEqual(next(derived[1].extensions()).name(), "identity-name")
899+
self.assertIsNone(next(sidentity.extensions(), None))
900+
self.assertIsNone(sidentity.get_extension("ext1"))
901+
self.assertFalse(sidentity.deprecated())
902+
self.assertFalse(sidentity.obsolete())
903+
self.assertEqual("current", sidentity.status())
904+
905+
snode = next(self.ctx.find_path("/yolo-nodetypes:identity_ref"))
906+
identities = list(snode.type().identity_bases())
907+
self.assertEqual(identities[0].name(), sidentity.name())
908+
self.assertEqual(identities[1].name(), "base2")
909+
910+
def test_identity_parsed(self):
911+
pidentity = next(self.module.parsed_identities())
912+
self.assertIsInstance(pidentity, PIdentity)
913+
self.assertEqual(pidentity.name(), "base1")
914+
self.assertIsNone(next(pidentity.if_features(), None))
915+
self.assertIsNone(next(pidentity.bases(), None))
916+
self.assertEqual(pidentity.description(), "Base 1.")
917+
self.assertEqual(pidentity.reference(), "Some reference.")
918+
self.assertIsNone(next(pidentity.extensions(), None))
919+
self.assertFalse(pidentity.deprecated())
920+
self.assertFalse(pidentity.obsolete())
921+
self.assertEqual("current", pidentity.status())

tests/yang/yolo/yolo-nodetypes.yang

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,39 @@ module yolo-nodetypes {
132132
when "../cont2";
133133
}
134134

135+
extension identity-name {
136+
description
137+
"Extend an identity to provide an alternative name.";
138+
argument name;
139+
}
140+
141+
identity base1 {
142+
description
143+
"Base 1.";
144+
reference "Some reference.";
145+
}
146+
identity base2;
147+
148+
identity derived1 {
149+
base base1;
150+
}
151+
152+
identity derived2 {
153+
base base1;
154+
sys:identity-name "Derived2";
155+
}
156+
157+
identity derived3 {
158+
base derived1;
159+
}
160+
161+
leaf identity_ref {
162+
type identityref {
163+
base base1;
164+
base base2;
165+
}
166+
}
167+
135168
leaf ip-address {
136169
type inet:ipv4-address;
137170
}

0 commit comments

Comments
 (0)