Visitar URL original
Fix method memleak test by filmor · Pull Request #2708 · pythonnet/pythonnet · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
Rework memory usage tracking to use .NET and Python memory tracing
  • Loading branch information
filmor committed Apr 19, 2026
commit 5fd51c9478881a35a7de55859483f5c0553efb8a
1 change: 0 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ dev = [
"find_libpython >= 0.3",
"numpy >=2 ; python_version >= '3.10'",
"numpy <2 ; python_version < '3.10'",
"psutil"
]

[[project.authors]]
Expand Down
47 changes: 26 additions & 21 deletions tests/test_method.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,27 @@

import System
import pytest
import psutil
import sys
import gc
import tracemalloc
from Python.Test import MethodTest

@pytest.fixture(scope="function")
def memory_usage_tracking():
was_tracing = tracemalloc.is_tracing()
if not was_tracing:
tracemalloc.start()
yield
if not was_tracing:
tracemalloc.stop()

@pytest.fixture
def process() -> psutil.Process:
"""Fixture for getting the current process."""
return psutil.Process()


def _get_precise_process_memory_bytes(process: psutil.Process) -> int:
return process.memory_full_info().uss
def _get_total_memory_bytes() -> int:
"""Get total memory consumption combining .NET GC memory and Python tracemalloc."""
dotnet_memory = System.GC.GetTotalMemory(forceFullCollection=False)
# Get Python-side memory
current, _peak = tracemalloc.get_traced_memory()
# Return combined measurement
return dotnet_memory + current


def test_instance_method_overwritable():
Expand Down Expand Up @@ -949,12 +956,13 @@ def test_getting_generic_method_binding_does_not_leak_ref_count():
assert refCount == 1


def test_getting_generic_method_binding_does_not_leak_memory(process: psutil.Process):
def test_getting_generic_method_binding_does_not_leak_memory(memory_usage_tracking):
"""Test that managed object is freed after calling generic method. Issue #691"""

from PlainOldNamespace import PlainOldClass

processBytesBeforeCall = _get_precise_process_memory_bytes(process)
tracemalloc.start()
processBytesBeforeCall = _get_total_memory_bytes()
print(
"\n\nMemory consumption (bytes) at start of test: "
+ str(processBytesBeforeCall)
Expand All @@ -967,7 +975,7 @@ def test_getting_generic_method_binding_does_not_leak_memory(process: psutil.Pro
gc.collect()
System.GC.Collect()

processBytesAfterCall = _get_precise_process_memory_bytes(process)
processBytesAfterCall = _get_total_memory_bytes()
print("Memory consumption (bytes) at end of test: " + str(processBytesAfterCall))
processBytesDelta = processBytesAfterCall - processBytesBeforeCall
print("Memory delta: " + str(processBytesDelta))
Expand All @@ -991,14 +999,12 @@ def test_getting_overloaded_method_binding_does_not_leak_ref_count():
assert refCount == 1


def test_getting_overloaded_method_binding_does_not_leak_memory(
process: psutil.Process,
):
def test_getting_overloaded_method_binding_does_not_leak_memory(memory_usage_tracking):
"""Test that managed object is freed after calling overloaded method. Issue #691"""

from PlainOldNamespace import PlainOldClass

processBytesBeforeCall = _get_precise_process_memory_bytes(process)
processBytesBeforeCall = _get_total_memory_bytes()
print(
"\n\nMemory consumption (bytes) at start of test: "
+ str(processBytesBeforeCall)
Expand All @@ -1011,7 +1017,7 @@ def test_getting_overloaded_method_binding_does_not_leak_memory(
gc.collect()
System.GC.Collect()

processBytesAfterCall = _get_precise_process_memory_bytes(process)
processBytesAfterCall = _get_total_memory_bytes()
print("Memory consumption (bytes) at end of test: " + str(processBytesAfterCall))
processBytesDelta = processBytesAfterCall - processBytesBeforeCall
print("Memory delta: " + str(processBytesDelta))
Expand All @@ -1036,13 +1042,12 @@ def test_getting_method_overloads_binding_does_not_leak_ref_count():
assert refCount == 1


@pytest.mark.xfail(reason="Fails locally, need to investigate later", strict=False)
def test_getting_method_overloads_binding_does_not_leak_memory(process: psutil.Process):
def test_getting_method_overloads_binding_does_not_leak_memory(memory_usage_tracking):
"""Test that managed object is freed after calling overloaded method. Issue #691"""

from PlainOldNamespace import PlainOldClass

processBytesBeforeCall = _get_precise_process_memory_bytes(process)
processBytesBeforeCall = _get_total_memory_bytes()
print(
"\n\nMemory consumption (bytes) at start of test: "
+ str(processBytesBeforeCall)
Expand All @@ -1055,7 +1060,7 @@ def test_getting_method_overloads_binding_does_not_leak_memory(process: psutil.P
gc.collect()
System.GC.Collect()

processBytesAfterCall = _get_precise_process_memory_bytes(process)
processBytesAfterCall = _get_total_memory_bytes()
print("Memory consumption (bytes) at end of test: " + str(processBytesAfterCall))
processBytesDelta = processBytesAfterCall - processBytesBeforeCall
print("Memory delta: " + str(processBytesDelta))
Expand Down
30 changes: 0 additions & 30 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading