pytest-memray

repository·main·Indexed 19 days ago

https://github.com/bloomberg/pytest-memray

A pytest plugin that integrates the memray memory profiler to track and report memory allocations during test execution. It provides markers to enforce peak memory limits (@pytest.mark.limit_memory), detect memory leaks (@pytest.mark.limit_leaks), and identify unreleased Python objects (@pytest.mark.limit_leaked_objects). The plugin supports configuration via CLI flags and pytest.ini, and offers programmatic access to call stacks through Stack and StackFrame classes.

Tokens
5K
Snippets
20
Records
30
Agent score
64%

What's inside pytest-memray

  1. Quick introduction to using pytest-memray

    main

    To activate memray tracking during a pytest run, add the --memray flag to your command line invocation. After the tests complete, a memory report will be printed to the console, showing total memory allocated, total allocations, a histogram, and the biggest allocating functions.

    pytest --memray tests
  2. Activate memray tracking via CLI or INI

    main

    To enable memory tracking with pytest-memray, you must activate it using either a command-line flag or a configuration setting in your pytest.ini file. If not activated, the plugin will not track memory even if markers are present.

    # Via CLI
    pytest --memray
    
    # Via pytest.ini
    [pytest]
    memray = true
  3. Use pytest-memray markers to enforce memory limits

    main

    You can apply specific markers to your tests to enforce memory constraints. Only one Memray marker can be applied to a single test.

    Available markers:

    • @pytest.mark.limit_memory(max_bytes): Fails the test if peak memory usage exceeds max_bytes.
    • @pytest.mark.limit_leaks(): Fails the test if memory leaks are detected. Note: This automatically enables --native and --trace-python-allocators.
    • @pytest.mark.limit_leaked_objects(max_objects): Fails the test if the number of surviving objects exceeds max_objects. Requires Python 3.13.3 or later.
    import pytest
    
    @pytest.mark.limit_memory(1024 * 1024 * 10)  # Limit to 10MB
    def test_memory_usage():
        ...
    
    @pytest.mark.limit_leaks()
    def test_no_leaks():
        ...
    
    @pytest.mark.limit_leaked_objects(5)
    def test_object_count():
        ...
  4. Install and use pytest-memray

    main

    pytest-memray is a pytest plugin that integrates memray into your test suite, allowing you to monitor memory usage and generate reports during test execution.

    You can run your tests with memory profiling enabled using the --memray flag via the pytest command.

    env COLUMNS=92 pytest --memray demo
  5. Install and enable pytest-memray

    main

    Install the plugin using pip. To enable memory tracking during your pytest execution, pass the --memray flag to the pytest command. By default, the plugin tracks allocations at the high watermark for all tests and reports this information after tests finish.

    pip install pytest-memray
    
    # Run tests with memray enabled
    pytest tests/ --memray
  6. Configure pytest-memray via pytest.ini

    main

    To avoid passing flags every time, you can configure pytest-memray in your pytest.ini file using the memray section. Note that some keys use underscores instead of hyphens compared to the CLI flags.

    [pytest]
    memray = true
    most_allocations = 5
    fail_on_increase = true
    verbosity_memray = 1
  7. Suppress memory leaks using a filter function

    main

    When using the .limit_leaks marker, you can provide a filter_fn to suppress reports for known leaks (e.g., objects cached by the code under test).

    To create a filter, implement a callable that accepts a Stack object and returns a bool.

    • Return True to report the leak.
    • Return False to suppress the leak.

    The Stack object contains a tuple of StackFrame objects, which include function, filename, and lineno.

    from typing import Protocol
    from src.pytest_memray.marks import Stack
    
    class MyLeakFilter:
        def __call__(self, stack: Stack) -> bool:
            # Suppress leaks if they originate from a specific function
            for frame in stack.frames:
                if frame.function == "known_leaky_function":
                    return False
            return True
    
    # Usage in a test (assuming the marker is available via pytest-memray)
    # @pytest.mark.limit_leaks("10MB", filter_fn=MyLeakFilter())
    # def test_something():
    #     ...
  8. Configure memray binary dump storage

    main

    By default, pytest-memray stores binary dumps in a temporary directory. You can control where these are stored using the following options:

    • --memray-bin-path <PATH>: Specify a custom directory for memray binary dumps.
    • --memray-bin-prefix <PREFIX>: Use a custom prefix for the generated .bin files (defaults to a random UUID4 hex).
    pytest --memray --memray-bin-path ./memray_results --memray-bin-prefix my_test_run
  9. Configure advanced memray tracing options

    main

    For deeper memory analysis, use these flags (note that these may slow down execution):

    • --native: Show native frames when showing tracebacks of memory allocations.
    • --trace-python-allocators: Record allocations made by the Pymalloc allocator.

    These can also be set in pytest.ini using native = true and trace_python_allocators = true.

    pytest --memray --native --trace-python-allocators
  10. Configure memray reporting and verbosity

    main

    Control how memory information is displayed in the terminal summary:

    • --hide-memray-summary: Hides the memray summary at the end of the execution.
    • --most-allocations <N>: Shows the N tests that allocate the most memory (set to 0 to show all). Defaults to 5.
    • --stacks <N>: Shows the N stack entries when showing tracebacks of memory allocations. Defaults to 1.
    • --fail-on-increase: When used with limit_memory, fails a test if it uses more memory than its last successful run.
    • verbosity_memray (INI): Controls the detail of limit_memory failure reports.
      • Negative levels: Summary only.
      • Level 0 or 1: Top 10 allocations.
      • Level 2+: All allocations.
      • Default: Follows pytest's -v / -q flags (0 if neither provided).
    pytest --memray --most-allocations 10 --stacks 3
  11. Suppress leaked objects using a filter function

    main

    When using the .limit_leaked_objects marker, you can provide a filter_fn to ignore specific objects that are known to leak (e.g., certain types that are intentionally cached).

    To create a filter, implement a callable that accepts an object and returns a bool.

    • Return True to report the leak (causing the test to fail).
    • Return False to suppress the leak.
    from typing import Protocol
    
    class MyObjectFilter:
        def __call__(self, obj: object) -> bool:
            # Suppress leaks for all instances of a specific class
            if isinstance(obj, MyKnownCacheClass):
                return False
            return True
    
    # Usage in a test
    # @pytest.mark.limit_leaked_objects(filter_fn=MyObjectFilter())
    # def test_something():
    #     ...