Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

bpo-27015: Save kwargs given to exceptions constructor#11580

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Open
remilapeyre wants to merge9 commits intopython:main
base:main
Choose a base branch
Loading
fromremilapeyre:BaseException-does-not-unpickle

Conversation

remilapeyre
Copy link
Contributor

@remilapeyreremilapeyre commentedJan 16, 2019
edited by bedevere-bot
Loading

Fix unpickling bug when exception class expect keyword arguments

https://bugs.python.org/issue27015

@remilapeyreremilapeyre changed the titleSave kwargs given to exceptions constructorbpo-27015: Save kwargs given to exceptions constructorJan 16, 2019
Fix unpickling bug when exception class expect keyword arguments
@remilapeyreremilapeyreforce-pushed theBaseException-does-not-unpickle branch fromce25dc8 to6a00f21CompareJanuary 16, 2019 14:53
@bedevere-bot
Copy link

A Python core developer has requested some changes be made to your pull request before we can consider merging it. If you could please address their requests along with any other requests in other reviews from core developers that would be appreciated.

Once you have made the requested changes, please leave a comment on this pull request containing the phraseI have made the requested changes; please review again. I will then notify any core developers who have left a review that you're ready for them to take another look at this pull request.

@remilapeyre
Copy link
ContributorAuthor

I have made the requested changes; please review again

@bedevere-bot
Copy link

Thanks for making the requested changes!

@gpshead: please review the changes made to this pull request.

}
key = PyTuple_GET_ITEM(item, 0);
value = PyTuple_GET_ITEM(item, 1);
PyTuple_SET_ITEM(seq, i, PyUnicode_FromFormat("%S=%R", key, value));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Unless I am missing somethingPyUnicode_FromFormat can fail if the _PyUnicodeWriter fails to write the string. Please, add error checking around all calls toPyUnicode_FromFormat

Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

There is no mention thatPyUnicode_FromFormat can fail athttps://docs.python.org/3/c-api/unicode.html#c.PyUnicode_FromFormat

Should I add a note about this?

@bedevere-bot
Copy link

A Python core developer has requested some changes be made to your pull request before we can consider merging it. If you could please address their requests along with any other requests in other reviews from core developers that would be appreciated.

Once you have made the requested changes, please leave a comment on this pull request containing the phraseI have made the requested changes; please review again. I will then notify any core developers who have left a review that you're ready for them to take another look at this pull request.

@remilapeyre
Copy link
ContributorAuthor

Thansks@pablogsal, I have made the requested changes; please review again

@bedevere-bot
Copy link

Thanks for making the requested changes!

@gpshead,@pablogsal: please review the changes made to this pull request.

Copy link
Contributor

@ncoghlanncoghlan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

This is a solid start, but the significant increase in the complexity of the__reduce__ implementation bothers me. It would be really nice if we could migrate exceptions to using the newer__get_newargs_ex__ API instead, as that natively handles exactly this problem.

@@ -87,6 +87,10 @@ The following exceptions are used mostly as base classes for other exceptions.
assign a special meaning to the elements of this tuple, while others are
usually called only with a single string giving an error message.

.. attribute:: kwargs

The dictionnary of keyword arguments given to the exception constructor.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Typo: dictionnary -> dictionary

Py_DECREF(self);
return NULL;
}
self->kwargs = kwds;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

This assignment should only be done ifkwds is a non-empty dict, otherwise we're risking increasing the size of exception instances bysys.getsizeof({}) bytes for no compelling reason (that's an extra 288 bytes on my machine, vs the current 88 bytes for aBaseException instance).

While calls likeSomeException() would getNULL for kwds, consider code likesuper().__new__(*some_args, **maybe_some_kwds) in exception subclasses.

Ifkwds is NULL, or an empty dict, thenself->kwargs should be set to theNone singleton.

Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Wouldn't it be good to do that forargs too?

