Skip to content

bpo-32226: Implementation of PEP 560 (core components)#4732

Merged
ilevkivskyi merged 40 commits intopython:masterfrom
ilevkivskyi:base-subclass
Dec 14, 2017
Merged

bpo-32226: Implementation of PEP 560 (core components)#4732
ilevkivskyi merged 40 commits intopython:masterfrom
ilevkivskyi:base-subclass

Conversation

@ilevkivskyi
Copy link
Copy Markdown
Member

@ilevkivskyi ilevkivskyi commented Dec 5, 2017

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

@ilevkivskyi
Copy link
Copy Markdown
Member Author

@serhiy-storchaka Thank you very much for review! I will make the changes tomorrow.

@ilevkivskyi
Copy link
Copy Markdown
Member Author

I have made the requested changes; please review again

@bedevere-bot
Copy link
Copy Markdown

Thanks for making the requested changes!

@serhiy-storchaka: please review the changes made to this pull request.

@ilevkivskyi
Copy link
Copy Markdown
Member Author

@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.

@ilevkivskyi
Copy link
Copy Markdown
Member Author

(I will add documentation for this in a separate PR after the second part of changes -- changes to typing -- will be in.)

Comment thread Python/bltinmodule.c Outdated
goto error;
}
if (!replacements) {
replacements = PyTuple_New(nargs - 2);
Copy link
Copy Markdown
Member

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;
}

Comment thread 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++){
Copy link
Copy Markdown
Member

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 {.

Comment thread Python/bltinmodule.c Outdated
Py_XDECREF(mkw);
Py_DECREF(bases);
if (modified_bases) {
Py_DECREF(old_bases);
Copy link
Copy Markdown
Member

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);

Copy link
Copy Markdown
Member Author

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 DECREFed exactly once.

Comment thread Python/bltinmodule.c Outdated
return NULL;
}
else {
old_bases = bases;
Copy link
Copy Markdown
Member

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;
}

Comment thread Python/bltinmodule.c Outdated
NULL, 0, NULL, 0, NULL, 0, NULL,
PyFunction_GET_CLOSURE(func));
if (cell != NULL) {
if (modified_bases) {
Copy link
Copy Markdown
Member

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.

@ilevkivskyi
Copy link
Copy Markdown
Member Author

@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?

@ilevkivskyi
Copy link
Copy Markdown
Member Author

@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.)

Comment thread Python/bltinmodule.c Outdated
PyErr_Clear();
if (new_bases) {
if (PyList_Append(new_bases, base) < 0) {
Py_DECREF(meth);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

meth is NULL here.

Comment thread Python/bltinmodule.c Outdated
if (!new_base) {
goto error;
}
if (PyTuple_Check(new_base)) {
Copy link
Copy Markdown
Member

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) {
    ...

Comment thread Python/bltinmodule.c Outdated
}
continue;
}
if (!(meth = _PyObject_GetAttrId(base, &PyId___mro_entries__))) {
Copy link
Copy Markdown
Member

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.

Comment thread 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
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is this comment outdated?

Comment thread Python/bltinmodule.c

/* 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++) {
Copy link
Copy Markdown
Member

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++) {

Copy link
Copy Markdown
Member Author

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).

Comment thread 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);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Check for error.

Comment thread Python/bltinmodule.c Outdated
if (!_PyList_Extend((PyListObject *)new_bases, new_base)) {
goto error;
}
Py_DECREF(Py_None);
Copy link
Copy Markdown
Member

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;
}

Comment thread Python/bltinmodule.c Outdated
#include "clinic/bltinmodule.c.h"

static PyObject*
update_bases(PyObject* bases, PyObject** args, int nargs)
Copy link
Copy Markdown
Member

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.

Comment thread Python/bltinmodule.c
result = PyList_AsTuple(new_bases);
Py_DECREF(new_bases);
return result;
error:
Copy link
Copy Markdown
Member

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.

@ilevkivskyi
Copy link
Copy Markdown
Member Author

@serhiy-storchaka Does this PR look OK to you now? (Taking into account that Guido likes the current implementation "strategy" of __class_getitem__, see https://bugs.python.org/issue32226#msg308171)

I am really sorry for constantly pushing you on this. There are few other things that depend on this PR

Copy link
Copy Markdown
Member

@serhiy-storchaka serhiy-storchaka left a 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.

@ilevkivskyi
Copy link
Copy Markdown
Member Author

LGTM. This introduces a small performance regression, but it can be be fixed later

OK, thanks! Then I will merge this now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants