moat-lib-path 0.3.6__tar.gz → 0.4.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: moat-lib-path
3
- Version: 0.3.6
3
+ Version: 0.4.0
4
4
  Summary: Hierarchical path representation and manipulation for MoaT
5
5
  Maintainer-email: Matthias Urlichs <matthias@urlichs.de>
6
6
  Project-URL: homepage, https://m-o-a-t.org
@@ -1,3 +1,15 @@
1
+ moat-lib-path (0.4.0-1) unstable; urgency=medium
2
+
3
+ * New release for 26.4.40
4
+
5
+ -- Matthias Urlichs <matthias@urlichs.de> Sat, 17 Oct 2026 14:39:43 +0200
6
+
7
+ moat-lib-path (0.3.7-1) unstable; urgency=medium
8
+
9
+ * New release for 26.4.27
10
+
11
+ -- Matthias Urlichs <matthias@urlichs.de> Sat, 03 Oct 2026 14:53:15 +0200
12
+
1
13
  moat-lib-path (0.3.6-1) unstable; urgency=medium
2
14
 
3
15
  * New release for 26.4.26
@@ -22,7 +22,7 @@ name = "moat-lib-path"
22
22
  maintainers = [{email = "matthias@urlichs.de",name = "Matthias Urlichs"}]
23
23
  description='Hierarchical path representation and manipulation for MoaT'
24
24
  readme = "README.md"
25
- version = "0.3.6"
25
+ version = "0.4.0"
26
26
 
27
27
  [project.urls]
28
28
  homepage = "https://m-o-a-t.org"
@@ -21,6 +21,7 @@ from ._impl import (
21
21
  RootPath,
22
22
  S_Root,
23
23
  logger_for,
24
+ norm_root,
24
25
  path_eval,
25
26
  set_root,
26
27
  )
@@ -39,6 +40,7 @@ __all__ = [
39
40
  "RootPath",
40
41
  "S_Root",
41
42
  "logger_for",
43
+ "norm_root",
42
44
  "path_eval",
43
45
  "set_root",
44
46
  ]
@@ -78,13 +78,31 @@ _PartRE = re.compile("[^:._]+|_|:|\\.")
78
78
  _RTagRE = re.compile("^:m[^:._]+:$")
79
79
 
80
80
 
81
+ def norm_root(root: Path) -> Path:
82
+ """Flatten a possibly ``:R``-prefixed path into a plain one.
83
+
84
+ Constructing a path that equals the ambient root (e.g. ``P("a.b")``
85
+ while ``Root`` is ``a.b``) absorbs the root as a ``:R`` prefix.
86
+ Storing such a self-referential path as a root would serialize to
87
+ the empty string, so roots must always be normalized to plain
88
+ element tuples.
89
+ """
90
+ if root._prefix is None: # noqa: SLF001
91
+ return root
92
+ pref = root._prefix # noqa: SLF001
93
+ if not isinstance(pref, RootPath):
94
+ # Explicitly-relative path (``:@``): not a root absorption.
95
+ return root
96
+ return Path(*pref._data, *root._data, _warn=False) # noqa: SLF001
97
+
98
+
81
99
  def set_root(cfg) -> None:
82
100
  """
83
101
  Utility to force-set the root global.
84
102
 
85
103
  Used for testing without a "proper" context.
86
104
  """
87
- Root.set(cfg.root)
105
+ Root.set(norm_root(cfg.root))
88
106
 
89
107
 
90
108
  @total_ordering
@@ -196,14 +214,20 @@ class Path(Sequence[PathElem]):
196
214
  if mark:
197
215
  warnings.warn("Marking a path is deprecated")
198
216
 
199
- if _warn and a:
217
+ if not a:
218
+ pass
219
+ elif decoded and isinstance(a[0], RootPath) and prefix is None:
220
+ prefix = a[0]
221
+ a = a[1:]
222
+ elif isinstance(a[0], Path):
223
+ if len(a) > 1:
224
+ raise ValueError("Can't build a path this way")
225
+ a = a[0].as_tuple()
226
+ elif len(a) != 1 and _warn:
200
227
  warnings.warn(
201
228
  "Call 'Path.build(args)', not 'Path(*args)'", DeprecationWarning, stacklevel=2
202
229
  )
203
230
 
204
- if decoded and a and isinstance(a[0], RootPath) and prefix is None:
205
- prefix = a[0]
206
- a = a[1:]
207
231
  for x in a:
208
232
  if not isinstance(x, PathElemI): # pyright:ignore[reportArgumentType]
