@@ -84,7 +84,8 @@ class TreeStore(DictStore):
8484
8585 Extends DictStore with strict hierarchical key validation and tree traversal
8686 capabilities. Keys must follow a hierarchical structure using '/' as separator
87- and always start with '/'.
87+ and always start with '/'. If user passes a key that doesn't start with '/',
88+ it will be automatically added.
8889
8990 Parameters
9091 ----------
@@ -195,13 +196,18 @@ def _translate_key_from_full(self, full_key: str) -> str | None:
195196 # Key is not within this subtree
196197 return None
197198
198- def _validate_key (self , key : str ) -> None :
199- """Validate hierarchical key structure.
199+ def _validate_key (self , key : str ) -> str :
200+ """Validate and normalize hierarchical key structure.
200201
201202 Parameters
202203 ----------
203204 key : str
204- The key to validate.
205+ The key to validate and normalize.
206+
207+ Returns
208+ -------
209+ normalized_key : str
210+ The normalized key with leading '/' added if missing.
205211
206212 Raises
207213 ------
@@ -211,8 +217,9 @@ def _validate_key(self, key: str) -> None:
211217 if not isinstance (key , str ):
212218 raise ValueError (f"Key must be a string, got { type (key )} " )
213219
220+ # Auto-add leading '/' if missing
214221 if not key .startswith ("/" ):
215- raise ValueError ( f"Key must start with '/', got: { key } " )
222+ key = "/" + key
216223
217224 if key != "/" and key .endswith ("/" ):
218225 raise ValueError (f"Key cannot end with '/' (except for root), got: { key } " )
@@ -226,13 +233,15 @@ def _validate_key(self, key: str) -> None:
226233 if char in key :
227234 raise ValueError (f"Key cannot contain invalid character { char !r} , got: { key } " )
228235
236+ return key
237+
229238 def __setitem__ (self , key : str , value : np .ndarray | NDArray | C2Array | SChunk ) -> None :
230239 """Add a node with hierarchical key validation.
231240
232241 Parameters
233242 ----------
234243 key : str
235- Hierarchical node key (must start with '/' and use '/' as separator) .
244+ Hierarchical node key.
236245 value : np.ndarray or blosc2.NDArray or blosc2.C2Array or blosc2.SChunk
237246 to store.
238247
@@ -243,7 +252,7 @@ def __setitem__(self, key: str, value: np.ndarray | NDArray | C2Array | SChunk)
243252 assign to a structural path that already has children, or if trying
244253 to add a child to a path that already contains data.
245254 """
246- self ._validate_key (key )
255+ key = self ._validate_key (key )
247256
248257 # Check if this key already has children (is a structural subtree)
249258 children = self .get_children (key )
@@ -293,7 +302,7 @@ def __getitem__(self, key: str) -> "NDArray | C2Array | SChunk | TreeStore":
293302 ValueError
294303 If key doesn't follow hierarchical structure rules.
295304 """
296- self ._validate_key (key )
305+ key = self ._validate_key (key )
297306 if self ._is_vlmeta_key (key ):
298307 raise KeyError (f"Key '{ key } ' not found; vlmeta keys are not directly accessible." )
299308
@@ -334,7 +343,7 @@ def __delitem__(self, key: str) -> None:
334343 ValueError
335344 If key doesn't follow hierarchical structure rules.
336345 """
337- self ._validate_key (key )
346+ key = self ._validate_key (key )
338347
339348 if self ._is_vlmeta_key (key ):
340349 raise KeyError (f"Key '{ key } ' not found; vlmeta keys are not directly accessible." )
@@ -379,7 +388,7 @@ def __contains__(self, key: str) -> bool:
379388 True if key exists, False otherwise.
380389 """
381390 try :
382- self ._validate_key (key )
391+ key = self ._validate_key (key )
383392 if self ._is_vlmeta_key (key ):
384393 return False
385394 full_key = self ._translate_key_to_full (key )
@@ -436,7 +445,7 @@ def get_children(self, path: str) -> list[str]:
436445 children : list[str]
437446 List of direct child paths.
438447 """
439- self ._validate_key (path )
448+ path = self ._validate_key (path )
440449
441450 if path == "/" :
442451 prefix = "/"
@@ -475,7 +484,7 @@ def get_descendants(self, path: str) -> list[str]:
475484 descendants : list[str]
476485 List of all descendant paths.
477486 """
478- self ._validate_key (path )
487+ path = self ._validate_key (path )
479488
480489 if path == "/" :
481490 prefix = "/"
@@ -523,7 +532,7 @@ def walk(self, path: str = "/", topdown: bool = True) -> Iterator[tuple[str, lis
523532 >>> for path, children, nodes in tstore.walk("/child0", topdown=True):
524533 ... print(f"Path: {path}, Children: {children}, Nodes: {nodes}")
525534 """
526- self ._validate_key (path )
535+ path = self ._validate_key (path )
527536
528537 # Get all direct children of this path
529538 direct_children = self .get_children (path )
@@ -583,7 +592,8 @@ def get_subtree(self, path: str) -> "TreeStore":
583592 Parameters
584593 ----------
585594 path : str
586- The path that will become the root of the subtree view (relative to current subtree).
595+ The path that will become the root of the subtree view (relative to current subtree,
596+ will be normalized to start with '/' if missing).
587597
588598 Returns
589599 -------
@@ -604,7 +614,7 @@ def get_subtree(self, path: str) -> "TreeStore":
604614 -----
605615 This is equivalent to `tstore[path]` when path is a structural path.
606616 """
607- self ._validate_key (path )
617+ path = self ._validate_key (path )
608618 full_path = self ._translate_key_to_full (path )
609619
610620 # Create a new TreeStore instance that shares the same underlying storage
0 commit comments