Coverage for qml_essentials / tape.py: 100%
35 statements
« prev ^ index » next coverage.py v7.13.4, created at 2026-03-30 11:43 +0000
« prev ^ index » next coverage.py v7.13.4, created at 2026-03-30 11:43 +0000
1from __future__ import annotations
3import threading
4from contextlib import contextmanager
5from typing import TYPE_CHECKING, Iterator, List, Optional
7if TYPE_CHECKING:
8 from qml_essentials.operations import Operation
10_local = threading.local()
13def _tape_stack() -> List[List["Operation"]]:
14 """Return the per-thread tape stack, creating it on first access.
16 Returns:
17 The tape stack for the current thread (a list of tape lists).
18 """
19 if not hasattr(_local, "stack"):
20 _local.stack = []
21 return _local.stack
24def active_tape() -> Optional[List["Operation"]]:
25 """Return the innermost active tape, or ``None`` if not recording.
27 This is called from :meth:`Operation.__init__` to decide whether an
28 operation should be appended to a tape.
30 Returns:
31 The currently active tape list, or ``None``.
32 """
33 stack = _tape_stack()
34 return stack[-1] if stack else None
37@contextmanager
38def recording() -> Iterator[List["Operation"]]:
39 """Context manager that creates a fresh tape for recording operations.
41 Operations instantiated inside this block will be appended to the
42 returned tape list (via :func:`active_tape`). Nesting is supported:
43 each ``with recording()`` pushes a new tape onto the per-thread stack,
44 and the previous tape is restored on exit.
46 Yields:
47 A new empty list that will be populated with ``Operation`` instances.
48 """
49 stack = _tape_stack()
50 tape: List["Operation"] = []
51 stack.append(tape)
52 try:
53 yield tape
54 finally:
55 stack.pop()
58def _pulse_tape_stack() -> List[list]:
59 """Return the per-thread pulse-event tape stack."""
60 if not hasattr(_local, "pulse_stack"):
61 _local.pulse_stack = []
62 return _local.pulse_stack
65def active_pulse_tape() -> Optional[list]:
66 """Return the innermost active pulse-event tape, or ``None``.
68 Called from :class:`~qml_essentials.gates.PulseGates` leaf methods
69 to record :class:`~qml_essentials.drawing.PulseEvent` objects.
70 """
71 stack = _pulse_tape_stack()
72 return stack[-1] if stack else None
75@contextmanager
76def pulse_recording() -> Iterator[list]:
77 """Context manager that collects pulse events emitted by PulseGates.
79 Yields:
80 A list that will be populated with
81 :class:`~qml_essentials.drawing.PulseEvent` instances.
82 """
83 stack = _pulse_tape_stack()
84 tape: list = []
85 stack.append(tape)
86 try:
87 yield tape
88 finally:
89 stack.pop()