moat-lib-path 0.3.3__tar.gz → 0.3.6__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.3
3
+ Version: 0.3.6
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
@@ -16,7 +16,7 @@ Requires-Python: >=3.8
16
16
  Description-Content-Type: text/markdown
17
17
  License-File: LICENSE.txt
18
18
  Requires-Dist: anyio~=4.0
19
- Requires-Dist: moat-lib-proxy~=0.1.1
19
+ Requires-Dist: moat-lib-proxy~=0.1.3
20
20
  Requires-Dist: regex
21
21
  Dynamic: license-file
22
22
 
@@ -1,3 +1,33 @@
1
+ moat-lib-path (0.3.6-1) unstable; urgency=medium
2
+
3
+ * New release for 26.4.26
4
+
5
+ -- Matthias Urlichs <matthias@urlichs.de> Sat, 03 Oct 2026 12:53:26 +0200
6
+
7
+ moat-lib-path (0.3.5-1) unstable; urgency=medium
8
+
9
+ * New release for 26.4.0
10
+
11
+ -- Matthias Urlichs <matthias@urlichs.de> Tue, 29 Sep 2026 16:47:45 +0200
12
+
13
+ moat-lib-path (0.3.4-1) unstable; urgency=medium
14
+
15
+ * New release for 26.2.13
16
+
17
+ -- Matthias Urlichs <matthias@urlichs.de> Wed, 23 Sep 2026 15:11:26 +0200
18
+
19
+ moat-lib-path (0.3.3-3) unstable; urgency=medium
20
+
21
+ * New release for 26.2.3
22
+
23
+ -- Matthias Urlichs <matthias@urlichs.de> Mon, 15 Jun 2026 18:03:16 +0200
24
+
25
+ moat-lib-path (0.3.3-2) unstable; urgency=medium
26
+
27
+ * New release for 26.1.12
28
+
29
+ -- Matthias Urlichs <matthias@urlichs.de> Fri, 24 Apr 2026 15:40:58 +0200
30
+
1
31
  moat-lib-path (0.3.3-1) unstable; urgency=medium
2
32
 
3
33
  * New release for 26.1.10
@@ -2,12 +2,11 @@ Source: moat-lib-path
2
2
  Maintainer: "Matthias Urlichs" <matthias@urlichs.de>
3
3
  Section: python
4
4
  Priority: optional
5
- Build-Depends: dh-python, python3-all, debhelper (>= 13),
5
+ Build-Depends: dh-python, python3-all, debhelper (>= 13), debhelper-compat (= 13),
6
6
  python3-setuptools,
7
7
  python3-wheel,
8
8
  Standards-Version: 3.9.6
9
9
  Homepage: https://m-o-a-t.org
10
- X-DH-Compat: 13
11
10
 
12
11
  Package: python3-moat-lib-path
13
12
  Architecture: all
@@ -13,7 +13,7 @@ classifiers = [
13
13
  ]
14
14
  dependencies = [
15
15
  "anyio ~= 4.0",
16
- "moat-lib-proxy ~= 0.1.1",
16
+ "moat-lib-proxy ~= 0.1.3",
17
17
  "regex",
18
18
  ]
19
19
  keywords = ["MoaT"]
@@ -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.3"
25
+ version = "0.3.6"
26
26
 
27
27
  [project.urls]
28
28
  homepage = "https://m-o-a-t.org"
@@ -53,11 +53,11 @@ from collections.abc import Sequence
53
53
  from typing import TYPE_CHECKING, Literal, TypeAlias, cast, overload
54
54
 
55
55
  if TYPE_CHECKING:
56
- from typing import Any, Literal
56
+ from typing import Any
57
57
 
58
58
  PathTuple: TypeAlias = tuple["PathElem", ...]
59
- PathElem: TypeAlias = None | bool | int | float | str | bytes | PathTuple
60
- PathElemI: TypeAlias = None | bool | int | float | str | bytes | tuple
59
+ PathElem: TypeAlias = bool | int | float | str | bytes | PathTuple | None
60
+ PathElemI: TypeAlias = bool | int | float | str | bytes | tuple | None
61
61
 
