botocore
botocore copied to clipboard
Copying WaiterError causes a TypeError
Describe the bug
WaiterError causes a TypeError when you try to copy it.
Steps to reproduce
In [1]: from botocore.exceptions import WaiterError
In [2]: error = WaiterError(
...: name="FakeWaiter", reason="FakeReason", last_response="FakeResponse"
...: )
In [3]: from copy import copy
In [4]: copy(error)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Input In [4], in <module>
----> 1 copy(error)
File /usr/lib/python3.8/copy.py:102, in copy(x)
100 if isinstance(rv, str):
101 return x
--> 102 return _reconstruct(x, None, *rv)
File /usr/lib/python3.8/copy.py:264, in _reconstruct(x, memo, func, args, state, listiter, dictiter, deepcopy)
262 if deep and args:
263 args = (deepcopy(arg, memo) for arg in args)
--> 264 y = func(*args)
265 if deep:
266 memo[id(x)] = y
File ~/.cache/pypoetry/virtualenvs/bp-1082-udpate-apt-002-product-kAdUhv53-py3.8/lib/python3.8/site-packages/botocore/exceptions.py:28, in _exception_from_packed_args(exception_cls, args, kwargs)
26 if kwargs is None:
27 kwargs = {}
---> 28 return exception_cls(*args, **kwargs)
TypeError: __init__() missing 1 required positional argument: 'last_response'
Expected behavior
That a copy of the WaiterError instance is created.
Additional context
This bug makes it more difficult to work with the botocove library to query multiple AWS accounts in the organization. Botocove copies the output, including the exceptions, before returning the final result to the client. See botocove issue #26.
The ClientError class is copyable because it implements the __reduce__ method.
https://github.com/boto/botocore/blob/a692f50213f4480f5fdf8faf30b87ad362bf0519/botocore/exceptions.py#L482-L486
To be copyable the WaiterError class needs also to implement the __reduce__ method to match its __init__ method.
https://github.com/boto/botocore/blob/a692f50213f4480f5fdf8faf30b87ad362bf0519/botocore/exceptions.py#L414-L420
Hi @iainelder,
Thanks for the report. We'll take a look to see what the best solution is to make this exception copyable.
This might overlap somewhat with https://github.com/boto/boto3/issues/1221
Is there a reason why the below function can't be added to the WaiterError class?
def __reduce__(self):
return _exception_from_packed_args, (
self.__class__,
(self.kwargs["name"], self.kwargs["reason"], self.last_response),
{},
)
becaue my code is now outputing the below text and I have no clue where it came from because it's an asynchronous error
Exception in thread Thread-3 (_handle_results): Traceback (most recent call last): File "/usr/local/lib/python3.11/threading.py", line 1038, in _bootstrap_inner self.run() File "/usr/local/lib/python3.11/threading.py", line 975, in run self._target(*self._args, **self._kwargs) File "/usr/local/lib/python3.11/multiprocessing/pool.py", line 579, in _handle_results task = get() ^^^^^ File "/usr/local/lib/python3.11/multiprocessing/connection.py", line 250, in recv return _ForkingPickler.loads(buf.getbuffer()) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/lib/python3.11/site-packages/botocore/exceptions.py", line 28, in _exception_from_packed_args return exception_cls(*args, **kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ TypeError: WaiterError.init() missing 1 required positional argument: 'last_response'
One workaround is patching it
import botocore.exceptions
def wait_error_reduce(wait_error: botocore.exceptions.WaiterError) -> botocore.exceptions.WaiterError:
return botocore.exceptions._exception_from_packed_args, (
wait_error.__class__,
(wait_error.kwargs["name"], wait_error.kwargs["reason"], wait_error.last_response),
{},
)
botocore.exceptions.WaiterError.__reduce__ = wait_error_reduce