Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/anyio/_core/_futures.py: 38%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

82 statements  

1from __future__ import annotations 

2 

3from collections.abc import Generator 

4from enum import Enum, auto 

5from typing import Any, Generic, TypeVar 

6 

7from ._exceptions import ( 

8 FutureAlreadyFinished, 

9 FutureCancelled, 

10 FutureFailed, 

11 FutureNotFinished, 

12) 

13from ._synchronization import Event 

14 

15T = TypeVar("T") 

16 

17 

18class Future(Generic[T]): 

19 """ 

20 An awaitable object that works similar to a :class:`asyncio.Future` but 

21 with similar characteristics to a :class:`.TaskHandle`. 

22 """ 

23 

24 class Status(Enum): 

25 """ 

26 The status of a future handle. 

27 

28 .. attribute:: PENDING 

29 

30 The future has not finished yet. 

31 .. attribute:: FINISHED 

32 

33 The future has finished with a return value. 

34 .. attribute:: CANCELLED 

35 

36 The future was cancelled and has finished since. 

37 .. attribute:: FAILED 

38 

39 The future raised an exception. 

40 """ 

41 

42 PENDING = auto() 

43 FINISHED = auto() 

44 CANCELLED = auto() 

45 FAILED = auto() 

46 

47 __slots__ = ( 

48 "_cancelled", 

49 "_exception", 

50 "_finished_event", 

51 "_name", 

52 "_return_value", 

53 ) 

54 _return_value: T 

55 

56 def __init__(self, *, name: str | None = None) -> None: 

57 self._finished_event = Event() 

58 self._exception: BaseException | None = None 

59 self._cancelled: bool = False 

60 self._name = name 

61 

62 def _check_pending(self) -> None: 

63 """Shortcut for checking if a Future is pending 

64 

65 :raises FutureAlreadyFinished: if future was already given a result or exception. 

66 :raises FutureCancelled: if future has been cancelled previously. 

67 """ 

68 match self.status: 

69 case Future.Status.PENDING: 

70 return 

71 case Future.Status.FINISHED: 

72 raise FutureAlreadyFinished("future has already finished") 

73 case Future.Status.FAILED: 

74 raise FutureAlreadyFinished("future already failed") 

75 case Future.Status.CANCELLED: 

76 raise FutureCancelled("future was cancelled") 

77 

78 async def wait(self) -> None: 

79 """ 

80 Waits for the future to finish. 

81 

82 This method will attempt to wait for a result or exception 

83 """ 

84 await self._finished_event.wait() 

85 

86 def cancel(self) -> None: 

87 """Cancels a pending `.Future` object 

88 

89 Does nothing if the Future was already finished. 

90 """ 

91 if self.status is Future.Status.PENDING: 

92 self._cancelled = True 

93 self._finished_event.set() 

94 

95 @property 

96 def exception(self) -> BaseException | None: 

97 """ 

98 The exception value of a `.Future` 

99 

100 :raises FutureNotFinished: if future is still pending 

101 :raises FutureCancelled: if future was cancelled 

102 :returns: None if future succeeds with a result sent instead 

103 otherwise this will be an exception 

104 """ 

105 

106 match self.status: 

107 case Future.Status.PENDING: 

108 raise FutureNotFinished("the future has not finished yet") 

109 case Future.Status.FINISHED: 

110 return None 

111 case Future.Status.CANCELLED: 

112 raise FutureCancelled("the future was cancelled") 

113 case Future.Status.FAILED: 

114 return self._exception 

115 

116 @exception.setter 

117 def exception(self, exception: BaseException) -> None: 

118 """Send exception for a `.Future` object 

119 

120 :raises FutureAlreadyFinished: if future was already given a result or exception. 

121 :raises FutureCancelled: if future has been cancelled previously. 

122 """ 

123 self._check_pending() 

124 self._exception = exception 

125 self._finished_event.set() 

126 

127 @property 

128 def return_value(self) -> T: 

129 """ 

130 The return value of the future. 

131 

132 :raises FutureNotFinished: if the future has not finished yet 

133 :raises FutureCancelled: if the future was cancelled 

134 :raises FutureFailed: if the future raised an exception 

135 

136 """ 

137 match self.status: 

138 case Future.Status.PENDING: 

139 raise FutureNotFinished("the future has not finished yet") 

140 case Future.Status.FINISHED: 

141 return self._return_value 

142 case Future.Status.CANCELLED: 

143 raise FutureCancelled("the future was cancelled") 

144 case Future.Status.FAILED: 

145 raise FutureFailed( 

146 "the future raised an exception" 

147 ) from self._exception 

148 

149 @return_value.setter 

150 def return_value(self, value: T) -> None: 

151 """ 

152 Send pending result for a `.Future` object 

153 

154 :raises FutureAlreadyFinished: if future was already given a result or exception. 

155 :raises FutureCancelled: if future has been cancelled previously. 

156 """ 

157 self._check_pending() 

158 self._return_value = value 

159 self._finished_event.set() 

160 

161 @property 

162 def status(self) -> Future.Status: 

163 """ 

164 The current status of a future. 

165 

166 Every future starts in the :attr:`~Future.Status.PENDING` state. 

167 If a future is cancelled while in this state, it will transition to the 

168 :attr:`Future.Status.CANCELLED` state. When the task finishes, it will 

169 transition to one of the three final states ( 

170 :attr:`Future.Status.FINISHED`, :attr:`Future.Status.FAILED`, or 

171 :attr:`Future.Status.CANCELLED`) depending on the exception the task 

172 raised, if any. No other status transitions will happen. 

173 """ 

174 if not self._finished_event.is_set(): 

175 return Future.Status.PENDING 

176 elif self._cancelled: 

177 return Future.Status.CANCELLED 

178 elif self._exception is not None: 

179 return Future.Status.FAILED 

180 else: 

181 return Future.Status.FINISHED 

182 

183 def __await__(self) -> Generator[Any, Any, T]: 

184 yield from self.wait().__await__() 

185 return self.return_value 

186 

187 def __repr__(self) -> str: 

188 return ( 

189 f"<{self.__class__.__name__} {self.status.name.lower()} " 

190 f"name={self._name!r}>" 

191 )