• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

agronholm / anyio / 10671450700

02 Sep 2024 06:05PM UTC coverage: 91.77% (+0.002%) from 91.768%
10671450700

push

github

agronholm
Fixed pytest plugin swallowing most exception groups

1 of 1 new or added line in 1 file covered. (100.0%)

4594 of 5006 relevant lines covered (91.77%)

9.51 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.51
/src/anyio/lowlevel.py
1
from __future__ import annotations
11✔
2

3
import enum
11✔
4
from dataclasses import dataclass
11✔
5
from typing import Any, Generic, Literal, TypeVar, overload
11✔
6
from weakref import WeakKeyDictionary
11✔
7

8
from ._core._eventloop import get_async_backend
11✔
9

10
T = TypeVar("T")
11✔
11
D = TypeVar("D")
11✔
12

13

14
async def checkpoint() -> None:
11✔
15
    """
16
    Check for cancellation and allow the scheduler to switch to another task.
17

18
    Equivalent to (but more efficient than)::
19

20
        await checkpoint_if_cancelled()
21
        await cancel_shielded_checkpoint()
22

23

24
    .. versionadded:: 3.0
25

26
    """
27
    await get_async_backend().checkpoint()
11✔
28

29

30
async def checkpoint_if_cancelled() -> None:
11✔
31
    """
32
    Enter a checkpoint if the enclosing cancel scope has been cancelled.
33

34
    This does not allow the scheduler to switch to a different task.
35

36
    .. versionadded:: 3.0
37

38
    """
39
    await get_async_backend().checkpoint_if_cancelled()
11✔
40

41

42
async def cancel_shielded_checkpoint() -> None:
11✔
43
    """
44
    Allow the scheduler to switch to another task but without checking for cancellation.
45

46
    Equivalent to (but potentially more efficient than)::
47

48
        with CancelScope(shield=True):
49
            await checkpoint()
50

51

52
    .. versionadded:: 3.0
53

54
    """
55
    await get_async_backend().cancel_shielded_checkpoint()
11✔
56

57

58
def current_token() -> object:
11✔
59
    """
60
    Return a backend specific token object that can be used to get back to the event
61
    loop.
62

63
    """
64
    return get_async_backend().current_token()
11✔
65

66

67
_run_vars: WeakKeyDictionary[Any, dict[str, Any]] = WeakKeyDictionary()
11✔
68
_token_wrappers: dict[Any, _TokenWrapper] = {}
11✔
69

70

71
@dataclass(frozen=True)
11✔
72
class _TokenWrapper:
11✔
73
    __slots__ = "_token", "__weakref__"
11✔
74
    _token: object
11✔
75

76

77
class _NoValueSet(enum.Enum):
11✔
78
    NO_VALUE_SET = enum.auto()
11✔
79

80

81
class RunvarToken(Generic[T]):
11✔
82
    __slots__ = "_var", "_value", "_redeemed"
11✔
83

84
    def __init__(self, var: RunVar[T], value: T | Literal[_NoValueSet.NO_VALUE_SET]):
11✔
85
        self._var = var
11✔
86
        self._value: T | Literal[_NoValueSet.NO_VALUE_SET] = value
11✔
87
        self._redeemed = False
11✔
88

89

90
class RunVar(Generic[T]):
11✔
91
    """
92
    Like a :class:`~contextvars.ContextVar`, except scoped to the running event loop.
93
    """
94

95
    __slots__ = "_name", "_default"
11✔
96

97
    NO_VALUE_SET: Literal[_NoValueSet.NO_VALUE_SET] = _NoValueSet.NO_VALUE_SET
11✔
98

99
    _token_wrappers: set[_TokenWrapper] = set()
11✔
100

101
    def __init__(
11✔
102
        self, name: str, default: T | Literal[_NoValueSet.NO_VALUE_SET] = NO_VALUE_SET
103
    ):
104
        self._name = name
11✔
105
        self._default = default
11✔
106

107
    @property
11✔
108
    def _current_vars(self) -> dict[str, T]:
11✔
109
        token = current_token()
11✔
110
        try:
11✔
111
            return _run_vars[token]
11✔
112
        except KeyError:
11✔
113
            run_vars = _run_vars[token] = {}
11✔
114
            return run_vars
11✔
115

116
    @overload
11✔
117
    def get(self, default: D) -> T | D: ...
11✔
118

119
    @overload
11✔
120
    def get(self) -> T: ...
11✔
121

122
    def get(
11✔
123
        self, default: D | Literal[_NoValueSet.NO_VALUE_SET] = NO_VALUE_SET
124
    ) -> T | D:
125
        try:
11✔
126
            return self._current_vars[self._name]
11✔
127
        except KeyError:
11✔
128
            if default is not RunVar.NO_VALUE_SET:
11✔
129
                return default
11✔
130
            elif self._default is not RunVar.NO_VALUE_SET:
11✔
131
                return self._default
×
132

133
        raise LookupError(
11✔
134
            f'Run variable "{self._name}" has no value and no default set'
135
        )
136

137
    def set(self, value: T) -> RunvarToken[T]:
11✔
138
        current_vars = self._current_vars
11✔
139
        token = RunvarToken(self, current_vars.get(self._name, RunVar.NO_VALUE_SET))
11✔
140
        current_vars[self._name] = value
11✔
141
        return token
11✔
142

143
    def reset(self, token: RunvarToken[T]) -> None:
11✔
144
        if token._var is not self:
11✔
145
            raise ValueError("This token does not belong to this RunVar")
11✔
146

147
        if token._redeemed:
11✔
148
            raise ValueError("This token has already been used")
11✔
149

150
        if token._value is _NoValueSet.NO_VALUE_SET:
11✔
151
            try:
11✔
152
                del self._current_vars[self._name]
11✔
153
            except KeyError:
×
154
                pass
×
155
        else:
156
            self._current_vars[self._name] = token._value
×
157

158
        token._redeemed = True
11✔
159

160
    def __repr__(self) -> str:
11✔
161
        return f"<RunVar name={self._name!r}>"
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc