Coverage for pyTooling/Configuration/__init__.py: 95%
55 statements
« prev ^ index » next coverage.py v7.8.0, created at 2025-04-25 22:22 +0000
« prev ^ index » next coverage.py v7.8.0, created at 2025-04-25 22:22 +0000
1# ==================================================================================================================== #
2# _____ _ _ ____ __ _ _ _ #
3# _ __ _ |_ _|__ ___ | (_)_ __ __ _ / ___|___ _ __ / _(_) __ _ _ _ _ __ __ _| |_(_) ___ _ __ #
4# | '_ \| | | || |/ _ \ / _ \| | | '_ \ / _` || | / _ \| '_ \| |_| |/ _` | | | | '__/ _` | __| |/ _ \| '_ \ #
5# | |_) | |_| || | (_) | (_) | | | | | | (_| || |__| (_) | | | | _| | (_| | |_| | | | (_| | |_| | (_) | | | | #
6# | .__/ \__, ||_|\___/ \___/|_|_|_| |_|\__, (_)____\___/|_| |_|_| |_|\__, |\__,_|_| \__,_|\__|_|\___/|_| |_| #
7# |_| |___/ |___/ |___/ #
8# ==================================================================================================================== #
9# Authors: #
10# Patrick Lehmann #
11# #
12# License: #
13# ==================================================================================================================== #
14# Copyright 2021-2025 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:: See :ref:`high-level help <CONFIG>` for explanations and usage examples.
35"""
36from pathlib import Path
37from typing import Union, ClassVar, Iterator, Type, Optional as Nullable
39try:
40 from pyTooling.Decorators import export, readonly
41 from pyTooling.MetaClasses import ExtendedType, mixin
42 from pyTooling.Exceptions import ToolingException
43except (ImportError, ModuleNotFoundError): # pragma: no cover
44 print("[pyTooling.Configuration] Could not import from 'pyTooling.*'!")
46 try:
47 from Decorators import export, readonly
48 from MetaClasses import ExtendedType, mixin
49 from Exceptions import ToolingException
50 except (ImportError, ModuleNotFoundError) as ex: # pragma: no cover
51 print("[pyTooling.Configuration] Could not import directly!")
52 raise ex
55KeyT = Union[str, int]
56NodeT = Union["Dictionary", "Sequence"]
57ValueT = Union[NodeT, str, int, float]
60@export
61class ConfigurationException(ToolingException):
62 pass
65@export
66class Node(metaclass=ExtendedType, slots=True):
67 """Abstract node in a configuration data structure."""
69 DICT_TYPE: ClassVar[Type["Dictionary"]] #: Type reference used when instantiating new dictionaries
70 SEQ_TYPE: ClassVar[Type["Sequence"]] #: Type reference used when instantiating new sequences
71 _root: "Configuration" #: Reference to the root node.
72 _parent: "Dictionary" #: Reference to a parent node.
74 def __init__(self, root: "Configuration" = None, parent: Nullable[NodeT] = None) -> None:
75 """
76 Initializes a node.
78 :param root: Reference to the root node.
79 :param parent: Reference to the parent node.
80 """
81 self._root = root
82 self._parent = parent
84 def __len__(self) -> int: # type: ignore[empty-body]
85 """
86 Returns the number of sub-elements.
88 :returns: Number of sub-elements.
89 """
91 def __getitem__(self, key: KeyT) -> ValueT: # type: ignore[empty-body]
92 raise NotImplementedError()
94 def __setitem__(self, key: KeyT, value: ValueT) -> None: # type: ignore[empty-body]
95 raise NotImplementedError()
97 def __iter__(self) -> Iterator[ValueT]: # type: ignore[empty-body]
98 raise NotImplementedError()
100 @property
101 def Key(self) -> KeyT:
102 raise NotImplementedError()
104 @Key.setter
105 def Key(self, value: KeyT):
106 raise NotImplementedError()
108 def QueryPath(self, query: str) -> ValueT: # type: ignore[empty-body]
109 raise NotImplementedError()
112@export
113@mixin
114class Dictionary(Node):
115 """Abstract dictionary node in a configuration."""
117 def __init__(self, root: "Configuration" = None, parent: Nullable[NodeT] = None) -> None:
118 """
119 Initializes a dictionary.
121 :param root: Reference to the root node.
122 :param parent: Reference to the parent node.
123 """
124 Node.__init__(self, root, parent)
126 def __contains__(self, key: KeyT) -> bool: # type: ignore[empty-body]
127 raise NotImplementedError()
130@export
131@mixin
132class Sequence(Node):
133 """Abstract sequence node in a configuration."""
135 def __init__(self, root: "Configuration" = None, parent: Nullable[NodeT] = None) -> None:
136 """
137 Initializes a sequence.
139 :param root: Reference to the root node.
140 :param parent: Reference to the parent node.
141 """
142 Node.__init__(self, root, parent)
144 def __getitem__(self, index: int) -> ValueT: # type: ignore[empty-body]
145 raise NotImplementedError()
147 def __setitem__(self, index: int, value: ValueT) -> None: # type: ignore[empty-body]
148 raise NotImplementedError()
151setattr(Node, "DICT_TYPE", Dictionary)
152setattr(Node, "SEQ_TYPE", Sequence)
155@export
156@mixin
157class Configuration(Node):
158 """Abstract root node in a configuration."""
160 _configFile: Path
162 def __init__(self, configFile: Path, root: "Configuration" = None, parent: Nullable[NodeT] = None) -> None:
163 """
164 Initializes a configuration.
166 :param configFile: Configuration file.
167 :param root: Reference to the root node.
168 :param parent: Reference to the parent node.
169 """
170 Node.__init__(self, root, parent)
171 self._configFile = configFile
173 @readonly
174 def ConfigFile(self) -> Path:
175 return self._configFile