Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions mypyc/irbuild/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -1553,8 +1553,7 @@ def is_native_attr_ref(self, expr: MemberExpr) -> bool:
return (
isinstance(obj_rtype, RInstance)
and obj_rtype.class_ir.is_ext_class
and obj_rtype.class_ir.has_attr(expr.name)
and not obj_rtype.class_ir.get_method(expr.name)
and any(expr.name in ir.attributes for ir in obj_rtype.class_ir.mro)
)

def mark_block_unreachable(self) -> None:
Expand Down
44 changes: 44 additions & 0 deletions mypyc/test-data/run-classes.test
Original file line number Diff line number Diff line change
Expand Up @@ -5925,3 +5925,47 @@ assert NonExtDict.BASE == {"x": 1}
assert NonExtDict.EXTENDED == {"x": 1, "y": 2}

assert NonExtChained.Z == {10, 20, 30}

[case testPropertyGetterLeak]
class Bar:
pass

class Foo:
def __init__(self) -> None:
self.obj: object = Bar()

@property
def val(self) -> object:
return self.obj

[file other.py]
import gc
from native import Foo, Bar

def check(foo: Foo) -> bool:
return isinstance(foo.val, Bar)

def test_property_getter_no_leak() -> None:
gc.collect()
before = gc.get_objects()
for _ in range(100):
foo = Foo()
check(foo)
gc.collect()
after = gc.get_objects()
diff = len(after) - len(before)
assert diff <= 2, diff
Comment on lines +5948 to +5957
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

does this test fail for you without your changes? for me it doesn't and i think it's because there's only one Foo and Bar created so even though it's leaking references it's not leaking any more new objects.

to test if we're no longer leaking new objects i think you could move the creation of Foo into the loop.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yeah you're right, moved creation inside the loop.

Do note that the test is non-deterministic, I think it triggers around 40% of the time without fixed PYTHONHASHSEED e.g PYTHONHASHSEED=0 repro.py does trigger.


test_property_getter_no_leak()

[file driver.py]
import sys
from other import check
from native import Foo

foo = Foo()
init = sys.getrefcount(foo.obj)
for _ in range(100):
check(foo)
after = sys.getrefcount(foo.obj)
assert after - init == 0, f"Leaked {after - init} refs"
Loading