python-etcd3-async 0.1.0__py3-none-any.whl

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.
@@ -0,0 +1,434 @@
1
+ """etcd semantic attributes for OpenTelemetry tracing.
2
+
3
+ This module defines standardized attributes for etcd operations in OpenTelemetry
4
+ traces, following the OpenTelemetry semantic conventions:
5
+ https://opentelemetry.io/docs/reference/specification/trace/semantic_conventions/
6
+ """
7
+
8
+ from dataclasses import dataclass
9
+ from enum import Enum
10
+ from typing import Any, Dict, Optional
11
+
12
+
13
+ class EtcdOperationType(Enum):
14
+ """etcd operation types for tracing."""
15
+
16
+ # KV operations
17
+ KV_GET = "kv.get"
18
+ KV_PUT = "kv.put"
19
+ KV_DELETE = "kv.delete"
20
+ KV_RANGE = "kv.range"
21
+ KV_TXN = "kv.transaction"
22
+
23
+ # Lease operations
24
+ LEASE_GRANT = "lease.grant"
25
+ LEASE_REVOKE = "lease.revoke"
26
+ LEASE_KEEPALIVE = "lease.keepalive"
27
+ LEASE_TTL = "lease.ttl"
28
+
29
+ # Lock operations
30
+ LOCK_ACQUIRE = "lock.acquire"
31
+ LOCK_RELEASE = "lock.release"
32
+ LOCK_REFRESH = "lock.refresh"
33
+
34
+ # Watch operations
35
+ WATCH_CREATE = "watch.create"
36
+ WATCH_CANCEL = "watch.cancel"
37
+
38
+ # Cluster operations
39
+ CLUSTER_MEMBER_ADD = "cluster.member.add"
40
+ CLUSTER_MEMBER_REMOVE = "cluster.member.remove"
41
+ CLUSTER_MEMBER_UPDATE = "cluster.member.update"
42
+ CLUSTER_MEMBER_LIST = "cluster.member.list"
43
+
44
+ # Maintenance operations
45
+ MAINTENANCE_STATUS = "maintenance.status"
46
+ MAINTENANCE_COMPACT = "maintenance.compact"
47
+ MAINTENANCE_DEFRAG = "maintenance.defragment"
48
+
49
+
50
+ @dataclass(frozen=True)
51
+ class TraceAttributes:
52
+ """Standard attribute names for etcd OpenTelemetry traces.
53
+
54
+ These attributes follow OpenTelemetry semantic conventions where applicable
55
+ and define etcd-specific attributes for comprehensive observability.
56
+ """
57
+
58
+ # === OpenTelemetry Standard Database Attributes ===
59
+ DB_SYSTEM = "db.system"
60
+ """Database system: always 'etcd' for this client."""
61
+
62
+ DB_OPERATION = "db.operation"
63
+ """Database operation name (e.g., 'kv.get', 'lease.grant')."""
64
+
65
+ DB_STATEMENT = "db.statement"
66
+ """Database statement (for complex queries, optional)."""
67
+
68
+ # === etcd Key-Value Attributes ===
69
+ ETCD_KEY = "etcd.key"
70
+ """The key being operated on."""
71
+
72
+ ETCD_RANGE_END = "etcd.range_end"
73
+ """End of the key range for range operations."""
74
+
75
+ ETCD_LIMIT = "etcd.limit"
76
+ """Maximum number of keys to return."""
77
+
78
+ ETCD_REVISION = "etcd.revision"
79
+ """Current revision of the operation result."""
80
+
81
+ ETCD_PREV_REVISION = "etcd.prev_revision"
82
+ """Previous revision (for compare operations)."""
83
+
84
+ ETCD_COUNT = "etcd.count"
85
+ """Number of keys returned or affected."""
86
+
87
+ ETCD_DELETED = "etcd.deleted"
88
+ """Number of keys deleted."""
89
+
90
+ # === etcd Cluster Attributes ===
91
+ ETCD_ENDPOINT = "etcd.endpoint"
92
+ """The etcd endpoint being accessed."""
93
+
94
+ ETCD_CLUSTER_ID = "etcd.cluster_id"
95
+ """Cluster ID from response header."""
96
+
97
+ ETCD_MEMBER_ID = "etcd.member_id"
98
+ """Member ID from response header."""
99
+
100
+ ETCD_RAFT_TERM = "etcd.raft_term"
101
+ """Raft term from response header."""
102
+
103
+ # === etcd Lease Attributes ===
104
+ ETCD_LEASE_ID = "etcd.lease.id"
105
+ """Lease ID."""
106
+
107
+ ETCD_LEASE_TTL = "etcd.lease.ttl"
108
+ """Lease TTL in seconds."""
109
+
110
+ ETCD_LEASE_REMAINING_TTL = "etcd.lease.remaining_ttl"
111
+ """Remaining TTL for the lease."""
112
+
113
+ # === etcd Lock Attributes ===
114
+ ETCD_LOCK_NAME = "etcd.lock.name"
115
+ """Lock name (key path)."""
116
+
117
+ ETCD_LOCK_UUID = "etcd.lock.uuid"
118
+ """Lock UUID (first 8 bytes)."""
119
+
120
+ ETCD_LOCK_ACQUIRED = "etcd.lock.acquired"
121
+ """Whether the lock was successfully acquired."""
122
+
123
+ # === etcd Transaction Attributes ===
124
+ ETCD_TXN_COMPARE_COUNT = "etcd.txn.compare_count"
125
+ """Number of compare conditions."""
126
+
127
+ ETCD_TXN_SUCCESS_COUNT = "etcd.txn.success_count"
128
+ """Number of success operations."""
129
+
130
+ ETCD_TXN_FAILURE_COUNT = "etcd.txn.failure_count"
131
+ """Number of failure operations."""
132
+
133
+ ETCD_TXN_SUCCEEDED = "etcd.txn.succeeded"
134
+ """Whether the transaction succeeded."""
135
+
136
+ # === etcd Batch Attributes ===
137
+ ETCD_BATCH_SIZE = "etcd.batch.size"
138
+ """Number of operations in the batch."""
139
+
140
+ ETCD_BATCH_ATOMIC = "etcd.batch.atomic"
141
+ """Whether the batch is atomic."""
142
+
143
+ # === etcd Error Attributes ===
144
+ ETCD_ERROR_CODE = "etcd.error_code"
145
+ """Error code if operation failed."""
146
+
147
+ ETCD_ERROR_MESSAGE = "etcd.error_message"
148
+ """Error message if operation failed."""
149
+
150
+ # === Retry Attributes ===
151
+ ETCD_RETRY_COUNT = "etcd.retry.count"
152
+ """Number of retries for this operation."""
153
+
154
+ ETCD_RETRY_ENDPOINT = "etcd.retry.endpoint"
155
+ """Endpoint used after retry."""
156
+
157
+
158
+ def _safe_decode(value: Optional[bytes], max_length: int = 256) -> Optional[str]:
159
+ """Safely decode bytes to string for attribute values.
160
+
161
+ Args:
162
+ value: Bytes value to decode
163
+ max_length: Maximum length for the decoded string
164
+
165
+ Returns:
166
+ Decoded string or None if input is None
167
+ """
168
+ if value is None:
169
+ return None
170
+
171
+ try:
172
+ decoded = value.decode('utf-8', errors='replace')
173
+ if len(decoded) > max_length:
174
+ decoded = decoded[:max_length] + "..."
175
+ return decoded
176
+ except Exception:
177
+ return repr(value)
178
+
179
+
180
+ def build_kv_attributes(
181
+ operation: str,
182
+ key: Optional[bytes] = None,
183
+ range_end: Optional[bytes] = None,
184
+ limit: Optional[int] = None,
185
+ **kwargs
186
+ ) -> Dict[str, Any]:
187
+ """Build trace attributes for KV operations.
188
+
189
+ Args:
190
+ operation: Operation type (e.g., 'kv.get', 'kv.put')
191
+ key: The key being operated on
192
+ range_end: End of key range for range operations
193
+ limit: Maximum number of keys to return
194
+ **kwargs: Additional attributes
195
+
196
+ Returns:
197
+ Dictionary of trace attributes
198
+ """
199
+ attrs = {
200
+ TraceAttributes.DB_SYSTEM: "etcd",
201
+ TraceAttributes.DB_OPERATION: operation,
202
+ }
203
+
204
+ if key is not None:
205
+ attrs[TraceAttributes.ETCD_KEY] = _safe_decode(key)
206
+
207
+ if range_end is not None:
208
+ attrs[TraceAttributes.ETCD_RANGE_END] = _safe_decode(range_end)
209
+
210
+ if limit is not None:
211
+ attrs[TraceAttributes.ETCD_LIMIT] = limit
212
+
213
+ attrs.update(kwargs)
214
+ return attrs
215
+
216
+
217
+ def build_response_attributes(
218
+ header=None,
219
+ count: Optional[int] = None,
220
+ deleted: Optional[int] = None,
221
+ **kwargs
222
+ ) -> Dict[str, Any]:
223
+ """Build trace attributes from etcd response.
224
+
225
+ Args:
226
+ header: Response header from etcd
227
+ count: Number of keys returned/affected
228
+ deleted: Number of keys deleted
229
+ **kwargs: Additional attributes
230
+
231
+ Returns:
232
+ Dictionary of trace attributes
233
+ """
234
+ attrs = {}
235
+
236
+ if header:
237
+ attrs[TraceAttributes.ETCD_REVISION] = header.revision
238
+ attrs[TraceAttributes.ETCD_CLUSTER_ID] = header.cluster_id
239
+ attrs[TraceAttributes.ETCD_MEMBER_ID] = header.member_id
240
+ attrs[TraceAttributes.ETCD_RAFT_TERM] = header.raft_term
241
+
242
+ if count is not None:
243
+ attrs[TraceAttributes.ETCD_COUNT] = count
244
+
245
+ if deleted is not None:
246
+ attrs[TraceAttributes.ETCD_DELETED] = deleted
247
+
248
+ attrs.update(kwargs)
249
+ return attrs
250
+
251
+
252
+ def build_lease_attributes(
253
+ lease_id: Optional[int] = None,
254
+ ttl: Optional[int] = None,
255
+ remaining_ttl: Optional[int] = None,
256
+ **kwargs
257
+ ) -> Dict[str, Any]:
258
+ """Build trace attributes for lease operations.
259
+
260
+ Args:
261
+ lease_id: Lease ID
262
+ ttl: Lease TTL in seconds
263
+ remaining_ttl: Remaining TTL
264
+ **kwargs: Additional attributes
265
+
266
+ Returns:
267
+ Dictionary of trace attributes
268
+ """
269
+ attrs = {
270
+ TraceAttributes.DB_SYSTEM: "etcd",
271
+ }
272
+
273
+ if lease_id is not None:
274
+ attrs[TraceAttributes.ETCD_LEASE_ID] = lease_id
275
+
276
+ if ttl is not None:
277
+ attrs[TraceAttributes.ETCD_LEASE_TTL] = ttl
278
+
279
+ if remaining_ttl is not None:
280
+ attrs[TraceAttributes.ETCD_LEASE_REMAINING_TTL] = remaining_ttl
281
+
282
+ attrs.update(kwargs)
283
+ return attrs
284
+
285
+
286
+ def build_lock_attributes(
287
+ name: Optional[str] = None,
288
+ uuid: Optional[bytes] = None,
289
+ acquired: Optional[bool] = None,
290
+ **kwargs
291
+ ) -> Dict[str, Any]:
292
+ """Build trace attributes for lock operations.
293
+
294
+ Args:
295
+ name: Lock name (key path)
296
+ uuid: Lock UUID (will use first 8 bytes)
297
+ acquired: Whether the lock was acquired
298
+ **kwargs: Additional attributes
299
+
300
+ Returns:
301
+ Dictionary of trace attributes
302
+ """
303
+ attrs = {
304
+ TraceAttributes.DB_SYSTEM: "etcd",
305
+ }
306
+
307
+ if name is not None:
308
+ attrs[TraceAttributes.ETCD_LOCK_NAME] = name
309
+
310
+ if uuid is not None:
311
+ # Use first 8 bytes of UUID for readability
312
+ uuid_str = uuid[:8].hex() if isinstance(uuid, bytes) else str(uuid)[:16]
313
+ attrs[TraceAttributes.ETCD_LOCK_UUID] = uuid_str
314
+
315
+ if acquired is not None:
316
+ attrs[TraceAttributes.ETCD_LOCK_ACQUIRED] = acquired
317
+
318
+ attrs.update(kwargs)
319
+ return attrs
320
+
321
+
322
+ def build_txn_attributes(
323
+ compare_count: Optional[int] = None,
324
+ success_count: Optional[int] = None,
325
+ failure_count: Optional[int] = None,
326
+ succeeded: Optional[bool] = None,
327
+ **kwargs
328
+ ) -> Dict[str, Any]:
329
+ """Build trace attributes for transaction operations.
330
+
331
+ Args:
332
+ compare_count: Number of compare conditions
333
+ success_count: Number of success operations
334
+ failure_count: Number of failure operations
335
+ succeeded: Whether the transaction succeeded
336
+ **kwargs: Additional attributes
337
+
338
+ Returns:
339
+ Dictionary of trace attributes
340
+ """
341
+ attrs = {
342
+ TraceAttributes.DB_SYSTEM: "etcd",
343
+ }
344
+
345
+ if compare_count is not None:
346
+ attrs[TraceAttributes.ETCD_TXN_COMPARE_COUNT] = compare_count
347
+
348
+ if success_count is not None:
349
+ attrs[TraceAttributes.ETCD_TXN_SUCCESS_COUNT] = success_count
350
+
351
+ if failure_count is not None:
352
+ attrs[TraceAttributes.ETCD_TXN_FAILURE_COUNT] = failure_count
353
+
354
+ if succeeded is not None:
355
+ attrs[TraceAttributes.ETCD_TXN_SUCCEEDED] = succeeded
356
+
357
+ attrs.update(kwargs)
358
+ return attrs
359
+
360
+
361
+ def build_batch_attributes(
362
+ size: int,
363
+ atomic: bool = False,
364
+ **kwargs
365
+ ) -> Dict[str, Any]:
366
+ """Build trace attributes for batch operations.
367
+
368
+ Args:
369
+ size: Number of operations in the batch
370
+ atomic: Whether the batch is atomic
371
+ **kwargs: Additional attributes
372
+
373
+ Returns:
374
+ Dictionary of trace attributes
375
+ """
376
+ attrs = {
377
+ TraceAttributes.DB_SYSTEM: "etcd",
378
+ TraceAttributes.ETCD_BATCH_SIZE: size,
379
+ TraceAttributes.ETCD_BATCH_ATOMIC: atomic,
380
+ }
381
+
382
+ attrs.update(kwargs)
383
+ return attrs
384
+
385
+
386
+ def build_endpoint_attributes(
387
+ endpoint: str,
388
+ **kwargs
389
+ ) -> Dict[str, Any]:
390
+ """Build trace attributes for endpoint-related operations.
391
+
392
+ Args:
393
+ endpoint: The etcd endpoint
394
+ **kwargs: Additional attributes
395
+
396
+ Returns:
397
+ Dictionary of trace attributes
398
+ """
399
+ attrs = {
400
+ TraceAttributes.ETCD_ENDPOINT: endpoint,
401
+ }
402
+
403
+ attrs.update(kwargs)
404
+ return attrs
405
+
406
+
407
+ def build_error_attributes(
408
+ error_code: Optional[str] = None,
409
+ error_message: Optional[str] = None,
410
+ **kwargs
411
+ ) -> Dict[str, Any]:
412
+ """Build trace attributes for error cases.
413
+
414
+ Args:
415
+ error_code: Error code
416
+ error_message: Error message
417
+ **kwargs: Additional attributes
418
+
419
+ Returns:
420
+ Dictionary of trace attributes
421
+ """
422
+ attrs = {}
423
+
424
+ if error_code is not None:
425
+ attrs[TraceAttributes.ETCD_ERROR_CODE] = error_code
426
+
427
+ if error_message is not None:
428
+ # Truncate long error messages
429
+ if len(error_message) > 256:
430
+ error_message = error_message[:256] + "..."
431
+ attrs[TraceAttributes.ETCD_ERROR_MESSAGE] = error_message
432
+
433
+ attrs.update(kwargs)
434
+ return attrs
etcd3/transactions.py ADDED
@@ -0,0 +1,280 @@
1
+ """Transaction operations for etcd3.
2
+
3
+ This module defines classes for building etcd transactions with
4
+ comparison operations and conditional mutations.
5
+ """
6
+
7
+ from typing import Any, Optional
8
+
9
+ import etcdrpc
10
+ import etcd3.utils as utils
11
+
12
+
13
+ _OPERATORS = {
14
+ etcdrpc.Compare.EQUAL: "==",
15
+ etcdrpc.Compare.NOT_EQUAL: "!=",
16
+ etcdrpc.Compare.LESS: "<",
17
+ etcdrpc.Compare.GREATER: ">",
18
+ }
19
+
20
+
21
+ class BaseCompare:
22
+ """Base class for transaction comparison operations.
23
+
24
+ :param key: The key to compare
25
+ :type key: str or bytes
26
+ :param range_end: End of key range for range comparisons
27
+ :type range_end: str or bytes, optional
28
+ """
29
+
30
+ key: Any
31
+ range_end: Optional[Any]
32
+ value: Any
33
+ op: Optional[Any]
34
+
35
+ def __init__(self, key: Any, range_end: Optional[Any] = None) -> None:
36
+ self.key = key
37
+ self.range_end = range_end
38
+ self.value = None
39
+ self.op = None
40
+
41
+ def __eq__(self, other: Any) -> "BaseCompare":
42
+ """Set comparison to equal."""
43
+ self.value = other
44
+ self.op = etcdrpc.Compare.EQUAL
45
+ return self
46
+
47
+ def __ne__(self, other: Any) -> "BaseCompare":
48
+ """Set comparison to not equal."""
49
+ self.value = other
50
+ self.op = etcdrpc.Compare.NOT_EQUAL
51
+ return self
52
+
53
+ def __lt__(self, other: Any) -> "BaseCompare":
54
+ """Set comparison to less than."""
55
+ self.value = other
56
+ self.op = etcdrpc.Compare.LESS
57
+ return self
58
+
59
+ def __gt__(self, other: Any) -> "BaseCompare":
60
+ """Set comparison to greater than."""
61
+ self.value = other
62
+ self.op = etcdrpc.Compare.GREATER
63
+ return self
64
+
65
+ def __repr__(self) -> str:
66
+ """Return string representation of the comparison."""
67
+ if self.range_end is None:
68
+ keys = self.key
69
+ else:
70
+ keys = "[{}, {})".format(self.key, self.range_end)
71
+ return "{}: {} {} '{}'".format(
72
+ self.__class__, keys, _OPERATORS.get(self.op), self.value
73
+ )
74
+
75
+ def build_message(self) -> etcdrpc.Compare:
76
+ """Build the gRPC Compare message.
77
+
78
+ :returns: Compare message for the transaction
79
+ :rtype: etcdrpc.Compare
80
+ """
81
+ compare = etcdrpc.Compare()
82
+ compare.key = utils.to_bytes(self.key)
83
+ if self.range_end is not None:
84
+ compare.range_end = utils.to_bytes(self.range_end)
85
+
86
+ if self.op is None:
87
+ raise ValueError("op must be one of =, !=, < or >")
88
+
89
+ compare.result = self.op
90
+
91
+ self.build_compare(compare)
92
+ return compare
93
+
94
+ def build_compare(self, compare: etcdrpc.Compare) -> None:
95
+ """Build the target-specific comparison.
96
+
97
+ Override in subclasses to set the appropriate comparison target.
98
+
99
+ :param compare: The Compare message to populate
100
+ :type compare: etcdrpc.Compare
101
+ """
102
+ raise NotImplementedError
103
+
104
+
105
+ class Value(BaseCompare):
106
+ """Compare the value of a key.
107
+
108
+ Example:
109
+ >>> transactions.Value('/key') == b'value'
110
+ """
111
+
112
+ def build_compare(self, compare: etcdrpc.Compare) -> None:
113
+ """Build a value comparison."""
114
+ compare.target = etcdrpc.Compare.VALUE
115
+ compare.value = utils.to_bytes(self.value)
116
+
117
+
118
+ class Version(BaseCompare):
119
+ """Compare the version of a key.
120
+
121
+ Example:
122
+ >>> transactions.Version('/key') > 0
123
+ """
124
+
125
+ def build_compare(self, compare: etcdrpc.Compare) -> None:
126
+ """Build a version comparison."""
127
+ compare.target = etcdrpc.Compare.VERSION
128
+ compare.version = int(self.value)
129
+
130
+
131
+ class Create(BaseCompare):
132
+ """Compare the creation revision of a key.
133
+
134
+ Example:
135
+ >>> transactions.Create('/key') == 0
136
+ """
137
+
138
+ def build_compare(self, compare: etcdrpc.Compare) -> None:
139
+ """Build a creation revision comparison."""
140
+ compare.target = etcdrpc.Compare.CREATE
141
+ compare.create_revision = int(self.value)
142
+
143
+
144
+ class Mod(BaseCompare):
145
+ """Compare the modification revision of a key.
146
+
147
+ Example:
148
+ >>> transactions.Mod('/key') > 100
149
+ """
150
+
151
+ def build_compare(self, compare: etcdrpc.Compare) -> None:
152
+ """Build a modification revision comparison."""
153
+ compare.target = etcdrpc.Compare.MOD
154
+ compare.mod_revision = int(self.value)
155
+
156
+
157
+ class Lease(BaseCompare):
158
+ """Compare the lease ID attached to a key.
159
+
160
+ Example:
161
+ >>> transactions.Lease('/key') == lease_id
162
+ """
163
+
164
+ def build_compare(self, compare: etcdrpc.Compare) -> None:
165
+ """Build a lease comparison."""
166
+ compare.target = etcdrpc.Compare.LEASE
167
+ compare.lease = int(self.value)
168
+
169
+
170
+ class Put:
171
+ """A put operation for use in transactions.
172
+
173
+ :param key: The key to set
174
+ :type key: str or bytes
175
+ :param value: The value to set
176
+ :type value: str or bytes, optional
177
+ :param lease: Lease ID to associate with the key
178
+ :type lease: int, optional
179
+ :param prev_kv: Whether to return the previous key-value pair
180
+ :type prev_kv: bool
181
+ :param ignore_value: Whether to ignore the value field
182
+ :type ignore_value: bool
183
+ :param ignore_lease: Whether to ignore the lease field
184
+ :type ignore_lease: bool
185
+ """
186
+
187
+ key: Any
188
+ value: Optional[Any]
189
+ lease: Optional[int]
190
+ prev_kv: bool
191
+ ignore_value: bool
192
+ ignore_lease: bool
193
+
194
+ def __init__(
195
+ self,
196
+ key: Any,
197
+ value: Optional[Any] = None,
198
+ lease: Optional[int] = None,
199
+ prev_kv: bool = False,
200
+ ignore_value: bool = False,
201
+ ignore_lease: bool = False,
202
+ ) -> None:
203
+ self.key = key
204
+ self.value = value
205
+ self.lease = lease
206
+ self.prev_kv = prev_kv
207
+ self.ignore_value = ignore_value
208
+ self.ignore_lease = ignore_lease
209
+
210
+
211
+ class Get:
212
+ """A get operation for use in transactions.
213
+
214
+ :param key: The key to get
215
+ :type key: str or bytes
216
+ :param range_end: End of key range for prefix queries
217
+ :type range_end: str or bytes, optional
218
+ """
219
+
220
+ key: Any
221
+ range_end: Optional[Any]
222
+
223
+ def __init__(self, key: Any, range_end: Optional[Any] = None) -> None:
224
+ self.key = key
225
+ self.range_end = range_end
226
+
227
+
228
+ class Delete:
229
+ """A delete operation for use in transactions.
230
+
231
+ :param key: The key to delete
232
+ :type key: str or bytes
233
+ :param range_end: End of key range for range deletes
234
+ :type range_end: str or bytes, optional
235
+ :param prev_kv: Whether to return the deleted key-value pairs
236
+ :type prev_kv: bool
237
+ """
238
+
239
+ key: Any
240
+ range_end: Optional[Any]
241
+ prev_kv: bool
242
+
243
+ def __init__(
244
+ self,
245
+ key: Any,
246
+ range_end: Optional[Any] = None,
247
+ prev_kv: bool = False,
248
+ ) -> None:
249
+ self.key = key
250
+ self.range_end = range_end
251
+ self.prev_kv = prev_kv
252
+
253
+
254
+ class Txn:
255
+ """A transaction with conditional operations.
256
+
257
+ Transactions allow atomic execution of a compare sequence
258
+ followed by success/failure operation lists.
259
+
260
+ :param compare: List of comparison operations
261
+ :type compare: list of BaseCompare
262
+ :param success: List of operations if all comparisons succeed
263
+ :type success: list of Put, Get, Delete, or Txn, optional
264
+ :param failure: List of operations if any comparison fails
265
+ :type failure: list of Put, Get, Delete, or Txn, optional
266
+ """
267
+
268
+ compare: list
269
+ success: Optional[list]
270
+ failure: Optional[list]
271
+
272
+ def __init__(
273
+ self,
274
+ compare: list,
275
+ success: Optional[list] = None,
276
+ failure: Optional[list] = None,
277
+ ) -> None:
278
+ self.compare = compare
279
+ self.success = success
280
+ self.failure = failure