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

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. 

33 

34.. hint:: 

35 

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 

40 

41from pyTooling.Decorators import export, readonly 

42from pyTooling.MetaClasses import ExtendedType, mixin 

43from pyTooling.Exceptions import ToolingException 

44 

45 

46__all__ = ["KeyT", "NodeT", "ValueT"] 

47 

48 

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. 

52 

53 

54@export 

55class ConfigurationException(ToolingException): 

56 """Base-exception of all exceptions raised by :mod:`pyTooling.Configuration`.""" 

57 

58 

59@export 

60class KeyNotFoundException(ConfigurationException): 

61 """ 

62 The requested key or index doesn't exist in the configuration node. 

63 

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 """ 

67 

68 

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`. 

73 

74 Supported are scalars (:class:`str`, :class:`int`, :class:`float`) and the parser's dictionary and sequence types. 

75 """ 

76 

77 

78@export 

79class InterpolationException(ConfigurationException): 

80 """A variable reference (``${...}``) in a configuration value is malformed or can't be resolved.""" 

81 

82 

83@export 

84class PathExpressionException(ConfigurationException): 

85 """A path expression (``a:b:c``) doesn't describe a valid node or value in the configuration.""" 

86 

87 

88@export 

89class Node(metaclass=ExtendedType, slots=True): 

90 """Abstract node in a configuration data structure.""" 

91 

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. 

96 

97 def __init__(self, root: "Configuration" = None, parent: Nullable[NodeT] = None) -> None: 

98 """ 

99 Initializes a node. 

100 

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 

106 

107 def __len__(self) -> int: # type: ignore[empty-body] 

108 """ 

109 Returns the number of sub-elements. 

110 

111 :returns: Number of sub-elements. 

112 """ 

113 

114 def __getitem__(self, key: KeyT) -> ValueT: # type: ignore[empty-body] 

115 """ 

116 Access an element in the node by index or key. 

117 

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() 

122 

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. 

126 

127 :param key: Index or key of the element. 

128 :param value: Value to set 

129 """ 

130 raise NotImplementedError() 

131 

132 def __iter__(self) -> Iterator[ValueT]: # type: ignore[empty-body] 

133 """ 

134 Returns an iterator to iterate a node. 

135 

136 :returns: Node iterator. 

137 """ 

138 raise NotImplementedError() 

139 

140 @property 

141 def Key(self) -> KeyT: 

142 """ 

143 Read-only property to access the node's key. 

144 

145 :returns: Key of the node. 

146 """ 

147 raise NotImplementedError() 

148 

149 @Key.setter 

150 def Key(self, value: KeyT) -> None: 

151 raise NotImplementedError() 

152 

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. 

156 

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() 

161 

162 

163@export 

164@mixin 

165class Dictionary(Node): 

166 """Abstract dictionary node in a configuration.""" 

167 

168 def __init__(self, root: "Configuration" = None, parent: Nullable[NodeT] = None) -> None: 

169 """ 

170 Initializes a dictionary. 

171 

172 :param root: Reference to the root node. 

173 :param parent: Reference to the parent node. 

174 """ 

175 Node.__init__(self, root, parent) 

176 

177 def __contains__(self, key: KeyT) -> bool: # type: ignore[empty-body] 

178 raise NotImplementedError() 

179 

180 

181@export 

182@mixin 

183class Sequence(Node): 

184 """Abstract sequence node in a configuration.""" 

185 

186 def __init__(self, root: "Configuration" = None, parent: Nullable[NodeT] = None) -> None: 

187 """ 

188 Initializes a sequence. 

189 

190 :param root: Reference to the root node. 

191 :param parent: Reference to the parent node. 

192 """ 

193 Node.__init__(self, root, parent) 

194 

195 def __getitem__(self, index: int) -> ValueT: # type: ignore[empty-body] 

196 raise NotImplementedError() 

197 

198 def __setitem__(self, index: int, value: ValueT) -> None: # type: ignore[empty-body] 

199 raise NotImplementedError() 

200 

201 

202setattr(Node, "DICT_TYPE", Dictionary) 

203setattr(Node, "SEQ_TYPE", Sequence) 

204 

205 

206@export 

207@mixin 

208class Configuration(Node): 

209 """Abstract root node in a configuration.""" 

210 

211 _configFile: Path #: Path to the configuration file. 

212 

213 def __init__(self, configFile: Path, root: "Configuration" = None, parent: Nullable[NodeT] = None) -> None: 

214 """ 

215 Initializes a configuration. 

216 

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 

223 

224 @readonly 

225 def ConfigFile(self) -> Path: 

226 """ 

227 Read-only property to access the configuration file's path. 

228 

229 :returns: Path to the configuration file. 

230 """ 

231 return self._configFile