11"""
22PynamoDB exceptions
33"""
4-
5- from typing import Any , Optional
4+ from dataclasses import dataclass
5+ from typing import Any
6+ from typing import Dict
7+ from typing import Iterable
8+ from typing import List
9+ from typing import Optional
10+ from typing_extensions import Literal
611
712import botocore .exceptions
813
@@ -112,16 +117,63 @@ def __init__(self, table_name: str) -> None:
112117 super (TableDoesNotExist , self ).__init__ (msg )
113118
114119
120+ @dataclass
121+ class CancellationReason :
122+ """
123+ A reason for a transaction cancellation.
124+ """
125+ code : Literal [
126+ 'ConditionalCheckFailed' ,
127+ 'ItemCollectionSizeLimitExceeded' ,
128+ 'TransactionConflict' ,
129+ 'ProvisionedThroughputExceeded' ,
130+ 'ThrottlingError' ,
131+ 'ValidationError' ,
132+ ]
133+ message : Optional [str ] = None
134+
135+
115136class TransactWriteError (PynamoDBException ):
116137 """
117138 Raised when a :class:`~pynamodb.transactions.TransactWrite` operation fails.
118139 """
119140
141+ @property
142+ def cancellation_reasons (self ) -> List [Optional [CancellationReason ]]:
143+ """
144+ When :attr:`.cause_response_code` is ``TransactionCanceledException``, this property lists
145+ cancellation reasons in the same order as the transaction items (one-to-one).
146+ Items which were not part of the reason for cancellation would have :code:`None` as the value.
147+
148+ For a list of possible cancellation reasons and their semantics,
149+ see `TransactWriteItems`_ in the AWS documentation.
150+
151+ .. _TransactWriteItems: https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_TransactWriteItems.html
152+ """
153+ if not isinstance (self .cause , VerboseClientError ):
154+ return []
155+ return self .cause .cancellation_reasons
156+
120157
121158class TransactGetError (PynamoDBException ):
122159 """
123160 Raised when a :class:`~pynamodb.transactions.TransactGet` operation fails.
124161 """
162+ @property
163+ def cancellation_reasons (self ) -> List [Optional [CancellationReason ]]:
164+ """
165+ When :attr:`.cause_response_code` is ``TransactionCanceledException``, this property lists
166+ cancellation reasons in the same order as the transaction items (one-to-one).
167+ Items which were not part of the reason for cancellation would have :code:`None` as the value.
168+
169+ For a list of possible cancellation reasons and their semantics,
170+ see `TransactGetItems`_ in the AWS documentation.
171+
172+ .. _TransactGetItems: https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_TransactGetItems.html
173+ """
174+ if not isinstance (self .cause , VerboseClientError ):
175+ return []
176+ return self .cause .cancellation_reasons
125177
126178
127179class InvalidStateError (PynamoDBException ):
@@ -156,13 +208,23 @@ def prepend_path(self, attr_name: str) -> None:
156208
157209
158210class VerboseClientError (botocore .exceptions .ClientError ):
159- def __init__ (self , error_response : Any , operation_name : str , verbose_properties : Optional [Any ] = None ) -> None :
211+ def __init__ (
212+ self ,
213+ error_response : Dict [str , Any ],
214+ operation_name : str ,
215+ verbose_properties : Optional [Any ] = None ,
216+ * ,
217+ cancellation_reasons : Iterable [Optional [CancellationReason ]] = (),
218+ ) -> None :
160219 """
161220 Like ClientError, but with a verbose message.
162221
163222 :param error_response: Error response in shape expected by ClientError.
164223 :param operation_name: The name of the operation that failed.
165224 :param verbose_properties: A dict of properties to include in the verbose message.
225+ :param cancellation_reasons: For `TransactionCanceledException` error code,
226+ a list of cancellation reasons in the same order as the transaction's items (one to one).
227+ For items which were not a reason for the transaction cancellation, :code:`None` will be the value.
166228 """
167229 if not verbose_properties :
168230 verbose_properties = {}
@@ -173,4 +235,9 @@ def __init__(self, error_response: Any, operation_name: str, verbose_properties:
173235 'operation: {{error_message}}'
174236 ).format (request_id = verbose_properties .get ('request_id' ), table_name = verbose_properties .get ('table_name' ))
175237
176- super (VerboseClientError , self ).__init__ (error_response , operation_name )
238+ self .cancellation_reasons = list (cancellation_reasons )
239+
240+ super (VerboseClientError , self ).__init__ (
241+ error_response , # type:ignore[arg-type] # in stubs: botocore.exceptions._ClientErrorResponseTypeDef
242+ operation_name ,
243+ )
0 commit comments