-
-
Notifications
You must be signed in to change notification settings - Fork 3k
[mypyc] Generate __getattr__ wrapper #19909
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
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -401,3 +401,12 @@ | |
c_function_name="CPy_GetName", | ||
error_kind=ERR_MAGIC, | ||
) | ||
|
||
# look-up name in tp_dict but don't raise AttributeError on failure | ||
generic_getattr = custom_op( | ||
arg_types=[object_rprimitive, object_rprimitive], | ||
return_type=object_rprimitive, | ||
c_function_name="CPyObject_GenericGetAttr", | ||
error_kind=ERR_NEVER, | ||
is_borrowed=True, | ||
|
||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2088,3 +2088,102 @@ class NonNative: | |
@mypyc_attr(free_list_len=1, allow_interpreted_subclasses=True) # E: "free_list_len" can't be used in a class that allows interpreted subclasses | ||
class InterpSub: | ||
pass | ||
|
||
[case testUnsupportedGetAttr] | ||
from mypy_extensions import mypyc_attr | ||
from typing import Optional | ||
|
||
@mypyc_attr(allow_interpreted_subclasses=True) | ||
class AllowsInterpreted: | ||
def __getattr__(self, attr: str) -> Optional[object]: # E: "__getattr__" not supported in class "AllowsInterpreted" because it allows interpreted subclasses | ||
return 0 | ||
|
||
class InheritsInterpreted(dict): | ||
def __getattr__(self, attr: str) -> Optional[object]: # E: "__getattr__" not supported in class "InheritsInterpreted" because it inherits from an interpreted class | ||
p-sawicki marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
return 0 | ||
|
||
[case testGetAttr] | ||
from typing import Optional, Tuple | ||
|
||
class GetAttr: | ||
class_var = "x" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Test also a "real" class variable that is annotated using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. obtaining the value through the wrapper works (since i've also added this to the run tests) but when the variable is annotated like this, mypyc seems to always call |
||
|
||
def __init__(self, regular_attr: int): | ||
self.regular_attr = regular_attr | ||
|
||
def __getattr__(self, attr: str) -> Optional[object]: | ||
return attr | ||
|
||
def test_getattr() -> Tuple[object, object, object]: | ||
i = GetAttr(42) | ||
one = i.one | ||
two = i.regular_attr | ||
three = i.class_var | ||
return (one, two, three) | ||
|
||
[typing fixtures/typing-full.pyi] | ||
[out] | ||
def GetAttr.__init__(self, regular_attr): | ||
self :: __main__.GetAttr | ||
regular_attr :: int | ||
L0: | ||
self.regular_attr = regular_attr | ||
return 1 | ||
def GetAttr.__getattr__(self, attr): | ||
self :: __main__.GetAttr | ||
attr :: str | ||
L0: | ||
return attr | ||
def GetAttr.__getattr____wrapper(__mypyc_self__, attr): | ||
__mypyc_self__ :: __main__.GetAttr | ||
attr, r0 :: object | ||
r1 :: bit | ||
r2 :: str | ||
r3 :: union[object, None] | ||
L0: | ||
r0 = CPyObject_GenericGetAttr(__mypyc_self__, attr) | ||
r1 = r0 != 0 | ||
if r1 goto L1 else goto L2 :: bool | ||
L1: | ||
return r0 | ||
L2: | ||
r2 = cast(str, attr) | ||
r3 = __mypyc_self__.__getattr__(r2) | ||
return r3 | ||
def GetAttr.__mypyc_defaults_setup(__mypyc_self__): | ||
__mypyc_self__ :: __main__.GetAttr | ||
r0 :: str | ||
L0: | ||
r0 = 'x' | ||
__mypyc_self__.class_var = r0 | ||
return 1 | ||
def test_getattr(): | ||
r0, i :: __main__.GetAttr | ||
r1 :: str | ||
r2 :: object | ||
one :: union[object, None] | ||
r3, two :: int | ||
r4, three :: str | ||
r5 :: tuple[union[object, None], int, str] | ||
r6 :: union[object, None] | ||
r7 :: int | ||
r8 :: str | ||
r9 :: object | ||
r10 :: tuple[union[object, None], object, str] | ||
L0: | ||
r0 = GetAttr(84) | ||
i = r0 | ||
r1 = 'one' | ||
r2 = CPyObject_GetAttr(i, r1) | ||
one = r2 | ||
r3 = i.regular_attr | ||
two = r3 | ||
r4 = i.class_var | ||
three = r4 | ||
r5 = (one, two, three) | ||
r6 = r5[0] | ||
r7 = r5[1] | ||
r8 = r5[2] | ||
r9 = box(int, r7) | ||
r10 = (r6, r9, r8) | ||
return r10 |
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.
Can the wrapper check the type of self, and only use the slow path if the runtime type is unexpected?
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.
yes, that was my thinking as well. updated the comment to mention this.