Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/google/cloud/firestore_v1/base_transaction.py: 61%

72 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2023-12-09 06:27 +0000

1# Copyright 2017 Google LLC All rights reserved. 

2# 

3# Licensed under the Apache License, Version 2.0 (the "License"); 

4# you may not use this file except in compliance with the License. 

5# You may obtain a copy of the License at 

6# 

7# http://www.apache.org/licenses/LICENSE-2.0 

8# 

9# Unless required by applicable law or agreed to in writing, software 

10# distributed under the License is distributed on an "AS IS" BASIS, 

11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 

12# See the License for the specific language governing permissions and 

13# limitations under the License. 

14 

15"""Helpers for applying Google Cloud Firestore changes in a transaction.""" 

16 

17from google.api_core import retry as retries 

18 

19from google.cloud.firestore_v1 import types 

20from typing import Any, Coroutine, NoReturn, Optional, Union 

21 

22_CANT_BEGIN: str 

23_CANT_COMMIT: str 

24_CANT_RETRY_READ_ONLY: str 

25_CANT_ROLLBACK: str 

26_EXCEED_ATTEMPTS_TEMPLATE: str 

27_INITIAL_SLEEP: float 

28_MAX_SLEEP: float 

29_MISSING_ID_TEMPLATE: str 

30_MULTIPLIER: float 

31_WRITE_READ_ONLY: str 

32 

33 

34MAX_ATTEMPTS = 5 

35"""int: Default number of transaction attempts (with retries).""" 

36_CANT_BEGIN: str = "The transaction has already begun. Current transaction ID: {!r}." 

37_MISSING_ID_TEMPLATE: str = "The transaction has no transaction ID, so it cannot be {}." 

38_CANT_ROLLBACK: str = _MISSING_ID_TEMPLATE.format("rolled back") 

39_CANT_COMMIT: str = _MISSING_ID_TEMPLATE.format("committed") 

40_WRITE_READ_ONLY: str = "Cannot perform write operation in read-only transaction." 

41_INITIAL_SLEEP: float = 1.0 

42"""float: Initial "max" for sleep interval. To be used in :func:`_sleep`.""" 

43_MAX_SLEEP: float = 30.0 

44"""float: Eventual "max" sleep time. To be used in :func:`_sleep`.""" 

45_MULTIPLIER: float = 2.0 

46"""float: Multiplier for exponential backoff. To be used in :func:`_sleep`.""" 

47_EXCEED_ATTEMPTS_TEMPLATE: str = "Failed to commit transaction in {:d} attempts." 

48_CANT_RETRY_READ_ONLY: str = "Only read-write transactions can be retried." 

49 

50 

51class BaseTransaction(object): 

52 """Accumulate read-and-write operations to be sent in a transaction. 

53 

54 Args: 

55 max_attempts (Optional[int]): The maximum number of attempts for 

56 the transaction (i.e. allowing retries). Defaults to 

57 :attr:`~google.cloud.firestore_v1.transaction.MAX_ATTEMPTS`. 

58 read_only (Optional[bool]): Flag indicating if the transaction 

59 should be read-only or should allow writes. Defaults to 

60 :data:`False`. 

61 """ 

62 

63 def __init__(self, max_attempts=MAX_ATTEMPTS, read_only=False) -> None: 

64 self._max_attempts = max_attempts 

65 self._read_only = read_only 

66 self._id = None 

67 

68 def _add_write_pbs(self, write_pbs) -> NoReturn: 

69 raise NotImplementedError 

70 

71 def _options_protobuf( 

72 self, retry_id: Union[bytes, None] 

73 ) -> Optional[types.common.TransactionOptions]: 

74 """Convert the current object to protobuf. 

75 

76 The ``retry_id`` value is used when retrying a transaction that 

77 failed (e.g. due to contention). It is intended to be the "first" 

78 transaction that failed (i.e. if multiple retries are needed). 

79 

80 Args: 

81 retry_id (Union[bytes, NoneType]): Transaction ID of a transaction 

82 to be retried. 

83 

84 Returns: 

85 Optional[google.cloud.firestore_v1.types.TransactionOptions]: 

86 The protobuf ``TransactionOptions`` if ``read_only==True`` or if 

87 there is a transaction ID to be retried, else :data:`None`. 

88 

89 Raises: 

90 ValueError: If ``retry_id`` is not :data:`None` but the 

91 transaction is read-only. 

92 """ 

93 if retry_id is not None: 

94 if self._read_only: 

95 raise ValueError(_CANT_RETRY_READ_ONLY) 

96 

97 return types.TransactionOptions( 

98 read_write=types.TransactionOptions.ReadWrite( 

99 retry_transaction=retry_id 

100 ) 

101 ) 

102 elif self._read_only: 

103 return types.TransactionOptions( 

104 read_only=types.TransactionOptions.ReadOnly() 

105 ) 

106 else: 

107 return None 

108 

109 @property 

110 def in_progress(self): 

111 """Determine if this transaction has already begun. 

112 

113 Returns: 

114 bool: Indicates if the transaction has started. 

115 """ 

116 return self._id is not None 

117 

118 @property 

119 def id(self): 

120 """Get the current transaction ID. 

121 

122 Returns: 

123 Optional[bytes]: The transaction ID (or :data:`None` if the 

124 current transaction is not in progress). 

125 """ 

126 return self._id 

127 

128 def _clean_up(self) -> None: 

129 """Clean up the instance after :meth:`_rollback`` or :meth:`_commit``. 

130 

131 This intended to occur on success or failure of the associated RPCs. 

132 """ 

133 self._write_pbs = [] 

134 self._id = None 

135 

136 def _begin(self, retry_id=None) -> NoReturn: 

137 raise NotImplementedError 

138 

139 def _rollback(self) -> NoReturn: 

140 raise NotImplementedError 

141 

142 def _commit(self) -> Union[list, Coroutine[Any, Any, list]]: 

143 raise NotImplementedError 

144 

145 def get_all( 

146 self, 

147 references: list, 

148 retry: retries.Retry = None, 

149 timeout: float = None, 

150 ) -> NoReturn: 

151 raise NotImplementedError 

152 

153 def get( 

154 self, 

155 ref_or_query, 

156 retry: retries.Retry = None, 

157 timeout: float = None, 

158 ) -> NoReturn: 

159 raise NotImplementedError 

160 

161 

162class _BaseTransactional(object): 

163 """Provide a callable object to use as a transactional decorater. 

164 

165 This is surfaced via 

166 :func:`~google.cloud.firestore_v1.transaction.transactional`. 

167 

168 Args: 

169 to_wrap (Callable[[:class:`~google.cloud.firestore_v1.transaction.Transaction`, ...], Any]): 

170 A callable that should be run (and retried) in a transaction. 

171 """ 

172 

173 def __init__(self, to_wrap) -> None: 

174 self.to_wrap = to_wrap 

175 self.current_id = None 

176 """Optional[bytes]: The current transaction ID.""" 

177 self.retry_id = None 

178 """Optional[bytes]: The ID of the first attempted transaction.""" 

179 

180 def _reset(self) -> None: 

181 """Unset the transaction IDs.""" 

182 self.current_id = None 

183 self.retry_id = None 

184 

185 def _pre_commit(self, transaction, *args, **kwargs) -> NoReturn: 

186 raise NotImplementedError 

187 

188 def __call__(self, transaction, *args, **kwargs): 

189 raise NotImplementedError