-
Notifications
You must be signed in to change notification settings - Fork 33
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
Unpickling exceptions can fail with TypeError
about the number of arguments given in specific scenarios
#65
Comments
I run into the same problem if we have a mandatory keyword argument:
If you look at what's in If I add in a positional argument the error message changes but the fundamental problem remains the same.
Again, the only argument preserved is the single argument passed to the superconstructor; the arguments I passed to the original exception are gone. The underlying issue appears to be that the original arguments passed to the constructor of the leaf class are not preserved, only what |
Same for some standard library exceptions, like
|
And I see that pickle library suffers from the same issue, so this might not be
|
The following change to tblib did the trick for me: def unpickle_exception(func, args, cause, tb, context=None, suppress_context=False, notes=None):
inst = func.__new__(func)
if args is not None:
inst.args = args
inst.__cause__ = cause
inst.__traceback__ = tb
inst.__context__ = context
inst.__suppress_context__ = suppress_context
if notes is not None:
inst.__notes__ = notes
return inst
def pickle_exception(obj: BaseException):
rv = obj.__reduce_ex__(3)
if isinstance(rv, str):
raise TypeError('str __reduce__ output is not supported')
assert isinstance(rv, tuple)
assert len(rv) >= 2
return (
unpickle_exception,
rv[:1]
+ (
obj.args,
obj.__cause__,
obj.__traceback__,
obj.__context__,
obj.__suppress_context__,
# __notes__ doesn't exist prior to Python 3.11; and even on Python 3.11 it may be absent
getattr(obj, '__notes__', None),
),
) + rv[2:] I also had to add pickling of |
Exception to IOError is because this built-in exception is assigning internal `myerrno` attribute in __init__ constructor and cannot be changed from Python code. Fixes: ionelmc#65 Signed-off-by: Oldřich Jedlička <oldium.pro@gmail.com>
Exception to IOError is because this built-in exception is assigning internal myerrno attribute in __init__ constructor and cannot be changed from Python code. Fixes: ionelmc#65 Signed-off-by: Oldřich Jedlička <oldium.pro@gmail.com>
Pull Request #73 should (hopefully) address the issue, at least it works-for-me™. |
Exception to IOError is because this built-in exception is assigning internal myerrno attribute in __init__ constructor and cannot be changed from Python code. Fixes: ionelmc#65 Signed-off-by: Oldřich Jedlička <oldium.pro@gmail.com>
You can't really pickle a live connection object anyway. Have you tried registering a custom handler for NameResolutionError in copyreg? |
Exception to IOError is because this built-in exception is assigning internal myerrno attribute in __init__ constructor and cannot be changed from Python code. Fixes: ionelmc#65 Signed-off-by: Oldřich Jedlička <oldium.pro@gmail.com>
Exception to IOError is because this built-in exception is assigning internal myerrno attribute in __init__ constructor and cannot be changed from Python code. Fixes: ionelmc#65 Signed-off-by: Oldřich Jedlička <oldium.pro@gmail.com>
Exception to IOError is because this built-in exception is assigning internal myerrno attribute in __init__ constructor and cannot be changed from Python code. Fixes: ionelmc#65 Signed-off-by: Oldřich Jedlička <oldium.pro@gmail.com>
That is correct. This is why I registered custom handler for |
Exception to IOError is because this built-in exception is assigning internal myerrno attribute in __init__ constructor and cannot be changed from Python code. Fixes: ionelmc#65 Signed-off-by: Oldřich Jedlička <oldium.pro@gmail.com>
…elmc#65 Signed-off-by: Oldřich Jedlička <oldium.pro@gmail.com>
…elmc#65 Signed-off-by: Oldřich Jedlička <oldium.pro@gmail.com>
Using of the class constructor and passing it the args values is not always correct, the custom parameters could be different to the args actually recorded by the built-in Exception class. Try to use the __new__ to create class instance and initialize the args attribute afterwards to prevent calling constructor with wrong arguments. Other instance attributes are initialized in a standard way by pickle module, so this way the reconstruction of the exception is complete. Subclasses of OSError are known exceptions to this, because they initialize read-only attributes from the constructor argument values, so usage of the __new__ factory method leads to lost values of those read-only attributes. Fixes: ionelmc#65 Signed-off-by: Oldřich Jedlička <oldium.pro@gmail.com>
…elmc#65 Signed-off-by: Oldřich Jedlička <oldium.pro@gmail.com>
Using of the class constructor and passing it the args values is not always correct, the custom parameters could be different to the args actually recorded by the built-in Exception class. Try to use the __new__ to create class instance and initialize the args attribute afterwards to prevent calling constructor with wrong arguments. Other instance attributes are initialized in a standard way by pickle module, so this way the reconstruction of the exception is complete. Subclasses of OSError are known exceptions to this, because they initialize read-only attributes from the constructor argument values, so usage of the __new__ factory method leads to lost values of those read-only attributes. Fixes: ionelmc#65 Signed-off-by: Oldřich Jedlička <oldium.pro@gmail.com>
…elmc#65 Signed-off-by: Oldřich Jedlička <oldium.pro@gmail.com>
Using of the class constructor and passing it the args values is not always correct, the custom parameters could be different to the args actually recorded by the built-in Exception class. Try to use the __new__ to create class instance and initialize the args attribute afterwards to prevent calling constructor with wrong arguments. Other instance attributes are initialized in a standard way by pickle module, so this way the reconstruction of the exception is complete. Subclasses of OSError are known exceptions to this, because they initialize read-only attributes from the constructor argument values, so usage of the __new__ factory method leads to lost values of those read-only attributes. Fixes: ionelmc#65 Signed-off-by: Oldřich Jedlička <oldium.pro@gmail.com>
…elmc#65 Signed-off-by: Oldřich Jedlička <oldium.pro@gmail.com>
Just a note, this is how I fixed pickling of locks in the exception instances: def unpickle_thread_lock():
return _thread.allocate_lock()
def pickle_thread_lock(obj):
return unpickle_thread_lock, ()
class MyPickler(pickle.Pickler):
def reducer_override(self, obj):
if isinstance(obj, _thread.LockType):
return pickle_thread_lock(obj)
elif isinstance(obj, TracebackType):
return tblib.pickling_support.pickle_traceback(obj)
elif isinstance(obj, BaseException):
return tblib.pickling_support.pickle_exception(obj)
else:
# For any other object, fallback to usual reduction
return NotImplemented Or if you are interested just in locks, you can use global pickling registry: copyreg.pickle(_thread.LockType, pickle_thread_lock) |
Using of the class constructor and passing it the args values is not always correct, the custom parameters could be different to the args actually recorded by the built-in Exception class. Try to use the __new__ to create class instance and initialize the args attribute afterwards to prevent calling constructor with wrong arguments. Other instance attributes are initialized in a standard way by pickle module, so this way the reconstruction of the exception is complete. Subclasses of OSError are known exceptions to this, because they initialize read-only attributes from the constructor argument values, so usage of the __new__ factory method leads to lost values of those read-only attributes. Fixes: ionelmc#65 Signed-off-by: Oldřich Jedlička <oldium.pro@gmail.com>
…elmc#65 Signed-off-by: Oldřich Jedlička <oldium.pro@gmail.com>
Using of the class constructor and passing it the args values is not always correct, the custom parameters could be different to the args actually recorded by the built-in Exception class. Try to use the __new__ to create class instance and initialize the args attribute afterwards to prevent calling constructor with wrong arguments. Other instance attributes are initialized in a standard way by pickle module, so this way the reconstruction of the exception is complete. Subclasses of OSError are known exceptions to this, because they initialize read-only attributes from the constructor argument values, so usage of the __new__ factory method leads to lost values of those read-only attributes. Fixes: ionelmc#65 Signed-off-by: Oldřich Jedlička <oldium.pro@gmail.com>
…elmc#65 Signed-off-by: Oldřich Jedlička <oldium.pro@gmail.com>
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:
pickle_exception
and it's counterunpickle_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: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:
The text was updated successfully, but these errors were encountered: