4040 ConfigurationKeyError ,
4141 DuplicatedConfigurationError ,
4242)
43- from layered_config_tree .types import InputData , NestedDict , NestedDictValue , NodeValue
43+ from layered_config_tree .types import InputData
4444
4545
4646class ConfigNode :
@@ -74,7 +74,7 @@ class ConfigNode:
7474 def __init__ (self , layers : list [str ], name : str ):
7575 self ._name = name
7676 self ._layers = layers
77- self ._values : dict [str , tuple [Optional [str ], NodeValue ]] = {}
77+ self ._values : dict [str , tuple [Optional [str ], Any ]] = {}
7878 self ._frozen = False
7979 self ._accessed = False
8080
@@ -89,7 +89,7 @@ def accessed(self) -> bool:
8989 return self ._accessed
9090
9191 @property
92- def metadata (self ) -> list [dict [str , Union [Optional [str ], NodeValue ]]]:
92+ def metadata (self ) -> list [dict [str , Union [Optional [str ], Any ]]]:
9393 """Returns all values and associated metadata for this node."""
9494 result = []
9595 for layer in self ._layers :
@@ -112,7 +112,7 @@ def freeze(self) -> None:
112112 """
113113 self ._frozen = True
114114
115- def get_value (self , layer : Optional [str ] = None ) -> NodeValue :
115+ def get_value (self , layer : Optional [str ] = None ) -> Any :
116116 """Returns the value at the specified layer.
117117
118118 If no layer is specified, the outermost (highest priority) layer
@@ -133,7 +133,7 @@ def get_value(self, layer: Optional[str] = None) -> NodeValue:
133133 self ._accessed = True
134134 return value
135135
136- def update (self , value : NodeValue , layer : Optional [str ], source : Optional [str ]) -> None :
136+ def update (self , value : Any , layer : Optional [str ], source : Optional [str ]) -> None :
137137 """Set a value for a layer with optional metadata about source.
138138
139139 Parameters
@@ -180,7 +180,7 @@ def update(self, value: NodeValue, layer: Optional[str], source: Optional[str])
180180 else :
181181 self ._values [layer ] = (source , value )
182182
183- def _get_value_with_source (self , layer : Optional [str ]) -> tuple [Optional [str ], NodeValue ]:
183+ def _get_value_with_source (self , layer : Optional [str ]) -> tuple [Optional [str ], Any ]:
184184 """Returns a (source, value) tuple at the specified layer.
185185
186186 If no layer is specified, the outermost (highest priority) layer
@@ -232,10 +232,10 @@ class ConfigIterator:
232232 This iterator is used to iterate over the keys of a LayeredConfigTree object.
233233 """
234234
235- def __init__ (self , config_tree : " LayeredConfigTree" ):
235+ def __init__ (self , config_tree : LayeredConfigTree ):
236236 self ._iterator = iter (config_tree ._children )
237237
238- def __iter__ (self ) -> " ConfigIterator" :
238+ def __iter__ (self ) -> ConfigIterator :
239239 return self
240240
241241 def __next__ (self ) -> str :
@@ -252,7 +252,7 @@ class LayeredConfigTree:
252252
253253 # Define type annotations here since they're indirectly defined below
254254 _layers : list [str ]
255- _children : dict [str , Union [" LayeredConfigTree" , " ConfigNode" ]]
255+ _children : dict [str , Union [LayeredConfigTree , ConfigNode ]]
256256 _frozen : bool
257257 _name : str
258258
@@ -308,7 +308,7 @@ def freeze(self) -> None:
308308 for child in self .values ():
309309 child .freeze ()
310310
311- def items (self ) -> Iterable [tuple [str , Union [" LayeredConfigTree" , ConfigNode ]]]:
311+ def items (self ) -> Iterable [tuple [str , Union [LayeredConfigTree , ConfigNode ]]]:
312312 """Return an iterable of all (child_name, child) pairs."""
313313 return self ._children .items ()
314314
@@ -332,7 +332,7 @@ def unused_keys(self) -> list[str]:
332332 unused .append (f"{ name } .{ grandchild_name } " )
333333 return unused
334334
335- def to_dict (self ) -> NestedDict :
335+ def to_dict (self ) -> dict [ str , Any ] :
336336 """Converts the LayeredConfigTree into a nested dictionary.
337337
338338 All metadata is lost in this conversion.
@@ -343,12 +343,38 @@ def to_dict(self) -> NestedDict:
343343 if isinstance (child , ConfigNode ):
344344 result [name ] = child .get_value (layer = None )
345345 else :
346- result [name ] = child .to_dict () # type: ignore[assignment]
346+ result [name ] = child .to_dict ()
347347 return result
348348
349- def get_from_layer (
350- self , name : str , layer : Optional [str ] = None
351- ) -> Union [NodeValue , "LayeredConfigTree" ]:
349+ def get (self , key : str , default_value : Any = None ) -> Any :
350+ """Return the LayeredConfigTree or value at the key in the outermost layer of the config tree.
351+
352+ Parameters
353+ ----------
354+ key
355+ The string to look for in the outermost layer of the config tree.
356+ default_value
357+ The value to return if key is not found
358+ """
359+ return self [key ] if key in self ._children else default_value
360+
361+ def get_tree (self , key : str ) -> LayeredConfigTree :
362+ """Return the LayeredConfigTree at the key in the outermost layer of the config tree.
363+
364+ Parameters
365+ ----------
366+ key
367+ The str we look up in the outermost layer of the config tree.
368+ """
369+ data = self [key ]
370+ if isinstance (data , LayeredConfigTree ):
371+ return data
372+ else :
373+ raise ConfigurationError (
374+ f"The data you accessed using { key } with get_tree was of type { type (data )} , but get_tree must return a LayeredConfigTree."
375+ )
376+
377+ def get_from_layer (self , name : str , layer : Optional [str ] = None ) -> Any :
352378 """Get a configuration value from the provided layer.
353379
354380 If no layer is specified, the outermost (highest priority) layer
@@ -420,7 +446,7 @@ def update(
420446 for k , v in data .items ():
421447 self ._set_with_metadata (k , v , layer , source )
422448
423- def metadata (self , name : str ) -> list [NestedDict ]:
449+ def metadata (self , name : str ) -> list [dict [ str , Any ] ]:
424450 if name in self :
425451 return self ._children [name ].metadata # type: ignore[return-value]
426452 name = f"{ self ._name } .{ name } " if self ._name else name
@@ -430,7 +456,7 @@ def metadata(self, name: str) -> list[NestedDict]:
430456 def _coerce (
431457 data : InputData ,
432458 source : Optional [str ],
433- ) -> tuple [NestedDict , Optional [str ]]:
459+ ) -> tuple [dict [ str , Any ] , Optional [str ]]:
434460 """Coerces data into dictionary format."""
435461 if isinstance (data , dict ):
436462 return data , source
@@ -465,7 +491,7 @@ def _coerce(
465491 def _set_with_metadata (
466492 self ,
467493 name : str ,
468- value : Union [ NestedDictValue , str , Path , "LayeredConfigTree" ] ,
494+ value : Any ,
469495 layer : Optional [str ],
470496 source : Optional [str ],
471497 ) -> None :
@@ -519,9 +545,9 @@ def _set_with_metadata(
519545 f"Can't assign a value to a LayeredConfigTree." , name
520546 )
521547
522- self ._children [name ].update (value , layer , source ) # type: ignore[arg-type]
548+ self ._children [name ].update (value , layer , source )
523549
524- def __setattr__ (self , name : str , value : NestedDictValue ) -> None :
550+ def __setattr__ (self , name : str , value : Any ) -> None :
525551 """Set a value on the outermost layer."""
526552 if name not in self :
527553 raise ConfigurationKeyError (
@@ -530,7 +556,7 @@ def __setattr__(self, name: str, value: NestedDictValue) -> None:
530556 )
531557 self ._set_with_metadata (name , value , layer = None , source = None )
532558
533- def __setitem__ (self , name : str , value : NestedDictValue ) -> None :
559+ def __setitem__ (self , name : str , value : Any ) -> None :
534560 """Set a value on the outermost layer."""
535561 if name not in self :
536562 raise ConfigurationKeyError (
@@ -554,14 +580,14 @@ def __getattr__(self, name: str) -> Any:
554580 # * Calling __getattr__ before we have set up the state doesn't work,
555581 # because it leads to an infinite loop looking for the module's
556582 # actual attributes (not config keys)
557- def __getstate__ (self ) -> NestedDict :
583+ def __getstate__ (self ) -> dict [ str , Any ] :
558584 return self .__dict__
559585
560- def __setstate__ (self , state : NestedDict ) -> None :
586+ def __setstate__ (self , state : dict [ str , Any ] ) -> None :
561587 for k , v in state .items ():
562588 self .__dict__ [k ] = v
563589
564- def __getitem__ (self , name : str ) -> Union [ NodeValue , "LayeredConfigTree" ] :
590+ def __getitem__ (self , name : str ) -> Any :
565591 """Get a value from the outermost layer in which it appears."""
566592 return self .get_from_layer (name )
567593
0 commit comments