Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Copying WaiterError causes a TypeError #2617

Open
iainelder opened this issue Feb 11, 2022 · 4 comments
Open

Copying WaiterError causes a TypeError #2617

iainelder opened this issue Feb 11, 2022 · 4 comments
Labels
bug This issue is a confirmed bug. p3 This is a minor priority issue waiter

Comments

@iainelder
Copy link

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.

def __reduce__(self):
# Subclasses of ClientError's are dynamically generated and
# cannot be pickled unless they are attributes of a
# module. So at the very least return a ClientError back.
return ClientError, (self.response, self.operation_name)

To be copyable the WaiterError class needs also to implement the __reduce__ method to match its __init__ method.

class WaiterError(BotoCoreError):
"""Waiter failed to reach desired state."""
fmt = 'Waiter {name} failed: {reason}'
def __init__(self, name, reason, last_response):
super(WaiterError, self).__init__(name=name, reason=reason)
self.last_response = last_response

@iainelder iainelder added the needs-triage This issue or PR still needs to be triaged. label Feb 11, 2022
@kdaily kdaily added bug This issue is a confirmed bug. and removed needs-triage This issue or PR still needs to be triaged. labels Feb 14, 2022
@kdaily
Copy link
Member

kdaily commented Feb 14, 2022

Hi @iainelder,

Thanks for the report. We'll take a look to see what the best solution is to make this exception copyable.

@RyanFitzSimmonsAK RyanFitzSimmonsAK added the p3 This is a minor priority issue label Nov 3, 2022
@tim-finnigan
Copy link
Contributor

This might overlap somewhat with boto/boto3#1221

@hb2638
Copy link

hb2638 commented Jun 15, 2023

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'

@hb2638
Copy link

hb2638 commented Jun 15, 2023

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug This issue is a confirmed bug. p3 This is a minor priority issue waiter
Projects
None yet
Development

No branches or pull requests

5 participants