209
233
  raise TypeError(f"{x!r} cannot be in a path")
@@ -1158,80 +1182,7 @@ path_eval = _eval.eval
1158
1182
  # Here we declare our bunch of "root" variables.
1159
1183
 
1160
1184
 
1161
- class _NoResetToken:
1162
- """Token returned by :meth:`Var.set`.
1163
-
1164
- ``Var`` has no context to restore, so unlike a real ``ContextVar`` token
1165
- this carries no state: its only job is to be recognisable to
1166
- :meth:`Var.reset` so the ``set``/``reset`` protocol driven by ``ctx_as``
1167
- works uniformly for both ``ContextVar`` and ``Var``.
1168
- """
1169
-
1170
- __slots__ = ()
1171
-
1172
- def __repr__(self) -> str:
1173
- return "<Var no-reset token>"
1174
-
1175
-
1176
- _VAR_SET_TOKEN = _NoResetToken()
1177
-
1178
-
1179
- class Var:
1180
- """
1181
- This mimics a ContextVar but without the context.
1182
- """
1183
-
1184
- def __init__(self, name, default=NotGiven) -> None:
1185
- self.name = name
1186
- self.default = default
1187
- self.data = NotGiven
1188
-
1189
- def get(self):
1190
- "get current value"
1191
- if self.data is NotGiven:
1192
- raise ValueError("Not set")
1193
- return self.data
1194
-
1195
- def set(self, val, *, force: bool = False):
1196
- "set current value"
1197
- # If the root is a.b.c, a new identical value will arrive as an
1198
- # empty path with a root of a.b.c. That's bad. Thus silently don't
1199
- # update in that case.
1200
- #
1201
- # We detect that case structurally.
1202
- #
1203
- # Match by the RootPath's *key* ("R"), not by ``_var is self``:
1204
- # the ``RootPath`` embedded in @val may hold a stale ``Var`` (a
1205
- # second ``Root`` instance created, if the namespace-packaged
1206
- # ``moat.lib.path._impl`` is loaded more than once), so identity
1207
- # would fail even though @val denotes the same root.
1208
- if (
1209
- not force
1210
- and self.data is not NotGiven
1211
- and isinstance(val, Path)
1212
- and isinstance(val._prefix, RootPath) # noqa:SLF001
1213
- and val._prefix._key == "R" # noqa:SLF001
1214
- and not len(val._data) # noqa:SLF001
1215
- ):
1216
- return _VAR_SET_TOKEN
1217
- if not force and self.data is not NotGiven:
1218
- if self.data.slashed != val.slashed:
1219
- raise ValueError("Already set", self.data, val)
1220
- return _VAR_SET_TOKEN
1221
- self.data = val
1222
- return _VAR_SET_TOKEN
1223
-
1224
- def __bool__(self) -> bool:
1225
- "check if value is set"
1226
- return self.data is not NotGiven
1227
-
1228
- def reset(self, token) -> None:
1229
- "reset value; no-op"
1230
- if token is not _VAR_SET_TOKEN:
1231
- raise ValueError(token)
1232
-
1233
-
1234
- Root = Var("Root", default=None)
1185
+ Root = ContextVar[Path | None]("Root", default=None)
1235
1186
 
1236
1187
 
1237
1188
  class RootPath(Path):
@@ -1244,7 +1195,7 @@ class RootPath(Path):
1244
1195
 
1245
1196
  _mark = ""
1246
1197
 
1247
- def __init__(self, key: str, var: ContextVar[Path | None] | Var) -> None:
1198
+ def __init__(self, key: str, var: ContextVar[Path | None]) -> None:
1248
1199
  self._key = key
1249
1200
  self._var = var
1250
1201
 
@@ -1263,10 +1214,7 @@ class RootPath(Path):
1263
1214
 
1264
1215
  def __bool__(self) -> bool:
1265
1216
  "check if the contextvar is set"
1266
- if isinstance(self._var, Var):
1267
- return bool(self._var)
1268
- p = self._var.get()
1269
- return p is not None
1217
+ return self._var.get() is not None
1270
1218
 
1271
1219
  @property
1272
1220
  def _data(self) -> PathTuple: # pyright:ignore
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: moat-lib-path
3
- Version: 0.3.6
3
+ Version: 0.4.0
4
4
  Summary: Hierarchical path representation and manipulation for MoaT
5
5
  Maintainer-email: Matthias Urlichs <matthias@urlichs.de>
6
6
  Project-URL: homepage, https://m-o-a-t.org
File without changes
File without changes
File without changes
File without changes