Skip to content

Conversation

@pablogsal
Copy link
Member

This commit adds support for tracking Python objects that survive test execution
using the new reference tracking API introduced in memray (commit e567c3cf).

Issue number of the reported bug or feature request: #

Describe your changes
A clear and concise description of the changes you have made.

Testing performed
Describe the testing you have performed to ensure that the bug has been addressed, or that the new feature works as planned.

Additional context
Add any other context about your contribution here.

This commit adds support for tracking Python objects that survive test execution
using the new reference tracking API introduced in memray (commit e567c3cf).
f"{padding}Object types that leaked (total: {len(self.surviving_objects)} objects):"
)
for obj_type, count in type_counts.most_common(10):
text_lines.append(f"{padding*2}- {obj_type}: {count} instance(s)")
Copy link
Contributor

@godlygeek godlygeek Nov 12, 2025

Choose a reason for hiding this comment

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

It might be better to do:

Suggested change
text_lines.append(f"{padding*2}- {obj_type}: {count} instance(s)")
text_lines.append(f"{padding*2}- {count} {obj_type} instance(s)")

That will make it easier to compare the quantities across different lines. Compare:

- int: 98175 instance(s)
- tuple: 12101 instance(s)
- dict: 8078 instance(s)

versus:

- 98175 int instance(s)
- 12101 tuple instance(s)
- 8078 dict instance(s)

It's a lot easier to compare the numbers when they're aligned. Maybe the counts should even be right aligned?

- 98175 int instance(s)
- 12101 tuple instance(s)
-  8078 dict instance(s)

Comment on lines +309 to +310
# Show a sample of the actual objects (up to 10)
text_lines.append(f"\n{padding}Sample of leaked objects:")
Copy link
Contributor

Choose a reason for hiding this comment

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

Hm. I wonder if it'd be nice to show one of each type, or something... if you imagine someone has leaked ten thousand ints and one dict, it's way more valuable to see the dict than the ints... Eh, or we can leave that as a potential future improvement.

Comment on lines +336 to +338
if _surviving_objects is None:
return None

Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
if _surviving_objects is None:
return None

the below if already handles everything falsey, so no need to have this check separately.

if not _surviving_objects:
return None

# Filter out frame objects and other internal Python objects
Copy link
Contributor

Choose a reason for hiding this comment

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

Hm. What's the rationale for filtering all of these? I think one of the most frequent types of leaks I see is when someone saves an exception object somewhere, which references a traceback, and frames, and all their locals, etc. Excluding the tracebacks and frames might make it harder to understand the cause of a leak.

Comment on lines +146 to +147
if sys.version_info >= (3, 13, 3):
pytest.skip("Test for old Python versions", allow_module_level=True)
Copy link
Contributor

Choose a reason for hiding this comment

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

This won't run at all for an old Python version, because of the file level

pytestmark = pytest.mark.skipif(
    sys.version_info < (3, 13, 3),

Comment on lines +180 to +181
# Note: The actual implementation would need to inject
# a function to retrieve the objects
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't understand how this is meant to work at all 😓

Inject how?


[tool.mypy]
python_version = "3.8"
python_version = "3.14"
Copy link
Contributor

Choose a reason for hiding this comment

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

Hm, shouldn't this stay the minimum version we support? Otherwise mypy will fail to tell us if we break 3.8 compatibility. Though I guess you could say that that's fine, we rely on the tests for checking that... Hm, OK, nevermind.

"""Type that holds information about objects that survived tracking."""

surviving_objects: list
surviving_objects: list[Any]
Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't it be list[object], not list[Any]? Likewise for every occurrence of Any that you've added.

tracker_kwargs["track_object_lifetimes"] = True

tracker = Tracker(result_file, **tracker_kwargs)
tracker = Tracker(result_file, **tracker_kwargs) # type: ignore[call-overload]
Copy link
Contributor

Choose a reason for hiding this comment

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

Huh, what error is it complaining about here?

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.

2 participants