62
62
  __all__ = [
63
63
  "PS",
@@ -78,7 +78,7 @@ _PartRE = re.compile("[^:._]+|_|:|\\.")
78
78
  _RTagRE = re.compile("^:m[^:._]+:$")
79
79
 
80
80
 
81
- def set_root(cfg):
81
+ def set_root(cfg) -> None:
82
82
  """
83
83
  Utility to force-set the root global.
84
84
 
@@ -130,6 +130,7 @@ class Path(Sequence[PathElem]):
130
130
  :Q An alias for an alternate root
131
131
  :P An alias for another alternate root
132
132
  :S An alias for yet another alternate root
133
+ :T An alias for a root used for testing
133
134
  :@ Marks this path as relative
134
135
 
135
136
  The empty path is denoted by a single colon. A path that starts or ends
@@ -175,7 +176,7 @@ class Path(Sequence[PathElem]):
175
176
  prefix: RootPath | Literal[True] | None = None,
176
177
  decoded: bool = False,
177
178
  _warn: bool = True,
178
- ):
179
+ ) -> None:
179
180
  """
180
181
  Create a path from the given elements.
181
182
 
@@ -343,7 +344,7 @@ class Path(Sequence[PathElem]):
343
344
  Slash encoding does not work with empty paths; marks are ignored.
344
345
 
345
346
  Roots are expanded when slashed. Otherwise they are represented
346
- as ``:S``, ``:P``, ``:Q``, or ``:R``.
347
+ as ``:S``, ``:P``, ``:Q``, ``:R``, or ":T".
347
348
  """
348
349
 
349
350
  def _escol(x, spaces=True):
@@ -608,10 +609,10 @@ class Path(Sequence[PathElem]):
608
609
 
609
610
  # TODO add alternate output with hex integers
610
611
 
611
- def __repr__(self):
612
+ def __repr__(self) -> str:
612
613
  return f"P({str(self)!r})"
613
614
 
614
- def __format__(self, spec):
615
+ def __format__(self, spec) -> str:
615
616
  return str(self).__format__(spec)
616
617
 
617
618
  @classmethod
@@ -649,7 +650,7 @@ class Path(Sequence[PathElem]):
649
650
  raise SyntaxError(f"Conflicting tags: {mark} vs. {mp[0][2:-1]}")
650
651
  return cls(mark=mark)
651
652
 
652
- def add(x):
653
+ def add(x) -> None:
653
654
  nonlocal part
654
655
  if not isinstance(part, str):
655
656
  part = ""
@@ -658,7 +659,7 @@ class Path(Sequence[PathElem]):
658
659
  except TypeError:
659
660
  raise SyntaxError(f"Cannot add {x!r} at {pos}") from None
660
661
 
661
- def done(new_part):
662
+ def done(new_part) -> None:
662
663
  nonlocal part
663
664
  nonlocal eval_
664
665
  if isinstance(part, str):
@@ -680,7 +681,7 @@ class Path(Sequence[PathElem]):
680
681
  res.append(part)
681
682
  part = new_part
682
683
 
683
- def new(x):
684
+ def new(x) -> None:
684
685
  nonlocal part
685
686
  if part is None:
686
687
  raise SyntaxError(f"Cannot use {part!r} at {pos}")
@@ -1065,7 +1066,7 @@ class PathShortener:
1065
1066
  Caution: this shortener ignores path marks.
1066
1067
  """
1067
1068
 
1068
- def __init__(self, prefix: Path | list | tuple = Path()):
1069
+ def __init__(self, prefix: Path | list | tuple = Path()) -> None:
1069
1070
  self.prefix = prefix if isinstance(prefix, Path) else Path.build(prefix)
1070
1071
  self.depth = len(prefix)
1071
1072
  self.path = []
@@ -1112,7 +1113,7 @@ class PathLongener:
1112
1113
 
1113
1114
  cls: type[Path] = Path
1114
1115
 
1115
- def __init__(self, prefix: Path | PathTuple = ()):
1116
+ def __init__(self, prefix: Path | PathTuple = ()) -> None:
1116
1117
  if isinstance(prefix, Path):
1117
1118
  self.cls = type(prefix)
1118
1119
  prefix = prefix.raw
@@ -1157,12 +1158,30 @@ path_eval = _eval.eval
1157
1158
  # Here we declare our bunch of "root" variables.
1158
1159
 
1159
1160
 
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
+
1160
1179
  class Var:
1161
1180
  """
1162
1181
  This mimics a ContextVar but without the context.
1163
1182
  """
1164
1183
 
1165
- def __init__(self, name, default=NotGiven):
1184
+ def __init__(self, name, default=NotGiven) -> None:
1166
1185
  self.name = name
1167
1186
  self.default = default
1168
1187
  self.data = NotGiven
@@ -1178,20 +1197,37 @@ class Var:
1178
1197
  # If the root is a.b.c, a new identical value will arrive as an
1179
1198
  # empty path with a root of a.b.c. That's bad. Thus silently don't
1180
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
1181
1217
  if not force and self.data is not NotGiven:
1182
1218
  if self.data.slashed != val.slashed:
1183
1219
  raise ValueError("Already set", self.data, val)
1184
- return 42
1220
+ return _VAR_SET_TOKEN
1185
1221
  self.data = val
1186
- return 42
1222
+ return _VAR_SET_TOKEN
1187
1223
 
1188
- def __bool__(self):
1224
+ def __bool__(self) -> bool:
1189
1225
  "check if value is set"
1190
1226
  return self.data is not NotGiven
1191
1227
 
1192
- def reset(self, token):
1228
+ def reset(self, token) -> None:
1193
1229
  "reset value; no-op"
1194
- if token != 42:
1230
+ if token is not _VAR_SET_TOKEN:
1195
1231
  raise ValueError(token)
1196
1232
 
1197
1233
 
@@ -1208,11 +1244,11 @@ class RootPath(Path):
1208
1244
 
1209
1245
  _mark = ""
1210
1246
 
1211
- def __init__(self, key: str, var: ContextVar[Path | None] | Var):
1247
+ def __init__(self, key: str, var: ContextVar[Path | None] | Var) -> None:
1212
1248
  self._key = key
1213
1249
  self._var = var
1214
1250
 
1215
- def __repr__(self):
1251
+ def __repr__(self) -> str:
1216
1252
  return f"{self._key}={super().__repr__()}"
1217
1253
 
1218
1254
  @property
@@ -1245,7 +1281,7 @@ _root = RootPath("R", Root)
1245
1281
  as_proxy("R", _root)
1246
1282
  _Roots = {"R": _root}
1247
1283
 
1248
- for _idx in "SPQ": # and R. Yes I know.
1284
+ for _idx in "SPQT": # and R. Yes I know.
1249
1285
  _name = f"{_idx}_Root"
1250
1286
  _ctx = ContextVar[Path | None](_name, default=None)
1251
1287
  _path = RootPath(_idx, _ctx)
@@ -1262,5 +1298,6 @@ if TYPE_CHECKING:
1262
1298
  S_Root = ContextVar[Path | None]("S_Root", default=None)
1263
1299
  P_Root = ContextVar[Path | None]("P_Root", default=None)
1264
1300
  Q_Root = ContextVar[Path | None]("Q_Root", default=None)
1301
+ T_Root = ContextVar[Path | None]("T_Root", default=None)
1265
1302
 
1266
- __all__ += ["P_Root", "Q_Root", "S_Root"]
1303
+ __all__ += ["P_Root", "Q_Root", "S_Root", "T_Root"]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: moat-lib-path
3
- Version: 0.3.3
3
+ Version: 0.3.6
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
@@ -16,7 +16,7 @@ Requires-Python: >=3.8
16
16
  Description-Content-Type: text/markdown
17
17
  License-File: LICENSE.txt
18
18
  Requires-Dist: anyio~=4.0
19
- Requires-Dist: moat-lib-proxy~=0.1.1
19
+ Requires-Dist: moat-lib-proxy~=0.1.3
20
20
  Requires-Dist: regex
21
21
  Dynamic: license-file
22
22
 
@@ -0,0 +1,3 @@
1
+ anyio~=4.0
2
+ moat-lib-proxy~=0.1.3
3
+ regex
@@ -1,3 +0,0 @@
1
- anyio~=4.0
2
- moat-lib-proxy~=0.1.1
3
- regex
File without changes
File without changes
File without changes
File without changes