Skip to content
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

[mypyc] Fix __dict__ in CPython 3.11 #13206

Closed
wants to merge 20 commits into from

Conversation

Fidget-Spinner
Copy link
Member

Fixes mypyc/mypyc#943.

Many thanks to @kumaraditya303 for pointing me in the right direction.

@Fidget-Spinner Fidget-Spinner changed the title Fix __dict__ in CPython 3.11 [mypyc] Fix __dict__ in CPython 3.11 Jul 20, 2022
@@ -18,6 +19,7 @@
from mypyc.sametype import is_same_type
from mypyc.namegen import NameGenerator

PY_3_11 = sys.version_info >= (3, 11)
Copy link
Member

Choose a reason for hiding this comment

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

I think this isn't right: the running version of Python doesn't necessarily equal the version used at runtime. Maybe we need to emit #ifdef'ed C code instead?

Copy link
Member Author

Choose a reason for hiding this comment

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

Oh right. Yeah my bad.

Copy link
Collaborator

@ichard26 ichard26 Jul 20, 2022

Choose a reason for hiding this comment

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

Currently mypyc does not support cross Python version compilation, but yes conditional C code is probably better since cross version compilation is indeed a future goal. The Emitter class does have the capi_version attribute for future reference (it's currently always set to the running version of Python, could be changed down the road).

Copy link
Member Author

Choose a reason for hiding this comment

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

For some reason the PY_VERSION_HEX checks are not working (I suspect the appropriate headers aren't being included properly). So for now I'm using capi_version and leaving TODOs to use PY_VERSION_HEX in the future.

@Fidget-Spinner
Copy link
Member Author

I have no clue how/why everything is now segfaulting.

@Fidget-Spinner
Copy link
Member Author

A second pair of eyes on the C code would be much appreciated :).

@Fidget-Spinner
Copy link
Member Author

🎉 All the tests are green. testProtocol is fixed in 3.11. Leaving only testProperty broken in 3.11.

@Fidget-Spinner Fidget-Spinner marked this pull request as ready for review July 26, 2022 13:42
@Fidget-Spinner
Copy link
Member Author

This fix will only work in 3.11b5 and above. It does not work for 3.11b4. So I'm waiting for GH actions/setup-python to update to 3.11b5.

@Fidget-Spinner
Copy link
Member Author

How do I make the CI use 3.11b5? @JelleZijlstra or @ichard26 do you have any idea?

@hauntsaninja
Copy link
Collaborator

hauntsaninja commented Jul 27, 2022

I think it requires actions/python-versions#180 to be merged

@Fidget-Spinner
Copy link
Member Author

This should now be ready. Thanks to everyone involved!

Copy link

@kumaraditya303 kumaraditya303 left a comment

Choose a reason for hiding this comment

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

Not a mypyc expert but C code LGTM. I left one nit for you to consider.

mypyc/codegen/emitclass.py Outdated Show resolved Hide resolved
Copy link
Collaborator

@hauntsaninja hauntsaninja left a comment

Choose a reason for hiding this comment

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

Thank you! Looks like the run against Python 3.11b5 happened and this has fixed testProtocol and testPickling

@hauntsaninja hauntsaninja mentioned this pull request Jul 27, 2022
17 tasks
Copy link
Collaborator

@hauntsaninja hauntsaninja left a comment

Choose a reason for hiding this comment

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

Thanks, after reading python/cpython#92678 this makes sense to me. I'll leave open for a couple days in case anyone else more familiar with mypyc or CPython wants to chime in :-)

@Fidget-Spinner Fidget-Spinner deleted the 3_11_dict_compat branch August 5, 2022 08:19
@Fidget-Spinner
Copy link
Member Author

We don't need this anymore. It was fixed upstream in CPython 3.11.

@ilevkivskyi
Copy link
Member

Hm, @Fidget-Spinner I think we may still need this PR to fix testPickling (the last failing test after #13787), see https://github.com/python/mypy/actions/runs/3165194624/jobs/5154028986

The test fails because of __dict__ handling (looks like wrong offset on Python 3.11). I started working on my own PR, but then remembered that we had this (more thorough) one. Would you like to adapt this PR to fix that last test case?

@Fidget-Spinner
Copy link
Member Author

Sorry but I'll be busy the next week, so I don't think I can work on this anytime soon.

I give full consent to copy my code in this PR for use in your PR. Just attribute it with Co-Authored-By if you do use it please.

@ilevkivskyi
Copy link
Member

@Fidget-Spinner I tried and it looks like this approach doesn't work. The problem is that _PyObject_VisitManagedDict and _PyObject_ClearManagedDict are not public in 3.11 (I am not sure what was the reason to revert the backport, is it possible to get the "better" backport from 3.12? cc @markshannon). And even if try to vendor them and include pycore_object.h (which is already sketchy), I still get errors (e.g. conflicting types for _PyObject_LookupSpecial).

Since this used to work until 3.10, it would be great if there was a reasonable way to support this in 3.11, does anyone have any ideas?

@ilevkivskyi
Copy link
Member

ilevkivskyi commented Oct 6, 2022

Actually after looking a bit more at this, I don't think I understand what is going on here. A simple repro mypyc test case is

[case test311Repro]
from typing import Any

def dec(x: Any) -> Any:
    return x

@dec
class D: pass

class E(D):
    y: int

[file driver.py]
from native import E

e = E()
e.x = 1
e.__dict__

This raises an AttributeError on Python 3.11, while it works on Python 3.10. What we do here is "compiled class that inherits from interpreted one". D is created by simply calling type("D", ...) while E is created using a static type that has a member descriptor __dict__ pointing right after E objects struct end.

Note that assigning attributes on e still works, so it does have a dict somewhere. Maybe D gets this managed dict flag, and therefore the negative dict offset is somehow used when storing attributes on e (instead of the one right after the object)? Also it may be a bug in CPyType_FromTemplate().

@msullivan Maybe you will have time to look at this to figure out what is going on here?

@ilevkivskyi
Copy link
Member

ilevkivskyi commented Oct 6, 2022

OK, so my guess was right, since E subclasses an interpreted class, it gets the managed dict. So the test is fixed by simply setting t->ht_type.tp_flags &= ~Py_TPFLAGS_MANAGED_DICT after E is created. I am not sure how safe it is, it seems like at least some code assumes that heap type implies managed dict.

If this hack gets us through 3.11 this is fine probably, since 3.12 already includes good public APIs to work with managed dicts.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

testProtocol failures on 3.11 build
6 participants