8000 Fix `__module__` attribute for shared types by da-woods · Pull Request #6845 · cython/cython · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Fix __module__ attribute for shared types #6845

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

Open
wants to merge 7 commits into
base: master
Choose a base branch
from

Conversation

da-woods
Copy link
Contributor
@da-woods da-woods commented May 6, 2025

It can't be a descriptor because that messes up
type(func).__module__. I decided not to put it in __dict__ because that involves instantiating __dict__ for every instance (which we've previously decided not to do) and also deciding on the behaviour when __dict__ is replaced.

Therefore the solution seems to be to add some special casing to tp_getattro and tp_setattro.

Fixes #6841.

@@ -1771,7 +1771,7 @@ static PyObject *__Pyx_CoroutineAwait_no_new(PyTypeObject *type, PyObject *args,
// We are applying this to all Python versions (hence the commented out version guard)
// to make the behaviour explicit.
// #if CYTHON_USE_TYPE_SPECS
static PyObject *__Pyx_CoroutineAwait_reduce_ex(__pyx_CoroutineAwaitObject *self, PyObject *arg) {
static PyObject *__Pyx_CoroutineTypes_reduce_ex(PyObject *self, PyObject *arg) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

For reasons I don't understand, changing the definition of __module__ magically made coroutines pickleable under protocols 1 and 2. Therefore I just explicitly banned it using the same function as CoroutineAwait uses.

It can't be a descriptor because that messes up
`type(func).__module__`. I decided not to put it in `__dict__`
because that involves instantiating `__dict__` for every instance
(which we've previously decided not to do) and also deciding on
the behaviour when `__dict__` is replaced.

Therefore the solution seems to be to add some special casing
to tp_getattro and tp_setattro.

Fixes cython#6841.
"""
>>> test_module.__module__
'cyfunction'
>>> type(test_module).__module__.startswith("_cython") 8000
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This seems to be a bit fragile locally, because for some reason I always end up with an older cached cyfunction implementation.

It works when I manually import the module alone but not when I use runtests. I'm not sure what the CI will end up doing, but I'm fairly sure this is a caching problem and will go away when the shared lib version changes.

@scoder
Copy link
Contributor
scoder commented May 7, 2025 via email

@da-woods
Copy link
Contributor Author
da-woods commented May 7, 2025

I'm always wary of changing attribute access away from PyObject_GenericGetAttr due to CPython's internal optimisations.

Agree

the pickle changes seem risky.

I don't think it's as bad as it seems - the non-pickleability of coroutines

def test_pickle(self):
is already tested so it's only making that more explicit.

Definitely not a change to merge at this point,

Yes. Probably worth waiting to see how many people the bug really affects for now

Copy link
Contributor
@scoder scoder left a comment

Choose a reason for hiding this comment

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

I think this is fine, just want to make sure we're not introducing performance regressions.

@@ -1771,7 +1771,7 @@ static PyObject *__Pyx_CoroutineAwait_no_new(PyTypeObject *type, PyObject *args,
// We are applying this to all Python versions (hence the commented out version guard)
// to make the behaviour explicit.
// #if CYTHON_USE_TYPE_SPECS
static PyObject *__Pyx_CoroutineAwait_reduce_ex(__pyx_CoroutineAwaitObject *self, PyObject *arg) {
static PyObject *__Pyx_CoroutineTypes_reduce_ex(PyObject *self, PyObject *arg) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Now that we're renaming this function, I think it's worth stating in the function name that this is not a pickle implementation but something that intentionally prevents pickling.

Suggested change
static PyObject *__Pyx_CoroutineTypes_reduce_ex(PyObject *self, PyObject *arg) {
static PyObject *__Pyx_Coroutine_fail_reduce_ex(PyObject *self, PyObject *arg) {

Comment on lines -1884 to +1910
{Py_tp_getattro, (void *) PyObject_GenericGetAttr},
{Py_tp_getattro, (void *)__pyx_Coroutine_getattro},
{Py_tp_setattro, (void *)__pyx_Coroutine_setattro},
Copy link
Contributor

Choose a reason for hiding this comment

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

Could you run a quick micro-benchmark to see if this has any visible impact on the time it takes to look up and call .send() from Python code (e.g. from within asyncio)? Especially with all the attribute lookup optimisations in Py3.1[34]? Just so that we have a number.

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.

[BUG] Invalid member_descriptor as __module__ on type(cython function) under 3.1.0rc1
2 participants
0