@@ -38,6 +38,7 @@ def __init__(
38
38
):
39
39
self ._label = None
40
40
self ._parent = None
41
+ self ._detached_parent_path = None
41
42
self .label = label
42
43
self .parent = parent
43
44
super ().__init__ (* args , ** kwargs )
@@ -64,17 +65,21 @@ def parent(self, new_parent: SemanticParent | None) -> None:
64
65
# Exit early if nothing is changing
65
66
return
66
67
67
- if new_parent is not None :
68
- if not isinstance (new_parent , SemanticParent ):
69
- raise ValueError (
70
- f"Expected None or a { SemanticParent .__name__ } for the parent of "
71
- f"{ self .label } , but got { new_parent } "
72
- )
68
+ if new_parent is not None and not isinstance (new_parent , SemanticParent ):
69
+ raise ValueError (
70
+ f"Expected None or a { SemanticParent .__name__ } for the parent of "
71
+ f"{ self .label } , but got { new_parent } "
72
+ )
73
73
74
- if self ._parent is not None and new_parent is not self ._parent :
74
+ if (
75
+ self ._parent is not None
76
+ and new_parent is not self ._parent
77
+ and self in self ._parent .children
78
+ ):
75
79
self ._parent .remove_child (self )
76
80
self ._parent = new_parent
77
- if self ._parent is not None :
81
+ self ._detached_parent_path = None
82
+ if self ._parent is not None and self not in self ._parent .children :
78
83
self ._parent .add_child (self )
79
84
80
85
@property
@@ -83,9 +88,34 @@ def semantic_path(self) -> str:
83
88
The path of node labels from the graph root (parent-most node) down to this
84
89
node.
85
90
"""
86
- prefix = self .parent .semantic_path if isinstance (self .parent , Semantic ) else ""
91
+ if self .parent is None and self .detached_parent_path is None :
92
+ prefix = ""
93
+ elif self .parent is None and self .detached_parent_path is not None :
94
+ prefix = self .detached_parent_path
95
+ elif self .parent is not None and self .detached_parent_path is None :
96
+ prefix = self .parent .semantic_path
97
+ else :
98
+ raise ValueError (
99
+ f"The parent and detached path should not be able to take non-None "
100
+ f"values simultaneously, but got { self .parent } and "
101
+ f"{ self .detached_parent_path } , respectively. Please raise an issue on GitHub "
102
+ f"outlining how your reached this state."
103
+ )
87
104
return prefix + self .semantic_delimiter + self .label
88
105
106
+ @property
107
+ def detached_parent_path (self ) -> str | None :
108
+ """
109
+ The get/set state cycle of :class:`Semantic` de-parents objects, but we may
110
+ still be interested in the semantic path -- e.g. if we `pickle` dump and load
111
+ the object we will lose parent information, but this will still hold what the
112
+ path _was_ before the orphaning process.
113
+
114
+ The detached path will get cleared if a new parent is set, but is otherwise
115
+ used as the root for the purposes of finding the semantic path.
116
+ """
117
+ return self ._detached_parent_path
118
+
89
119
@property
90
120
def full_label (self ) -> str :
91
121
"""
@@ -109,6 +139,8 @@ def as_path(self, root: Path | str | None = None) -> Path:
109
139
110
140
def __getstate__ (self ):
111
141
state = super ().__getstate__ ()
142
+ if self .parent is not None :
143
+ state ["_detached_parent_path" ] = self .parent .semantic_path
112
144
state ["_parent" ] = None
113
145
# Regarding removing parent from state:
114
146
# Basically we want to avoid recursion during (de)serialization; when the
@@ -240,7 +272,7 @@ def add_child(
240
272
# Finally, update label and reflexively form the parent-child relationship
241
273
child .label = label
242
274
self .children [child .label ] = child
243
- child ._parent = self
275
+ child .parent = self
244
276
return child
245
277
246
278
@staticmethod
@@ -316,7 +348,7 @@ def remove_child(self, child: Semantic | str) -> Semantic:
316
348
f"{ Semantic .__name__ } but got { child } "
317
349
)
318
350
319
- child ._parent = None
351
+ child .parent = None
320
352
321
353
return child
322
354
@@ -361,7 +393,7 @@ def __setstate__(self, state):
361
393
# but rather can send just the requested object and its scope (semantic
362
394
# children). So, now return their parent to them:
363
395
for child in self :
364
- child ._parent = self
396
+ child .parent = self
365
397
366
398
367
399
class ParentMostError (TypeError ):
0 commit comments