@@ -279,22 +279,43 @@ class DNode:
279
279
280
280
__slots__ = ("context" , "cdata" , "attributes" , "free_func" , "__dict__" )
281
281
282
- def __init__ (self , context : "libyang.Context" , cdata ):
282
+ def __init__ (self , context : "libyang.Context" , cdata , refcnt_parent ):
283
283
"""
284
284
:arg context:
285
285
The libyang.Context python object.
286
286
:arg cdata:
287
287
The pointer to the C structure allocated by libyang.so.
288
+ :arg refcnt_parent:
289
+ New nodes may be created with internal references to the internal
290
+ cdata tree. By holding a reference to the parent node that created
291
+ us, we can force Python to keep proper reference counts so the
292
+ destructor doesn't get called. The only time this may be set to
293
+ None is if we can guarantee we are the initial creator of the
294
+ cdata object.
288
295
"""
289
296
self .context = context
290
297
self .cdata = cdata # C type: "struct lyd_node *"
291
298
self .attributes = None
292
299
self .free_func = None # type: Callable[DNode]
300
+ self .refcnt_parent = refcnt_parent
293
301
294
302
def __del__ (self ):
295
- # Delete only the root node when it goes out of scope, this will delete
296
- # all children.
297
- if node .parent () is None :
303
+ # Don't auto-destroy the node unless we know we're allowed. We can
304
+ # determine this if refcnt_parent is set or not. If we have a parent
305
+ # they own the registered cdata.
306
+ # Functions that may create a DNode without a parent:
307
+ # * Context.create_data_path() if parent is None
308
+ # * Context.parse_op{_mem}() if parent is None
309
+ # * Context.parse_data{_mem,_file}() if parent is None
310
+ # * DNode.diff()
311
+ # * DNode.duplicate() if parent is None
312
+ # * dict_to_dnode() if parent is None
313
+ # * DNode.merge_data_dict()
314
+ # * DNode.iter_tree()
315
+ # * DNode.leafref_nodes()
316
+ # Functions that might unset the refcnt_parent:
317
+ # * DNode.merge{_module}()
318
+ if not self .refcnt_parent :
298
319
self .free ()
299
320
300
321
def meta (self ):
@@ -458,17 +479,17 @@ def schema(self) -> SNode:
458
479
def parent (self ) -> Optional ["DNode" ]:
459
480
if not self .cdata .parent :
460
481
return None
461
- return self .new (self .context , self .cdata .parent )
482
+ return self .new (self .context , self .cdata .parent , self )
462
483
463
484
def next (self ) -> Optional ["DNode" ]:
464
485
if not self .cdata .next :
465
486
return None
466
- return self .new (self .context , self .cdata .next )
487
+ return self .new (self .context , self .cdata .next , self )
467
488
468
489
def prev (self ) -> Optional ["DNode" ]:
469
490
if not self .cdata .prev :
470
491
return None
471
- return self .new (self .context , self .cdata .prev )
492
+ return self .new (self .context , self .cdata .prev , self )
472
493
473
494
def root (self ) -> "DNode" :
474
495
node = self
@@ -480,7 +501,7 @@ def first_sibling(self) -> "DNode":
480
501
n = lib .lyd_first_sibling (self .cdata )
481
502
if n == self .cdata :
482
503
return self
483
- return self .new (self .context , n )
504
+ return self .new (self .context , n , self )
484
505
485
506
def siblings (self , include_self : bool = True ) -> Iterator ["DNode" ]:
486
507
n = lib .lyd_first_sibling (self .cdata )
@@ -489,14 +510,14 @@ def siblings(self, include_self: bool = True) -> Iterator["DNode"]:
489
510
if include_self :
490
511
yield self
491
512
else :
492
- yield self .new (self .context , n )
513
+ yield self .new (self .context , n , self )
493
514
n = n .next
494
515
495
516
def find_path (self , path : str , output : bool = False ):
496
517
node = ffi .new ("struct lyd_node **" )
497
518
ret = lib .lyd_find_path (self .cdata , str2c (path ), output , node )
498
519
if ret == lib .LY_SUCCESS :
499
- return DNode .new (self .context , node [0 ])
520
+ return DNode .new (self .context , node [0 ], self )
500
521
return None
501
522
502
523
def find_one (self , xpath : str ) -> Optional ["DNode" ]:
@@ -514,7 +535,7 @@ def find_all(self, xpath: str) -> Iterator["DNode"]:
514
535
node_set = node_set [0 ]
515
536
try :
516
537
for i in range (node_set .count ):
517
- n = DNode .new (self .context , node_set .dnodes [i ])
538
+ n = DNode .new (self .context , node_set .dnodes [i ], self )
518
539
yield n
519
540
finally :
520
541
lib .ly_set_free (node_set , ffi .NULL )
@@ -599,7 +620,7 @@ def diff(
599
620
if node_p [0 ] == ffi .NULL :
600
621
return None
601
622
602
- return self .new (self .context , node_p [0 ])
623
+ return self .new (self .context , node_p [0 ], None ''' New allocation ''' )
603
624
604
625
def diff_apply (self , diff_node : "DNode" ) -> None :
605
626
node_p = ffi .new ("struct lyd_node **" )
@@ -636,7 +657,7 @@ def duplicate(
636
657
else :
637
658
lib .lyd_dup_single (self .cdata , parent , flags , node )
638
659
639
- return DNode .new (self .context , node [0 ])
660
+ return DNode .new (self .context , node [0 ], parent )
640
661
641
662
def merge_module (
642
663
self ,
@@ -654,6 +675,10 @@ def merge_module(
654
675
if ret != lib .LY_SUCCESS :
655
676
raise self .context .error ("merge failed" )
656
677
678
+ if destruct :
679
+ source .cdata = None
680
+ source .refcnt_parent = None
681
+
657
682
def merge (
658
683
self ,
659
684
source : "DNode" ,
@@ -675,10 +700,14 @@ def merge(
675
700
676
701
self .cdata = node_p [0 ]
677
702
703
+ if destruct :
704
+ source .cdata = None
705
+ source .refcnt_parent = None
706
+
678
707
def iter_tree (self ) -> Iterator ["DNode" ]:
679
708
n = next_n = self .cdata
680
709
while n != ffi .NULL :
681
- yield self .new (self .context , n )
710
+ yield self .new (self .context , n , self )
682
711
683
712
next_n = lib .lyd_child (n )
684
713
if next_n == ffi .NULL :
@@ -1031,7 +1060,7 @@ def leafref_nodes(self) -> Iterator["DNode"]:
1031
1060
if lib .lyd_leafref_get_links (term_node , out ) != lib .LY_SUCCESS :
1032
1061
return
1033
1062
for n in ly_array_iter (out [0 ].leafref_nodes ):
1034
- yield DNode .new (self .context , n )
1063
+ yield DNode .new (self .context , n , self )
1035
1064
1036
1065
def __repr__ (self ):
1037
1066
cls = self .__class__
@@ -1052,7 +1081,7 @@ def _decorator(nodeclass):
1052
1081
return _decorator
1053
1082
1054
1083
@classmethod
1055
- def new (cls , context : "libyang.Context" , cdata ) -> "DNode" :
1084
+ def new (cls , context : "libyang.Context" , cdata , refcnt_parent ) -> "DNode" :
1056
1085
cdata = ffi .cast ("struct lyd_node *" , cdata )
1057
1086
if not cdata .schema :
1058
1087
schemas = list (context .find_path (cls ._get_path (cdata )))
@@ -1063,7 +1092,7 @@ def new(cls, context: "libyang.Context", cdata) -> "DNode":
1063
1092
nodecls = cls .NODETYPE_CLASS .get (cdata .schema .nodetype , None )
1064
1093
if nodecls is None :
1065
1094
raise TypeError ("node type %s not implemented" % cdata .schema .nodetype )
1066
- return nodecls (context , cdata )
1095
+ return nodecls (context , cdata , allow_destroy )
1067
1096
1068
1097
@staticmethod
1069
1098
def _get_path (cdata ) -> str :
@@ -1096,7 +1125,7 @@ def children(self, no_keys=False) -> Iterator[DNode]:
1096
1125
1097
1126
while child :
1098
1127
if child .schema != ffi .NULL :
1099
- yield DNode .new (self .context , child )
1128
+ yield DNode .new (self .context , child , self )
1100
1129
child = child .next
1101
1130
1102
1131
def __iter__ (self ):
@@ -1243,7 +1272,7 @@ def dict_to_dnode(
1243
1272
1244
1273
created = []
1245
1274
1246
- def _create_leaf (_parent , module , name , value , in_rpc_output = False ):
1275
+ def _create_leaf (_parent_cdata , module , name , value , in_rpc_output = False ):
1247
1276
if value is not None :
1248
1277
if isinstance (value , bool ):
1249
1278
value = str (value ).lower ()
@@ -1253,7 +1282,7 @@ def _create_leaf(_parent, module, name, value, in_rpc_output=False):
1253
1282
n = ffi .new ("struct lyd_node **" )
1254
1283
flags = newval_flags (rpc_output = in_rpc_output , store_only = store_only )
1255
1284
ret = lib .lyd_new_term (
1256
- _parent ,
1285
+ _parent_cdata ,
1257
1286
module .cdata ,
1258
1287
str2c (name ),
1259
1288
str2c (value ),
@@ -1262,21 +1291,21 @@ def _create_leaf(_parent, module, name, value, in_rpc_output=False):
1262
1291
)
1263
1292
1264
1293
if ret != lib .LY_SUCCESS :
1265
- if _parent :
1266
- parent_path = repr (DNode .new (module .context , _parent ).path ())
1294
+ if _parent_cdata :
1295
+ parent_path = repr (DNode .new (module .context , _parent_cdata , parent ).path ())
1267
1296
else :
1268
1297
parent_path = "module %r" % module .name ()
1269
1298
raise module .context .error (
1270
1299
"failed to create leaf %r as a child of %s" , name , parent_path
1271
1300
)
1272
1301
created .append (n [0 ])
1273
1302
1274
- def _create_container (_parent , module , name , in_rpc_output = False ):
1303
+ def _create_container (_parent_cdata , module , name , in_rpc_output = False ):
1275
1304
n = ffi .new ("struct lyd_node **" )
1276
- ret = lib .lyd_new_inner (_parent , module .cdata , str2c (name ), in_rpc_output , n )
1305
+ ret = lib .lyd_new_inner (_parent_cdata , module .cdata , str2c (name ), in_rpc_output , n )
1277
1306
if ret != lib .LY_SUCCESS :
1278
- if _parent :
1279
- parent_path = repr (DNode .new (module .context , _parent ).path ())
1307
+ if _parent_cdata :
1308
+ parent_path = repr (DNode .new (module .context , _parent_cdata , parent ).path ())
1280
1309
else :
1281
1310
parent_path = "module %r" % module .name ()
1282
1311
raise module .context .error (
@@ -1287,20 +1316,20 @@ def _create_container(_parent, module, name, in_rpc_output=False):
1287
1316
created .append (n [0 ])
1288
1317
return n [0 ]
1289
1318
1290
- def _create_list (_parent , module , name , key_values , in_rpc_output = False ):
1319
+ def _create_list (_parent_cdata , module , name , key_values , in_rpc_output = False ):
1291
1320
n = ffi .new ("struct lyd_node **" )
1292
1321
flags = newval_flags (rpc_output = in_rpc_output , store_only = store_only )
1293
1322
ret = lib .lyd_new_list (
1294
- _parent ,
1323
+ _parent_cdata ,
1295
1324
module .cdata ,
1296
1325
str2c (name ),
1297
1326
flags ,
1298
1327
n ,
1299
1328
* [str2c (str (i )) for i in key_values ],
1300
1329
)
1301
1330
if ret != lib .LY_SUCCESS :
1302
- if _parent :
1303
- parent_path = repr (DNode .new (module .context , _parent ).path ())
1331
+ if _parent_cdata :
1332
+ parent_path = repr (DNode .new (module .context , _parent_cdata , parent ).path ())
1304
1333
else :
1305
1334
parent_path = "module %r" % module .name ()
1306
1335
raise module .context .error (
@@ -1356,7 +1385,7 @@ def _dic_keys(_dic, _schema):
1356
1385
return keys
1357
1386
return _dic .keys ()
1358
1387
1359
- def _to_dnode (_dic , _schema , _parent = ffi .NULL , in_rpc_output = False ):
1388
+ def _to_dnode (_dic , _schema , _parent_cdata = ffi .NULL , in_rpc_output = False ):
1360
1389
for key in _dic_keys (_dic , _schema ):
1361
1390
if ":" in key :
1362
1391
prefix , name = key .split (":" )
@@ -1379,7 +1408,7 @@ def _to_dnode(_dic, _schema, _parent=ffi.NULL, in_rpc_output=False):
1379
1408
value = _dic [key ]
1380
1409
1381
1410
if isinstance (s , SLeaf ):
1382
- _create_leaf (_parent , module , name , value , in_rpc_output )
1411
+ _create_leaf (_parent_cdata , module , name , value , in_rpc_output )
1383
1412
1384
1413
elif isinstance (s , SLeafList ):
1385
1414
if not isinstance (value , (list , tuple )):
@@ -1388,14 +1417,14 @@ def _to_dnode(_dic, _schema, _parent=ffi.NULL, in_rpc_output=False):
1388
1417
% (s .schema_path (), value )
1389
1418
)
1390
1419
for v in value :
1391
- _create_leaf (_parent , module , name , v , in_rpc_output )
1420
+ _create_leaf (_parent_cdata , module , name , v , in_rpc_output )
1392
1421
1393
1422
elif isinstance (s , SRpc ):
1394
- n = _create_container (_parent , module , name , in_rpc_output )
1423
+ n = _create_container (_parent_cdata , module , name , in_rpc_output )
1395
1424
_to_dnode (value , s , n , rpcreply )
1396
1425
1397
1426
elif isinstance (s , SContainer ):
1398
- n = _create_container (_parent , module , name , in_rpc_output )
1427
+ n = _create_container (_parent_cdata , module , name , in_rpc_output )
1399
1428
_to_dnode (value , s , n , in_rpc_output )
1400
1429
1401
1430
elif isinstance (s , SList ):
@@ -1420,31 +1449,31 @@ def _to_dnode(_dic, _schema, _parent=ffi.NULL, in_rpc_output=False):
1420
1449
except KeyError as e :
1421
1450
raise ValueError ("Missing key %s in the list" % (k )) from e
1422
1451
1423
- n = _create_list (_parent , module , name , key_values , in_rpc_output )
1452
+ n = _create_list (_parent_cdata , module , name , key_values , in_rpc_output )
1424
1453
_to_dnode (val , s , n , in_rpc_output )
1425
1454
1426
1455
elif isinstance (s , SNotif ):
1427
- n = _create_container (_parent , module , name , in_rpc_output )
1456
+ n = _create_container (_parent_cdata , module , name , in_rpc_output )
1428
1457
_to_dnode (value , s , n , in_rpc_output )
1429
1458
1430
1459
result = None
1431
1460
1432
1461
try :
1433
1462
if parent is not None :
1434
- _parent = parent .cdata
1463
+ _parent_cdata = parent .cdata
1435
1464
_schema_parent = parent .schema ()
1436
1465
else :
1437
- _parent = ffi .NULL
1466
+ _parent_cdata = ffi .NULL
1438
1467
_schema_parent = module
1439
1468
1440
1469
_to_dnode (
1441
1470
dic ,
1442
1471
_schema_parent ,
1443
- _parent ,
1472
+ _parent_cdata ,
1444
1473
in_rpc_output = rpcreply and isinstance (parent , DRpc ),
1445
1474
)
1446
1475
if created :
1447
- result = DNode .new (module .context , created [0 ])
1476
+ result = DNode .new (module .context , created [0 ], parent )
1448
1477
if validate :
1449
1478
result .root ().validate (
1450
1479
no_state = no_state ,
0 commit comments