kaparoo-python 0.6.0__tar.gz → 0.7.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.
Files changed (38) hide show
  1. {kaparoo_python-0.6.0 → kaparoo_python-0.7.0}/PKG-INFO +2 -2
  2. {kaparoo_python-0.6.0 → kaparoo_python-0.7.0}/README.md +1 -1
  3. {kaparoo_python-0.6.0 → kaparoo_python-0.7.0}/kaparoo/filesystem/README.md +22 -1
  4. {kaparoo_python-0.6.0 → kaparoo_python-0.7.0}/kaparoo/filesystem/__init__.py +2 -0
  5. {kaparoo_python-0.6.0 → kaparoo_python-0.7.0}/kaparoo/filesystem/utils.py +53 -1
  6. {kaparoo_python-0.6.0 → kaparoo_python-0.7.0}/kaparoo/utils/README.md +14 -14
  7. {kaparoo_python-0.6.0 → kaparoo_python-0.7.0}/kaparoo/utils/__init__.py +3 -3
  8. {kaparoo_python-0.6.0 → kaparoo_python-0.7.0}/kaparoo/utils/timer.py +32 -32
  9. {kaparoo_python-0.6.0 → kaparoo_python-0.7.0}/pyproject.toml +1 -1
  10. {kaparoo_python-0.6.0 → kaparoo_python-0.7.0}/LICENSE +0 -0
  11. {kaparoo_python-0.6.0 → kaparoo_python-0.7.0}/kaparoo/__init__.py +0 -0
  12. {kaparoo_python-0.6.0 → kaparoo_python-0.7.0}/kaparoo/data/README.md +0 -0
  13. {kaparoo_python-0.6.0 → kaparoo_python-0.7.0}/kaparoo/data/__init__.py +0 -0
  14. {kaparoo_python-0.6.0 → kaparoo_python-0.7.0}/kaparoo/data/sequences/__init__.py +0 -0
  15. {kaparoo_python-0.6.0 → kaparoo_python-0.7.0}/kaparoo/data/sequences/base.py +0 -0
  16. {kaparoo_python-0.6.0 → kaparoo_python-0.7.0}/kaparoo/data/sequences/composers.py +0 -0
  17. {kaparoo_python-0.6.0 → kaparoo_python-0.7.0}/kaparoo/data/sequences/templates.py +0 -0
  18. {kaparoo_python-0.6.0 → kaparoo_python-0.7.0}/kaparoo/data/sequences/utils.py +0 -0
  19. {kaparoo_python-0.6.0 → kaparoo_python-0.7.0}/kaparoo/filesystem/directory.py +0 -0
  20. {kaparoo_python-0.6.0 → kaparoo_python-0.7.0}/kaparoo/filesystem/exceptions.py +0 -0
  21. {kaparoo_python-0.6.0 → kaparoo_python-0.7.0}/kaparoo/filesystem/existence.py +0 -0
  22. {kaparoo_python-0.6.0 → kaparoo_python-0.7.0}/kaparoo/filesystem/search/README.md +0 -0
  23. {kaparoo_python-0.6.0 → kaparoo_python-0.7.0}/kaparoo/filesystem/search/__init__.py +0 -0
  24. {kaparoo_python-0.6.0 → kaparoo_python-0.7.0}/kaparoo/filesystem/search/classes.py +0 -0
  25. {kaparoo_python-0.6.0 → kaparoo_python-0.7.0}/kaparoo/filesystem/search/deprecated.py +0 -0
  26. {kaparoo_python-0.6.0 → kaparoo_python-0.7.0}/kaparoo/filesystem/search/filters/__init__.py +0 -0
  27. {kaparoo_python-0.6.0 → kaparoo_python-0.7.0}/kaparoo/filesystem/search/filters/base.py +0 -0
  28. {kaparoo_python-0.6.0 → kaparoo_python-0.7.0}/kaparoo/filesystem/search/filters/logical.py +0 -0
  29. {kaparoo_python-0.6.0 → kaparoo_python-0.7.0}/kaparoo/filesystem/search/filters/multi_pattern.py +0 -0
  30. {kaparoo_python-0.6.0 → kaparoo_python-0.7.0}/kaparoo/filesystem/search/filters/pattern.py +0 -0
  31. {kaparoo_python-0.6.0 → kaparoo_python-0.7.0}/kaparoo/filesystem/search/filters/types.py +0 -0
  32. {kaparoo_python-0.6.0 → kaparoo_python-0.7.0}/kaparoo/filesystem/search/filters/utils.py +0 -0
  33. {kaparoo_python-0.6.0 → kaparoo_python-0.7.0}/kaparoo/filesystem/search/wrappers.py +0 -0
  34. {kaparoo_python-0.6.0 → kaparoo_python-0.7.0}/kaparoo/filesystem/staged.py +0 -0
  35. {kaparoo_python-0.6.0 → kaparoo_python-0.7.0}/kaparoo/filesystem/types.py +0 -0
  36. {kaparoo_python-0.6.0 → kaparoo_python-0.7.0}/kaparoo/py.typed +0 -0
  37. {kaparoo_python-0.6.0 → kaparoo_python-0.7.0}/kaparoo/utils/aggregate.py +0 -0
  38. {kaparoo_python-0.6.0 → kaparoo_python-0.7.0}/kaparoo/utils/optional.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kaparoo-python
