Skip to content

Unpickling exceptions can fail with TypeError about the number of arguments given in specific scenarios #65

Open
@kezabelle

Description

@kezabelle

Firstly, thanks so much for this library and the time you've spent on it!

This is a real edge case, and the library I encountered it in (httpretty) doesn't even do this exception in quite the same way now, but I'm flagging this because it's an exotic usage that has required my investigating, and perhaps it's otherwise unknown to you (though tbf, it may be a limitation, and I'm certainly not expecting a 'fix' from you!)

Using:

httpretty==1.0.5
tblib==1.7.0

pickle_exception and it's counter unpickle_exception can become confused about how to restore the exception, if the exception is defined the same way as it was in httpretty at that version, like so:

class HTTPrettyError(Exception):
    pass

class UnmockedError(HTTPrettyError):
    def __init__(self):
        super(UnmockedError, self).__init__(
            'No mocking was registered, and real connections are '
            'not allowed (httpretty.allow_net_connect = False).'
        )

It's important to note that the __init__ doesn't declare any params, but internally passes some up so that they're ultimately set into .args

If we then try and pickle & unpickle that, like so:

In [1]: from httpretty import UnmockedError
In [2]: from tblib import pickling_support
In [3]: x = UnmockedError()
In [4]: repr(x)
Out[4]: "UnmockedError('No mocking was registered, and real connections are not allowed (httpretty.allow_net_connect = False).')"

In [5]: str(x)
Out[5]: 'No mocking was registered, and real connections are not allowed (httpretty.allow_net_connect = False).'

In [6]: func, params = pickling_support.pickle_exception(x)
In [7]: func(*params)
File ~/path/to/site-packages/tblib/pickling_support.py:26, in unpickle_exception(func, args, cause, tb)
     25 def unpickle_exception(func, args, cause, tb):
---> 26     inst = func(*args)
     27     inst.__cause__ = cause
     28     inst.__traceback__ = tb
TypeError: UnmockedError.__init__() takes 1 positional argument but 2 were given

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions