Coverage for pyTooling/Configuration/__init__.py: 95%
57 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-07-31 07:24 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-07-31 07:24 +0000
1# ==================================================================================================================== #
2# _____ _ _ ____ __ _ _ _ #
3# _ __ _ |_ _|__ ___ | (_)_ __ __ _ / ___|___ _ __ / _(_) __ _ _ _ _ __ __ _| |_(_) ___ _ __ #
4# | '_ \| | | || |/ _ \ / _ \| | | '_ \ / _` || | / _ \| '_ \| |_| |/ _` | | | | '__/ _` | __| |/ _ \| '_ \ #
5# | |_) | |_| || | (_) | (_) | | | | | | (_| || |__| (_) | | | | _| | (_| | |_| | | | (_| | |_| | (_) | | | | #
6# | .__/ \__, ||_|\___/ \___/|_|_|_| |_|\__, (_)____\___/|_| |_|_| |_|\__, |\__,_|_| \__,_|\__|_|\___/|_| |_| #
7# |_| |___/ |___/ |___/ #
8# ==================================================================================================================== #
9# Authors: #
10# Patrick Lehmann #
11# #
12# License: #
13# ==================================================================================================================== #
14# Copyright 2021-2026 Patrick Lehmann - Bötzingen, Germany #
15# #
16# Licensed under the Apache License, Version 2.0 (the "License"); #
17# you may not use this file except in compliance with the License. #
18# You may obtain a copy of the License at #
19# #
20# http://www.apache.org/licenses/LICENSE-2.0 #
21# #
22# Unless required by applicable law or agreed to in writing, software #
23# distributed under the License is distributed on an "AS IS" BASIS, #
24# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
25# See the License for the specific language governing permissions and #
26# limitations under the License. #
27# #
28# SPDX-License-Identifier: Apache-2.0 #
29# ==================================================================================================================== #
30#
31"""
32Abstract configuration reader.
34.. hint::
36 See :ref:`high-level help <CONFIG>` for explanations and usage examples.
37"""
38from pathlib import Path
39from typing import Union, ClassVar, Iterator, Type, Optional as Nullable
41from pyTooling.Decorators import export, readonly
42from pyTooling.MetaClasses import ExtendedType, mixin
43from pyTooling.Exceptions import ToolingException
46__all__ = ["KeyT", "NodeT", "ValueT"]
49KeyT = Union[str, int] #: Type variable for keys.
50NodeT = Union["Dictionary", "Sequence"] #: Type variable for nodes.
51ValueT = Union[NodeT, str, int, float] #: Type variable for values.
54@export
55class ConfigurationException(ToolingException):
56 """Base-exception of all exceptions raised by :mod:`pyTooling.Configuration`."""
59@export
60class KeyNotFoundException(ConfigurationException):
61 """
62 The requested key or index doesn't exist in the configuration node.
64 The key was neither found as a string, nor converted to an integer or float. A note lists the keys or the index
65 range offered by the node.
66 """
69@export
70class UnsupportedValueTypeException(ConfigurationException):
71 """
72 The configuration file parser returned a value of a type that isn't supported by :mod:`pyTooling.Configuration`.
74 Supported are scalars (:class:`str`, :class:`int`, :class:`float`) and the parser's dictionary and sequence types.
75 """
78@export
79class InterpolationException(ConfigurationException):
80 """A variable reference (``${...}``) in a configuration value is malformed or can't be resolved."""
83@export
84class PathExpressionException(ConfigurationException):
85 """A path expression (``a:b:c``) doesn't describe a valid node or value in the configuration."""
88@export
89class Node(metaclass=ExtendedType, slots=True):
90 """Abstract node in a configuration data structure."""
92 DICT_TYPE: ClassVar[Type["Dictionary"]] #: Type reference used when instantiating new dictionaries
93 SEQ_TYPE: ClassVar[Type["Sequence"]] #: Type reference used when instantiating new sequences
94 _root: "Configuration" #: Reference to the root node.
95 _parent: "Dictionary" #: Reference to a parent node.
97 def __init__(self, root: "Configuration" = None, parent: Nullable[NodeT] = None) -> None:
98 """
99 Initializes a node.
101 :param root: Reference to the root node.
102 :param parent: Reference to the parent node.
103 """
104 self._root = root
105 self._parent = parent
107 def __len__(self) -> int: # type: ignore[empty-body]
108 """
109 Returns the number of sub-elements.
111 :returns: Number of sub-elements.
112 """
114 def __getitem__(self, key: KeyT) -> ValueT: # type: ignore[empty-body]
115 """
116 Access an element in the node by index or key.
118 :param key: Index or key of the element.
119 :returns: A node (sequence or dictionary) or scalar value (int, float, str).
120 """
121 raise NotImplementedError()
123 def __setitem__(self, key: KeyT, value: ValueT) -> None: # type: ignore[empty-body]
124 """
125 Set an element in the node by index or key.
127 :param key: Index or key of the element.
128 :param value: Value to set
129 """
130 raise NotImplementedError()
132 def __iter__(self) -> Iterator[ValueT]: # type: ignore[empty-body]
133 """
134 Returns an iterator to iterate a node.
136 :returns: Node iterator.
137 """
138 raise NotImplementedError()
140 @property
141 def Key(self) -> KeyT:
142 """
143 Read-only property to access the node's key.
145 :returns: Key of the node.
146 """
147 raise NotImplementedError()
149 @Key.setter
150 def Key(self, value: KeyT) -> None:
151 raise NotImplementedError()
153 def QueryPath(self, query: str) -> ValueT: # type: ignore[empty-body]
154 """
155 Return a node or value based on a path description to that node or value.
157 :param query: String describing the path to the node or value.
158 :returns: A node (sequence or dictionary) or scalar value (int, float, str).
159 """
160 raise NotImplementedError()
163@export
164@mixin
165class Dictionary(Node):
166 """Abstract dictionary node in a configuration."""
168 def __init__(self, root: "Configuration" = None, parent: Nullable[NodeT] = None) -> None:
169 """
170 Initializes a dictionary.
172 :param root: Reference to the root node.
173 :param parent: Reference to the parent node.
174 """
175 Node.__init__(self, root, parent)
177 def __contains__(self, key: KeyT) -> bool: # type: ignore[empty-body]
178 raise NotImplementedError()
181@export
182@mixin
183class Sequence(Node):
184 """Abstract sequence node in a configuration."""
186 def __init__(self, root: "Configuration" = None, parent: Nullable[NodeT] = None) -> None:
187 """
188 Initializes a sequence.
190 :param root: Reference to the root node.
191 :param parent: Reference to the parent node.
192 """
193 Node.__init__(self, root, parent)
195 def __getitem__(self, index: int) -> ValueT: # type: ignore[empty-body]
196 raise NotImplementedError()
198 def __setitem__(self, index: int, value: ValueT) -> None: # type: ignore[empty-body]
199 raise NotImplementedError()
202setattr(Node, "DICT_TYPE", Dictionary)
203setattr(Node, "SEQ_TYPE", Sequence)
206@export
207@mixin
208class Configuration(Node):
209 """Abstract root node in a configuration."""
211 _configFile: Path #: Path to the configuration file.
213 def __init__(self, configFile: Path, root: "Configuration" = None, parent: Nullable[NodeT] = None) -> None:
214 """
215 Initializes a configuration.
217 :param configFile: Configuration file.
218 :param root: Reference to the root node.
219 :param parent: Reference to the parent node.
220 """
221 Node.__init__(self, root, parent)
222 self._configFile = configFile
224 @readonly
225 def ConfigFile(self) -> Path:
226 """
227 Read-only property to access the configuration file's path.
229 :returns: Path to the configuration file.
230 """
231 return self._configFile