3
- Version: 0.6.0
3
+ Version: 0.7.0
4
4
  Summary: Personally common and useful Python features
5
5
  Keywords: filesystem,pathlib,paths,utilities
6
6
  Author: Jaewoo Park
@@ -65,7 +65,7 @@ hook for custom filter kinds.
65
65
 
66
66
  ### [`kaparoo.utils`](https://github.com/kaparoo/kaparoo-python/tree/main/kaparoo/utils)
67
67
 
68
- `Timer` / `SegmentTimer` context-manager-and-decorator timers (with
68
+ `Timer` / `SpanTimer` context-manager-and-decorator timers (with
69
69
  `lap`-split and `measure`-block timings); `Aggregator` for nested,
70
70
  pluggable metric aggregation (the batch → epoch → run pattern;
71
71
  experimental); plus a small family of helpers for working with
@@ -44,7 +44,7 @@ hook for custom filter kinds.
44
44
 
45
45
  ### [`kaparoo.utils`](https://github.com/kaparoo/kaparoo-python/tree/main/kaparoo/utils)
46
46
 
47
- `Timer` / `SegmentTimer` context-manager-and-decorator timers (with
47
+ `Timer` / `SpanTimer` context-manager-and-decorator timers (with
48
48
  `lap`-split and `measure`-block timings); `Aggregator` for nested,
49
49
  pluggable metric aggregation (the batch → epoch → run pattern;
50
50
  experimental); plus a small family of helpers for working with
@@ -10,7 +10,7 @@
10
10
  `dir_not_empty(s)` with validation, plus `_unsafe` variants that skip
11
11
  pre-checks
12
12
  - [`utils`](./utils.py) — `stringify_path(s)`, `wrap_path(s)`,
13
- `reserve_path(s)`
13
+ `reserve_path(s)`, `ensure_file_extension`
14
14
  - [`staged`](./staged.py) — `StagedFile` / `StagedDirectory`, safe
15
15
  (atomic) writers usable as a context manager or explicitly
16
16
  - [`exceptions`](./exceptions.py) — `DirectoryNotFoundError`, `NotAFileError`
@@ -116,6 +116,27 @@ stringify_paths(["data/a.txt", "data/b.txt"], after="data") # ["a.txt", "b.txt"
116
116
  wrap_path("logs", prepend="var", append="server.log") # var/logs/server.log
117
117
  ```
118
118
 
119
+ `ensure_file_extension` is a pure (no filesystem) extension check: it
120
+ requires a `.<ext>` final suffix, raising `ValueError` otherwise. `ext` may
121
+ be a single extension or an iterable of acceptable ones; the leading dot is
122
+ optional and the match is case-insensitive. `add=True` mirrors `make` on
123
+ `ensure_dir_exists` — it appends the (first) extension when the path has no
124
+ suffix (`np.save`-style) instead of raising; a *wrong* suffix still raises.
125
+
126
+ ```python
127
+ from kaparoo.filesystem import ensure_file_extension
128
+
129
+ ensure_file_extension("data.bin", "bin") # Path("data.bin")
130
+ ensure_file_extension("data.txt", "bin") # ValueError
131
+ ensure_file_extension("out/00000_phase", "bin") # ValueError (no suffix)
132
+
133
+ # Any of several accepted extensions:
134
+ ensure_file_extension("img.jpeg", ("jpg", "jpeg", "png")) # Path("img.jpeg")
135
+
136
+ ensure_file_extension("out/00000_phase", "bin", add=True) # Path("out/00000_phase.bin")
137
+ ensure_file_extension("out/data.txt", "bin", add=True) # ValueError (wrong suffix)
138
+ ```
139
+
119
140
  ## Reserving a destination
120
141
 
121
142
  `reserve_path` guards a path that should *not* yet exist, so you don't
@@ -16,6 +16,7 @@ __all__ = (
16
16
  "ensure_dir_exists",
17
17
  "ensure_dirs_exist",
18
18
  "ensure_file_exists",
19
+ "ensure_file_extension",
19
20
  "ensure_files_exist",
20
21
  "ensure_path_exists",
21
22
  "ensure_paths_exist",
@@ -79,6 +80,7 @@ from kaparoo.filesystem.search import (
79
80
  )
80
81
  from kaparoo.filesystem.staged import StagedDirectory, StagedFile
81
82
  from kaparoo.filesystem.utils import (
83
+ ensure_file_extension,
82
84
  reserve_path,
83
85
  reserve_paths,
84
86
  stringify_path,
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  __all__ = (
4
+ "ensure_file_extension",
4
5
  "reserve_path",
5
6
  "reserve_paths",
6
7
  "stringify_path",
@@ -15,7 +16,7 @@ from pathlib import Path
15
16
  from typing import TYPE_CHECKING, overload
16
17
 
17
18
  if TYPE_CHECKING:
18
- from collections.abc import Sequence
19
+ from collections.abc import Iterable, Sequence
19
20
  from typing import Literal
20
21
 
21
22
  from kaparoo.filesystem.types import StrPath, StrPaths
@@ -357,3 +358,54 @@ def reserve_paths(
357
358
  reserve_path(p, exist_ok=exist_ok, make_parents=make_parents) for p in paths
358
359
  ]
359
360
  return stringify_paths(paths) if stringify else paths
361
+
362
+
363
+ def ensure_file_extension(
364
+ path: StrPath, ext: str | Iterable[str], *, add: bool = False
365
+ ) -> Path:
366
+ """Return `path` as a `Path`, requiring a case-insensitive `.<ext>` suffix.
367
+
368
+ A pure path check that never touches the filesystem. `ext` is a single
369
+ extension or an iterable of acceptable ones (e.g. `("jpg", "jpeg")`); the
370
+ leading dot on each is optional, so `"bin"` and `".bin"` behave the same.
371
+ Only the final suffix is considered: `archive.tar.gz` matches `ext="gz"`,
372
+ not `ext="tar.gz"`.
373
+
374
+ `add` mirrors `make` on `ensure_dir_exists`: when False (the default) a
375
+ path with no suffix raises like any other mismatch; when True, the missing
376
+ suffix is appended -- the *first* of `ext` when several are given, so pass
377
+ an ordered list/tuple if that matters. A *wrong* suffix always raises,
378
+ regardless of `add`.
379
+
380
+ Args:
381
+ path: The path to check.
382
+ ext: The required extension, or an iterable of acceptable ones, each
383
+ with or without a leading dot.
384
+ add: Whether to append the (first) extension when `path` has no
385
+ suffix, instead of raising. Defaults to False.
386
+
387
+ Returns:
388
+ The path as a Path object, guaranteed to end in an accepted `.<ext>`.
389
+
390
+ Raises:
391
+ ValueError: If `ext` is empty, or `path`'s final suffix is none of the
392
+ accepted extensions -- except the no-suffix case resolved by
393
+ `add=True`.
394
+ """
395
+ exts = [ext] if isinstance(ext, str) else list(ext)
396
+ exts = [e.removeprefix(".") for e in exts]
397
+
398
+ if not exts:
399
+ msg = "ext must name at least one extension"
400
+ raise ValueError(msg)
401
+
402
+ path = Path(path)
403
+ if add and not path.suffix:
404
+ return path.with_suffix(f".{exts[0]}")
405
+
406
+ if path.suffix.lower() not in {f".{e.lower()}" for e in exts}:
407
+ wanted = " / ".join(f".{e}" for e in exts)
408
+ msg = f"{path.name} must have a {wanted} extension (got {path.suffix!r})"
409
+ raise ValueError(msg)
410
+
411
+ return path
@@ -4,7 +4,7 @@ Small, focused helpers — not enough material for their own packages.
4
4
 
5
5
  ## Modules
6
6
 
7
- - [`timer`](./timer.py) — `Timer`, `SegmentTimer`, `SegmentRecord`
7
+ - [`timer`](./timer.py) — `Timer`, `SpanTimer`, `SpanRecord`
8
8
  - [`aggregate`](./aggregate.py) — `Aggregator` + the `Reduction` family
9
9
  (`Mean`, `Sum`, `Min`, `Max`, `Last`, `Fold`)
10
10
  - [`optional`](./optional.py) — helpers for `T | None` values
@@ -47,29 +47,29 @@ with Timer("ms") as t:
47
47
  do_work()
48
48
  ```
49
49
 
50
- ## SegmentTimer
50
+ ## SpanTimer
51
51
 
52
- `SegmentTimer` extends `Timer` with named time *segments*. Each segment
53
- is a `SegmentRecord` (a `TypedDict` with `label`, `duration`,
52
+ `SpanTimer` extends `Timer` with named time *spans*. Each span
53
+ is a `SpanRecord` (a `TypedDict` with `label`, `duration`,
54
54
  `total_time`) and is produced in one of two ways:
55
55
 
56
56
  - **`lap(label)` — split.** Each lap's `duration` is the time since the
57
57
  previous lap (or the start), so every instant belongs to exactly one
58
- segment.
58
+ span.
59
59
  - **`measure(label)` — stopwatch.** Times only the wrapped block; time
60
- spent outside any `measure` block is attributed to no segment.
60
+ spent outside any `measure` block is attributed to no span.
61
61
 
62
62
  ```python
63
- from kaparoo.utils.timer import SegmentTimer
63
+ from kaparoo.utils.timer import SpanTimer
64
64
 
65
- with SegmentTimer("ms", ndigits=1) as st:
65
+ with SpanTimer("ms", ndigits=1) as st:
66
66
  step_a()
67
67
  st.lap("A") # split: time since start
68
68
  idle() # NOT counted by the next measure
69
69
  with st.measure("B"): # stopwatch: only this block
70
70
  step_b()
71
71
 
72
- # Per-segment details:
72
+ # Per-span details:
73
73
  for record in st.records:
74
74
  print(record["label"], record["duration"])
75
75
 
@@ -80,7 +80,7 @@ print(st.elapsed) # total wall time of the `with` block
80
80
 
81
81
  ### `lap` vs `measure`
82
82
 
83
- `lap` splits the timeline into contiguous segments — the gap before a
83
+ `lap` splits the timeline into contiguous spans — the gap before a
84
84
  lap is folded into that lap. `measure` brackets a region and ignores
85
85
  everything outside it, so untimed work between blocks is excluded from
86
86
  `summary`. Pick `lap` for back-to-back phases, `measure` for discrete
@@ -88,16 +88,16 @@ operations interleaved with untimed work. Pauses inside either are
88
88
  excluded; a `measure` block that raises records nothing.
89
89
 
90
90
  `measure` doubles as a decorator (every decorated call records one
91
- segment, as long as the timer is running when it is called):
91
+ span, as long as the timer is running when it is called):
92
92
 
93
93
  ```python
94
- st = SegmentTimer("ms")
94
+ st = SpanTimer("ms")
95
95
 
96
96
  @st.measure("load")
97
97
  def load() -> None: ...
98
98
 
99
99
  with st:
100
- load() # records a "load" segment each call
100
+ load() # records a "load" span each call
101
101
  ```
102
102
 
103
103
  ### Same-label policies
@@ -111,7 +111,7 @@ with st:
111
111
  The policy applies to both `lap` and `measure`:
112
112
 
113
113
  ```python
114
- with SegmentTimer(on_same_label="separate") as st:
114
+ with SpanTimer(on_same_label="separate") as st:
115
115
  st.lap("A")
116
116
  st.lap("A") # recorded as "A (2)"
117
117
  st.lap("A") # recorded as "A (3)"
@@ -6,8 +6,8 @@ __all__ = (
6
6
  "Mean",
7
7
  "Min",
8
8
  "Reduction",
9
- "SegmentRecord",
10
- "SegmentTimer",
9
+ "SpanRecord",
10
+ "SpanTimer",
11
11
  "Std",
12
12
  "Sum",
13
13
  "Timer",
@@ -42,4 +42,4 @@ from kaparoo.utils.optional import (
42
42
  unwrap_or_factories,
43
43
  unwrap_or_factory,
44
44
  )
45
- from kaparoo.utils.timer import SegmentRecord, SegmentTimer, Timer
45
+ from kaparoo.utils.timer import SpanRecord, SpanTimer, Timer
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- __all__ = ("SegmentRecord", "SegmentTimer", "Timer")
3
+ __all__ = ("SpanRecord", "SpanTimer", "Timer")
4
4
 
5
5
  import time
6
6
  from abc import ABC, abstractmethod
@@ -22,20 +22,20 @@ _SCALES: dict[str, float] = {"s": 1e-9, "ms": 1e-6, "us": 1e-3, "ns": 1.0}
22
22
  _LABEL_POLICIES: frozenset[str] = frozenset({"merge", "separate", "reject"})
23
23
 
24
24
 
25
- class SegmentRecord(TypedDict):
26
- """A single timing record produced by `SegmentTimer`.
25
+ class SpanRecord(TypedDict):
26
+ """A single timing record produced by `SpanTimer`.
27
27
 
28
- A segment is produced either by `lap` (the span since the previous
28
+ A span is produced either by `lap` (the span since the previous
29
29
  lap) or by `measure` (the span of a wrapped block).
30
30
 
31
31
  Attributes:
32
- label: The segment's name. May carry a " (N)" suffix when produced
32
+ label: The span's name. May carry a " (N)" suffix when produced
33
33
  under `on_same_label="separate"`.
34
- duration: Length of this segment, in the timer's `unit` and rounded
34
+ duration: Length of this span, in the timer's `unit` and rounded
35
35
  by `ndigits` if given. For `lap`, the time since the previous
36
36
  lap (or the timer start); for `measure`, the wrapped block's
37
37
  duration.
38
- total_time: Time elapsed from the timer start to this segment's end,
38
+ total_time: Time elapsed from the timer start to this span's end,
39
39
  in the timer's `unit` and rounded by `ndigits` if given.
40
40
  """
41
41
 
@@ -45,14 +45,14 @@ class SegmentRecord(TypedDict):
45
45
 
46
46
 
47
47
  class BaseTimer(ContextDecorator, ABC):
48
- """Abstract base for `Timer` and `SegmentTimer`.
48
+ """Abstract base for `Timer` and `SpanTimer`.
49
49
 
50
50
  Provides the shared timing machinery: unit/precision formatting,
51
51
  `pause`/`resume`/`suspend`, and a context-manager protocol that
52
52
  auto-resumes a paused timer on exit. Subclasses implement `_finalize`
53
53
  to record their final result, and may override the `_reset` hook to
54
54
  clear per-`with`-block state. Not part of the public API -- prefer
55
- `Timer` or `SegmentTimer`.
55
+ `Timer` or `SpanTimer`.
56
56
 
57
57
  An instance is reusable but **not reentrant**: a single instance must
58
58
  not be nested within itself -- including as a decorator on a recursive
@@ -249,30 +249,30 @@ class Timer(BaseTimer):
249
249
  self.elapsed = self._format_time(elapsed_ns)
250
250
 
251
251
 
252
- class SegmentTimer(BaseTimer):
253
- """A timer recording named time segments within one `with` block.
252
+ class SpanTimer(BaseTimer):
253
+ """A timer recording named time spans within one `with` block.
254
254
 
255
- Usable as a context manager or as a decorator. Segments are recorded
255
+ Usable as a context manager or as a decorator. Spans are recorded
256
256
  in two complementary ways:
257
257
 
258
258
  - `lap(label)` splits the timeline: each lap's `duration` is the
259
259
  time since the previous lap (or the start), so every instant is
260
- attributed to exactly one segment.
260
+ attributed to exactly one span.
261
261
  - `measure(label)` brackets a region (as a `with` block or a
262
262
  decorator): only the wrapped span is recorded, and time spent
263
- outside any `measure` block is attributed to no segment.
263
+ outside any `measure` block is attributed to no span.
264
264
 
265
265
  Both feed the same `records` / `summary`, honour `on_same_label`, and
266
266
  exclude paused intervals.
267
267
 
268
268
  Attributes:
269
269
  on_same_label: The same-label handling policy (see `__init__`).
270
- records: The recorded segments, in call order.
270
+ records: The recorded spans, in call order.
271
271
  elapsed: The total measured duration, from start to exit.
272
272
  Defaults to 0.0 until the first exit.
273
273
 
274
274
  Example:
275
- with SegmentTimer("ms", ndigits=1) as st:
275
+ with SpanTimer("ms", ndigits=1) as st:
276
276
  step_a()
277
277
  st.lap("A") # split: time since start
278
278
  with st.measure("B"): # block: only this region
@@ -288,7 +288,7 @@ class SegmentTimer(BaseTimer):
288
288
  ndigits: int | None = None,
289
289
  on_same_label: LabelPolicy = "merge",
290
290
  ) -> None:
291
- """Initialize the lap timer.
291
+ """Initialize the span timer.
292
292
 
293
293
  Args:
294
294
  unit: The time unit for reported values. One of "s", "ms", "us",
@@ -313,7 +313,7 @@ class SegmentTimer(BaseTimer):
313
313
  raise ValueError(msg)
314
314
 
315
315
  self.on_same_label = on_same_label
316
- self.records: list[SegmentRecord] = []
316
+ self.records: list[SpanRecord] = []
317
317
  self.elapsed: float = 0.0
318
318
 
319
319
  self._last_time: int = 0
@@ -323,8 +323,8 @@ class SegmentTimer(BaseTimer):
323
323
  def summary(self) -> dict[str, float]:
324
324
  """Per-label sum of `duration` across `records`.
325
325
 
326
- Only recorded segments count: time outside every `lap` / `measure`
327
- segment (e.g. after the last `lap`, or between `measure` blocks) is
326
+ Only recorded spans count: time outside every `lap` / `measure`
327
+ span (e.g. after the last `lap`, or between `measure` blocks) is
328
328
  not included. Each record's `duration` is already rounded by
329
329
  `ndigits` (when set); this property sums those rounded values and
330
330
  rounds the sum once more.
@@ -377,11 +377,11 @@ class SegmentTimer(BaseTimer):
377
377
  else label
378
378
  )
379
379
 
380
- def _make_record(self, label: str) -> SegmentRecord:
380
+ def _make_record(self, label: str) -> SpanRecord:
381
381
  """Build a record stamped with the current time and advance `_last_time`."""
382
382
  current_time = time.perf_counter_ns()
383
383
 
384
- record: SegmentRecord = {
384
+ record: SpanRecord = {
385
385
  "label": label,
386
386
  "duration": self._format_time(current_time - self._last_time),
387
387
  "total_time": self._format_time(current_time - self._start_time),
@@ -415,12 +415,12 @@ class SegmentTimer(BaseTimer):
415
415
 
416
416
  @contextmanager
417
417
  def measure(self, label: str = "Block") -> Iterator[None]:
418
- """Record a segment covering only the wrapped block (stopwatch style).
418
+ """Record a span covering only the wrapped block (stopwatch style).
419
419
 
420
- Unlike `lap`, which splits the timeline into contiguous segments,
420
+ Unlike `lap`, which splits the timeline into contiguous spans,
421
421
  `measure` times only the wrapped region; time spent outside any
422
- `measure` block is attributed to no segment. Pauses inside the
423
- block are excluded. A segment is recorded only on clean exit -- if
422
+ `measure` block is attributed to no span. Pauses inside the
423
+ block are excluded. A span is recorded only on clean exit -- if
424
424
  the block raises, nothing is recorded and the exception propagates.
425
425
  Repeated labels follow `on_same_label`, exactly as `lap`. Do not
426
426
  nest `measure` blocks: each resets the shared baseline, so an outer
@@ -428,23 +428,23 @@ class SegmentTimer(BaseTimer):
428
428
 
429
429
  Because `contextmanager` results are also `ContextDecorator`s, the
430
430
  returned object doubles as a decorator (every decorated call
431
- records one segment, provided the timer is running when called):
431
+ records one span, provided the timer is running when called):
432
432
 
433
- st = SegmentTimer("ms")
433
+ st = SpanTimer("ms")
434
434
 
435
435
  @st.measure("load")
436
436
  def load() -> None: ...
437
437
 
438
438
  with st:
439
- load() # records a "load" segment
439
+ load() # records a "load" span
440
440
  with st.measure("parse"):
441
- parse() # records a "parse" segment
441
+ parse() # records a "parse" span
442
442
 
443
443
  Args:
444
- label: The segment's name. Defaults to "Block".
444
+ label: The span's name. Defaults to "Block".
445
445
 
446
446
  Yields:
447
- None. The wrapped block runs while the segment is timed.
447
+ None. The wrapped block runs while the span is timed.
448
448
 
449
449
  Raises:
450
450
  RuntimeError: If the timer has not been started, or is paused on
@@ -12,7 +12,7 @@ build-backend = "uv_build"
12
12
 
13
13
  [project]
14
14
  name = "kaparoo-python"
15
- version = "0.6.0"
15
+ version = "0.7.0"
16
16
  description = "Personally common and useful Python features"
17
17
  readme = "README.md"
18
18
  requires-python = ">=3.14"
File without changes