Coverage for pyTooling / Configuration / __init__.py: 94%

54 statements  

« prev     ^ index     » next       coverage.py v7.13.4, created at 2026-02-13 22:36 +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 Node(metaclass=ExtendedType, slots=True): 

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

62 

63 DICT_TYPE: ClassVar[Type["Dictionary"]] #: Type reference used when instantiating new dictionaries 

64 SEQ_TYPE: ClassVar[Type["Sequence"]] #: Type reference used when instantiating new sequences 

65 _root: "Configuration" #: Reference to the root node. 

66 _parent: "Dictionary" #: Reference to a parent node. 

67 

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

69 """ 

70 Initializes a node. 

71 

72 :param root: Reference to the root node. 

73 :param parent: Reference to the parent node. 

74 """ 

75 self._root = root 

76 self._parent = parent 

77 

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

79 """ 

80 Returns the number of sub-elements. 

81 

82 :returns: Number of sub-elements. 

83 """ 

84 

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

86 """ 

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

88 

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

90 :returns: A node (sequence or dictionary) or scalar value (int, float, str). 

91 """ 

92 raise NotImplementedError() 

93 

94 def __setitem__(self, key: KeyT, value: ValueT) -> None: # type: ignore[empty-body] 

95 """ 

96 Set an element in the node by index or key. 

97 

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

99 :param value: Value to set 

100 """ 

101 raise NotImplementedError() 

102 

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

104 """ 

105 Returns an iterator to iterate a node. 

106 

107 :returns: Node iterator. 

108 """ 

109 raise NotImplementedError() 

110 

111 @property 

112 def Key(self) -> KeyT: 

113 raise NotImplementedError() 

114 

115 @Key.setter 

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

117 raise NotImplementedError() 

118 

119 def QueryPath(self, query: str) -> ValueT: # type: ignore[empty-body] 

120 """ 

121 Return a node or value based on a path description to that node or value. 

122 

123 :param query: String describing the path to the node or value. 

124 :returns: A node (sequence or dictionary) or scalar value (int, float, str). 

125 """ 

126 raise NotImplementedError() 

127 

128 

129@export 

130@mixin 

131class Dictionary(Node): 

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

133 

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

135 """ 

136 Initializes a dictionary. 

137 

138 :param root: Reference to the root node. 

139 :param parent: Reference to the parent node. 

140 """ 

141 Node.__init__(self, root, parent) 

142 

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

144 raise NotImplementedError() 

145 

146 

147@export 

148@mixin 

149class Sequence(Node): 

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

151 

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

153 """ 

154 Initializes a sequence. 

155 

156 :param root: Reference to the root node. 

157 :param parent: Reference to the parent node. 

158 """ 

159 Node.__init__(self, root, parent) 

160 

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

162 raise NotImplementedError() 

163 

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

165 raise NotImplementedError() 

166 

167 

168setattr(Node, "DICT_TYPE", Dictionary) 

169setattr(Node, "SEQ_TYPE", Sequence) 

170 

171 

172@export 

173@mixin 

174class Configuration(Node): 

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

176 

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

178 

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

180 """ 

181 Initializes a configuration. 

182 

183 :param configFile: Configuration file. 

184 :param root: Reference to the root node. 

185 :param parent: Reference to the parent node. 

186 """ 

187 Node.__init__(self, root, parent) 

188 self._configFile = configFile 

189 

190 @readonly 

191 def ConfigFile(self) -> Path: 

192 """ 

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

194 

195 :returns: Path to the configuration file. 

196 """ 

197 return self._configFile