-
-
Notifications
You must be signed in to change notification settings - Fork 32.4k
bpo-32226: Implementation of PEP 560 (core components) #4732
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
Conversation
@serhiy-storchaka Thank you very much for review! I will make the changes tomorrow. |
I have made the requested changes; please review again |
Thanks for making the requested changes! @serhiy-storchaka: please review the changes made to this pull request. |
@serhiy-storchaka Following your comment #4731 (comment) I also removed callability checks for special methods here. If you think it makes sense to customize error messages in future, then I can open a b.p.o. issue for this. |
(I will add documentation for this in a separate PR after the second part of changes -- changes to |
Python/bltinmodule.c
Outdated
goto error; | ||
} | ||
if (!replacements) { | ||
replacements = PyTuple_New(nargs - 2); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Handle an error.
I suggest to use a list instead of a tuple. You can fill it at the single pass and convert to a tuple at end.
if (!new_bases) {
new_bases = PyList_New(i);
if (!new_bases) {
goto error;
}
// copy i items to new_bases
}
if (_PyList_Extend(new_bases, new_base) < 0) {
goto error;
}
Python/bltinmodule.c
Outdated
replacements = NULL; | ||
/* We have a separate cycle to calculate replacements with the idea that in | ||
most cases we just scroll quickly though it and return original bases */ | ||
for (i = 2; i < nargs; i++){ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the code would look simpler if pass nargs-2
and args+2
. Or passing just bases
may be enough.
Needed a space before {
.
Python/bltinmodule.c
Outdated
@@ -206,6 +298,9 @@ builtin___build_class__(PyObject *self, PyObject **args, Py_ssize_t nargs, | |||
Py_DECREF(meta); | |||
Py_XDECREF(mkw); | |||
Py_DECREF(bases); | |||
if (modified_bases) { | |||
Py_DECREF(old_bases); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just
Py_XDECREF(old_bases);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not really possible, since this code is also executed on success, and in this case if there were no conversion, then bases
should be DECREF
ed exactly once.
Python/bltinmodule.c
Outdated
return NULL; | ||
} | ||
else { | ||
old_bases = bases; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code would look simpler if set the original bases to orig_bases
.
orig_bases = _PyStack_AsTupleSlice(...);
if (orig_bases == NULL)
return NULL;
bases = update_bases(...);
if (bases == NULL) {
Py_DECREF(orig_bases);
return NULL;
}
Python/bltinmodule.c
Outdated
@@ -168,6 +255,11 @@ builtin___build_class__(PyObject *self, PyObject **args, Py_ssize_t nargs, | |||
NULL, 0, NULL, 0, NULL, 0, NULL, | |||
PyFunction_GET_CLOSURE(func)); | |||
if (cell != NULL) { | |||
if (modified_bases) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (bases != orig_bases) {
modified_bases
is not needed.
@serhiy-storchaka Thank you for another review! I converted the code to the single pass as you suggested. The code indeed looks cleaner, however performance is worse now. For normal (non-generic) classes there is slow-down of up to 5%. Do you have any ideas about how to boost performance here? |
@serhiy-storchaka Ah, sorry, I forgot to merge master before bechmarking. In fact, there are no visible slow-downs. (Which makes sense, since I just added few pointer comparisons.) |
Python/bltinmodule.c
Outdated
PyErr_Clear(); | ||
if (new_bases) { | ||
if (PyList_Append(new_bases, base) < 0) { | ||
Py_DECREF(meth); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
meth is NULL here.
Python/bltinmodule.c
Outdated
if (!new_base) { | ||
goto error; | ||
} | ||
if (PyTuple_Check(new_base)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suggest to swap branches and move the success branch to upper level. I think this will make the code clearer.
if (!PyTuple_Check(new_base)) {
...
goto error;
}
if (!new_bases) {
...
Python/bltinmodule.c
Outdated
} | ||
continue; | ||
} | ||
if (!(meth = _PyObject_GetAttrId(base, &PyId___mro_entries__))) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would look clearer if make an assignment on a separate line.
Python/bltinmodule.c
Outdated
PyObject *stack[1] = {bases}; | ||
assert(PyTuple_Check(bases)); | ||
|
||
/* We have a separate cycle to calculate replacements with the idea that in |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this comment outdated?
Python/bltinmodule.c
Outdated
|
||
/* We have a separate cycle to calculate replacements with the idea that in | ||
most cases we just scroll quickly though it and return original bases */ | ||
for (i = 0; i < nargs; i++) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For performance try to add a separate fast loop for non-generic types.
for (i = 0; i < nargs && PyType_Check(args[i]); i++) {
}
if (i == nargs) {
return bases;
}
new_bases = PyList_New(i);
...
for (; i < nargs; i++) {
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't give any visible difference, so I will just keep the single loop (As I mentioned before, a more careful benchmark showed that it is already good).
Python/bltinmodule.c
Outdated
if (!new_bases) { | ||
/* If this is a first successful conversion, create new_bases list and | ||
copy previously encountered bases. */ | ||
new_bases = PyList_New(i); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Check for error.
Python/bltinmodule.c
Outdated
if (!_PyList_Extend((PyListObject *)new_bases, new_base)) { | ||
goto error; | ||
} | ||
Py_DECREF(Py_None); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks confusing. Add a comment to this non-obvious code.
Alternatively you can use a public PyList_SetSlice instead of _PyList_Extend if you prefer.
j = PyList_GET_SIZE(new_bases);
if (PyList_SetSlice(new_bases, j, j, new_base) < 0) {
goto error;
}
Python/bltinmodule.c
Outdated
@@ -46,12 +47,85 @@ _Py_IDENTIFIER(stderr); | |||
|
|||
#include "clinic/bltinmodule.c.h" | |||
|
|||
static PyObject* | |||
update_bases(PyObject* bases, PyObject** args, int nargs) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add the const
qualifier. See bpo-32240.
It is more common to add a space before *
instead of after it.
Python/bltinmodule.c
Outdated
result = PyList_AsTuple(new_bases); | ||
Py_DECREF(new_bases); | ||
return result; | ||
error: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is just my personal preference, but I prefer to add an empty line after return
and before a label (closing brace can replace an empty line). This make the code cleaner to me.
@serhiy-storchaka Does this PR look OK to you now? (Taking into account that Guido likes the current implementation "strategy" of I am really sorry for constantly pushing you on this. There are few other things that depend on this PR |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. This introduces a small performance regression, but it can be be fixed later.
OK, thanks! Then I will merge this now. |
This is a basic implementation of the PEP. Suggestions are welcome.
@gvanrossum @serhiy-storchaka Can one of you (or both?) please review?
cc: @markshannon IIRC you are interested in implementation details of this PEP.
https://bugs.python.org/issue32226