Coverage for pyTooling/GenericPath/__init__.py: 97%
55 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-22 21:29 +0000
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-22 21:29 +0000
1# ==================================================================================================================== #
2# _____ _ _ ____ _ ____ _ _ #
3# _ __ _ |_ _|__ ___ | (_)_ __ __ _ / ___| ___ _ __ ___ _ __(_) ___| _ \ __ _| |_| |__ #
4# | '_ \| | | || |/ _ \ / _ \| | | '_ \ / _` || | _ / _ \ '_ \ / _ \ '__| |/ __| |_) / _` | __| '_ \ #
5# | |_) | |_| || | (_) | (_) | | | | | | (_| || |_| | __/ | | | __/ | | | (__| __/ (_| | |_| | | | #
6# | .__/ \__, ||_|\___/ \___/|_|_|_| |_|\__, (_)____|\___|_| |_|\___|_| |_|\___|_| \__,_|\__|_| |_| #
7# |_| |___/ |___/ #
8# ==================================================================================================================== #
9# Authors: #
10# Patrick Lehmann #
11# #
12# License: #
13# ==================================================================================================================== #
14# Copyright 2017-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"""
32A generic path to derive domain specific path libraries.
34.. seealso::
36 :mod:`pyTooling.GenericPath.URL`
37 |rarr| A URL as a domain-specific path.
38 :mod:`pyTooling.Configuration`
39 |rarr| Path expressions addressing a node in a configuration.
40"""
41from __future__ import annotations
43from typing import ClassVar, Optional as Nullable
45from pyTooling.Decorators import export
46from pyTooling.MetaClasses import ExtendedType
49@export
50class Base(metaclass=ExtendedType, mixin=True):
51 """Base-mixin-class for all :mod:`pyTooling.GenericPath` path elements."""
53 DELIMITER: ClassVar[str] = "/" #: Path element delimiter sign.
55 _parent: Nullable[Base] #: Reference to the parent object.
57 def __init__(self, parent: Nullable[Base] = None) -> None:
58 """
59 Initialize the base-mixin-class with a parent reference.
61 :param parent: Optional, parent reference.
62 """
63 self._parent = parent
66@export
67class RootMixIn(Base, mixin=True):
68 """Mixin-class for root elements in a path system."""
70 def __init__(self) -> None:
71 """
72 Initialize the mixin-class for a root element.
73 """
74 super().__init__(None)
77@export
78class ElementMixIn(Base, mixin=True):
79 """Mixin-class for elements in a path system."""
81 _elementName: str #: Name of the path element.
83 def __init__(self, parent: Base, elementName: str) -> None:
84 """
85 Initialize the mixin-class for a path element.
87 :param parent: Optional, reference to a parent path element.
88 :param elementName: Name of the path element.
89 """
90 super().__init__(parent)
92 self._elementName = elementName
94 def __str__(self) -> str:
95 """
96 Return a string representation of this path element.
98 :returns: The element's name.
99 """
100 return self._elementName
103@export
104class PathMixIn(metaclass=ExtendedType, mixin=True):
105 """Mixin-class for a path."""
107 ELEMENT_DELIMITER: ClassVar[str] = "/" #: Path element delimiter sign.
108 ROOT_DELIMITER: ClassVar[str] = "/" #: Root element delimiter sign.
110 _isAbsolute: bool #: True, if the path is absolute.
111 _elements: list[ElementMixIn] #: List of path elements.
113 def __init__(self, elements: list[ElementMixIn], isAbsolute: bool) -> None:
114 """
115 Initialize the mixin-class for a path.
117 :param elements: Reference to a parent path element.
118 :param isAbsolute: ``True``, if the path is absolute, otherwise ``False``.
119 """
120 self._isAbsolute = isAbsolute
121 self._elements = elements
123 def __len__(self) -> int:
124 """
125 Returns the number of path elements.
127 :returns: Number of path elements.
128 """
129 return len(self._elements)
131 def __str__(self) -> str:
132 """
133 Return a string representation of this path.
135 :returns: The path's elements, joined by the delimiter, prefixed by the root delimiter if the path is absolute.
136 """
137 result = self.ROOT_DELIMITER if self._isAbsolute else ""
139 if len(self._elements) > 0: 139 ↛ 145line 139 didn't jump to line 145 because the condition on line 139 was always true
140 result = result + str(self._elements[0])
142 for element in self._elements[1:]:
143 result = result + self.ELEMENT_DELIMITER + str(element)
145 return result
147 @classmethod
148 def Parse(
149 cls,
150 path: str,
151 root: RootMixIn,
152 pathCls: type[PathMixIn],
153 elementCls: type[ElementMixIn]
154 ) -> PathMixIn:
155 """
156 Parses a string representation of a path and returns a path instance.
158 :param path: Path to be parsed.
159 :param root: Root element the parsed path is relative to.
160 :param pathCls: Type used to create the path.
161 :param elementCls: Type used to create the path elements.
162 :returns: A path instance of type ``pathCls``.
163 """
164 if path.startswith(cls.ROOT_DELIMITER):
165 isAbsolute = True
166 path = path[len(cls.ELEMENT_DELIMITER):]
167 else:
168 isAbsolute = False
170 parent = root
171 elements = []
172 for part in path.split(cls.ELEMENT_DELIMITER):
173 element = elementCls(parent, part)
174 parent = element
175 elements.append(element)
177 return pathCls(elements, isAbsolute)
180@export
181class SystemMixIn(metaclass=ExtendedType, mixin=True):
182 """Mixin-class for a path system."""