if (val == NULL) {
PyErr_SetString(PyExc_TypeError, "kwargs may not be deleted");
return -1;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

val needs to be checked to make sure it is a mapping (withPyMapping_Check) orNone.

Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I thought about doing that but it seems to me thatPyMapping_Check is only a loose check, for example, wouldn't we be able to do:

>>> b = BaseException()>>> b.kwargs = ()

since tuples support slicing?

If so, is the check still worth it?

PyObject **newargs;

_Py_IDENTIFIER(partial);
functools = PyImport_ImportModule("functools");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

This reduce implementation concerns me, as it looks like it will make everything much slower, even for exception instances whereself->kwargs isn't set.

Instead, I'd recommend migratingBaseException away from implementing__reduce__ directly, and instead have it implement__getnewargs_ex__:https://docs.python.org/3/library/pickle.html#object.__getnewargs_ex__

That way the pickle machinery will take care of calling__new__ with the correct arguments, and you wouldn't need to introduce a weird dependency from a core builtin into a standard library module.

(That would have potential backwards compatibility implications for subclasses implementing reduce based on the parent class implementation, but the same would hold true for introduce a partial object in place of a direct reference to the class - either way, there'll need to be a note in the Porting section of the What's New guide, and switching to__get_newargs_ex__ will at least have the virtue of simplifying the code rather than making it more complicated)

Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I tried to do that, I removed__reduce__ and added__getnewargs_ex__ to the methods as:

staticPyObject*BaseException_getnewargs_ex(PyBaseExceptionObject*self,PyObject*Py_UNUSED(ignored)){PyObject*args=PyObject_GetAttrString((PyObject*)self,"args");PyObject*kwargs=PyObject_GetAttrString((PyObject*)self,"kwargs");if (args==NULL||kwargs==NULL) {returnNULL;    }returnPy_BuildValue("(OO)",args,kwargs);}

but it brocke pickling. Did I miss something?

Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Oh, found my mistake, using__getnewargs_ex__ broke pickling for protocols 0 and 1. Is this expected?

I don't think this happened when using a partial reference on the the constructor of the class.

Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Maybe it's ok to broke pickling support for protocols 0 and 1 since it was broken for keyword args anyway?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Defining__reduce_ex__ would let you restore the old behaviour for those protocols, but I'm not sure__getnewargs_ex__ will still be called if you do that (we're reaching the limits of my ownpickle knowledge).

@pitrou Do you have any recommendations here? (Context: trying to getBaseException to pickle keyword args properly, wanting to use__getnewargs_ex__ for more recent pickle protocols, but wondering how to handle the older protocols that don't use that)

Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

How should I callobject.__reduce_ex__?

It seems to me that calling the builtin super is not done anywhere in the source code but I don't find the right way to do it.

Do I need to callobject___reduce_ex__ directly?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

@ncoghlan Well, I'm not sure why you wouldn't implement the entire logic in__reduce_ex__, instead of also defining__getnewargs_ex__?

Or, rather, you could just define__getnewargs_ex__ and stop caring about protocols 0 and 1 (which are extremely obsolete by now, so we want to maintain compatibility, but fixing bugs in them is not important).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

@pitrou I only suggested delegating to__getnewargs_ex__ because I wasn't sure how to mimic that behaviour from inside a custom__reduce_ex__ implementation.

But if__reduce__ still gets called for protocols 0 and 1 even when__getnewargs_ex__ is defined, then that's even better.

Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Hi@pitrou@ncoghlan, thanks for you input. I pushed a new commit that implement__getnewargs_ex__ but it seems that__reduce_ex__ does not check it and call__reduce__ no matter what the protocol is:

>>> BaseException().__reduce_ex__(0)(<class 'BaseException'>, ())>>> BaseException().__reduce_ex__(1)(<class 'BaseException'>, ())>>> BaseException().__reduce_ex__(2)(<class 'BaseException'>, ())>>> BaseException().__reduce_ex__(3)(<class 'BaseException'>, ())>>> BaseException().__reduce_ex__(4)(<class 'BaseException'>, ())>>> BaseException().__getnewargs_ex__()((), {})

If I remove the__reduce__, then it breaks pickling for protocols 0 and 1:

>>> BaseException().__reduce_ex__(0)Traceback (most recent call last):  File "<stdin>", line 1, in <module>  File "/Users/remi/src/cpython/Lib/copyreg.py", line 66, in _reduce_ex    raise TypeError(f"cannot pickle {cls.__name__!r} object")TypeError: cannot pickle 'BaseException' object>>> BaseException().__reduce_ex__(1)Traceback (most recent call last):  File "<stdin>", line 1, in <module>  File "/Users/remi/src/cpython/Lib/copyreg.py", line 66, in _reduce_ex    raise TypeError(f"cannot pickle {cls.__name__!r} object")TypeError: cannot pickle 'BaseException' object>>> BaseException().__reduce_ex__(2)(<function __newobj__ at 0x105c63040>, (<class 'BaseException'>,), None, None, None)>>> BaseException().__reduce_ex__(3)(<function __newobj__ at 0x105c63040>, (<class 'BaseException'>,), None, None, None)>>> BaseException().__reduce_ex__(4)(<function __newobj__ at 0x105c63040>, (<class 'BaseException'>,), None, None, None)

Do I need to define a custom__reduce_ex__ as well?

Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I dug further and it seems my issue comes fromhttps://github.com/python/cpython/blob/master/Lib/copyreg.py#L66, I will look into the details tomorrow.

@remilapeyre
Copy link
ContributorAuthor

remilapeyre commentedFeb 2, 2019 via email

Le sam. 2 févr. 2019 à 16:06, Nick Coghlan <notifications@github.com> aécrit :
***@***.**** commented on this pull request. ------------------------------ In Objects/exceptions.c <#11580 (comment)>: > } /* Pickling support */ static PyObject * BaseException_reduce(PyBaseExceptionObject *self, PyObject *Py_UNUSED(ignored)) { - if (self->args && self->dict) - return PyTuple_Pack(3, Py_TYPE(self), self->args, self->dict); - else - return PyTuple_Pack(2, Py_TYPE(self), self->args); + PyObject *functools; + PyObject *partial; + PyObject *constructor; + PyObject *result; + PyObject **newargs; + + _Py_IDENTIFIER(partial); + functools = PyImport_ImportModule("functools"); Thanks@pitrou <https://github.com/pitrou>. Looking athttps://github.com/python/cpython/blob/5c8f537669d3379fc50bb0a96accac756e43e281/Objects/typeobject.c#L4414, I think what a BaseException.__reduce_ex__ implementation could do is: 1. Define __getnewargs_ex__ 2. For protocol 2 and higher, call up to object.__reduce_ex__ (which will then invoke __getnewargs_ex__) 3. For protocols 0 and 1, delegate to the existing BaseException.__reduce__ Does that sound plausible, or have I missed something that will keep that approach from working?
I still need to update __reduce__ to use the partial function thoughotherwise support for pickling would still be broken for protocol 0 and 1wouldn't it ?—
You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub <#11580 (comment)>, or mute the thread <https://github.com/notifications/unsubscribe-auth/AhkhUAP6IAezkA6CcBGAkijSiB3SXTNsks5vJamSgaJpZM4aDKP_> .

@ncoghlan
Copy link
Contributor

I still need to updatereduce to use the partial function though otherwise support for pickling would still be broken for protocol 0 and 1 wouldn't it ?

It would, but there's no good reason for anyone to emit protocols lower than 2, since that was introduced all the way back in Python 2.3 (https://docs.python.org/3/library/pickle.html#data-stream-format).

The key part is to notbreak pickling & unpickling with those protocols - you don't need to enhance it.

@remilapeyreremilapeyreforce-pushed theBaseException-does-not-unpickle branch from5de8cf2 to4657d7eCompareFebruary 21, 2019 15:03
@remilapeyre
Copy link
ContributorAuthor

Hi@ncoghlan @pitroum, I made some changes, could you review again?

Seeing the increase in complexity to support slots and data-descriptors, I may have misunderstood what you were saying. If so, I will be happy to scratch that and do it again.

I tried my best to remove all refleaks but doing./python.exe -m test -R : test_exceptions gives:

Run tests sequentially0:00:00 load avg: 1.99 [1/1] test_exceptionsbeginning 9 repetitions123456789.........test_exceptions leaked [3, 3, 3, 3] references, sum=12test_exceptions failed== Tests result: FAILURE ==1 test failed:    test_exceptionsTotal duration: 16 sec 535 msTests result: FAILURE

This means I missed one, doesn't it?

It seems to me that argument clinic has not been used forObjects/exceptions.c, would a PR that convert it useful?

@github-actionsGitHub Actions
Copy link

This PR is stale because it has been open for 30 days with no activity.

@github-actionsgithub-actionsbot added the staleStale PR or inactive for long period of time. labelApr 13, 2025
Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment
Reviewers

@ncoghlanncoghlanncoghlan left review comments

@pitroupitroupitrou left review comments

@gpsheadgpsheadgpshead requested changes

@pablogsalpablogsalpablogsal requested changes

Assignees
No one assigned
Labels
awaiting change reviewstaleStale PR or inactive for long period of time.
Projects
None yet
Milestone
No milestone
Development

Successfully merging this pull request may close these issues.

8 participants
@remilapeyre@bedevere-bot@ncoghlan@gpshead@pitrou@pablogsal@the-knights-who-say-ni@ezio-melotti

[8]ページ先頭

©2009-2025 Movatter.jp