pytest-memray
Advanced tools
+15
-2
@@ -8,2 +8,17 @@ Release History | ||
| v1.3.2 (2022-11-30) | ||
| ------------------- | ||
| Bug Fixes - 1.3.2 | ||
| ~~~~~~~~~~~~~~~~~ | ||
| - Make the plugin compatible with ``pytest-xdist`` | ||
| v1.3.1 (2022-11-14) | ||
| ------------------- | ||
| Bug Fixes - 1.3.1 | ||
| ~~~~~~~~~~~~~~~~~ | ||
| - Declare 3.11 support | ||
| - Fix incompatibility with the ``flaky`` plugin | ||
| v1.3.0 (2022-08-21) | ||
@@ -16,3 +31,2 @@ ------------------- | ||
| v1.2.0 (2022-05-26) | ||
@@ -33,3 +47,2 @@ ------------------- | ||
| v1.1.0 (2022-05-17) | ||
@@ -36,0 +49,0 @@ ------------------- |
+1
-1
@@ -63,2 +63,2 @@ Usage | ||
| def test_foobar(): | ||
| pass # do some stuff that allocates memory | ||
| pass # do some stuff that allocates memory |
+14
-13
| Metadata-Version: 2.1 | ||
| Name: pytest-memray | ||
| Version: 1.3.1 | ||
| Version: 1.3.2 | ||
| Summary: A simple plugin to use with pytest | ||
@@ -21,21 +21,22 @@ Project-URL: Bug Tracker, https://github.com/bloomberg/pytest-memray/issues | ||
| Requires-Python: >=3.8 | ||
| Requires-Dist: memray>=1.3 | ||
| Requires-Dist: pytest>=7.1.2 | ||
| Requires-Dist: memray>=1.4.1 | ||
| Requires-Dist: pytest>=7.2 | ||
| Provides-Extra: docs | ||
| Requires-Dist: furo>=2022.6.21; extra == 'docs' | ||
| Requires-Dist: sphinx-argparse>=0.3.1; extra == 'docs' | ||
| Requires-Dist: furo>=2022.9.29; extra == 'docs' | ||
| Requires-Dist: sphinx-argparse>=0.4; extra == 'docs' | ||
| Requires-Dist: sphinx-inline-tabs>=2022.1.2b11; extra == 'docs' | ||
| Requires-Dist: sphinx>=5.1.1; extra == 'docs' | ||
| Requires-Dist: sphinx>=5.3; extra == 'docs' | ||
| Requires-Dist: sphinxcontrib-programoutput>=0.17; extra == 'docs' | ||
| Requires-Dist: towncrier>=21.9; extra == 'docs' | ||
| Requires-Dist: towncrier>=22.8; extra == 'docs' | ||
| Provides-Extra: lint | ||
| Requires-Dist: black==22.6; extra == 'lint' | ||
| Requires-Dist: flake8==5.0.4; extra == 'lint' | ||
| Requires-Dist: black==22.10; extra == 'lint' | ||
| Requires-Dist: flake8==6; extra == 'lint' | ||
| Requires-Dist: isort==5.10.1; extra == 'lint' | ||
| Requires-Dist: mypy==0.971; extra == 'lint' | ||
| Requires-Dist: mypy==0.991; extra == 'lint' | ||
| Provides-Extra: test | ||
| Requires-Dist: covdefaults>=2.2; extra == 'test' | ||
| Requires-Dist: coverage>=6.4.4; extra == 'test' | ||
| Requires-Dist: flaky>=3.7.0; extra == 'test' | ||
| Requires-Dist: pytest>=7.1.2; extra == 'test' | ||
| Requires-Dist: coverage>=6.5; extra == 'test' | ||
| Requires-Dist: flaky>=3.7; extra == 'test' | ||
| Requires-Dist: pytest-xdist>=3.0.2; extra == 'test' | ||
| Requires-Dist: pytest>=7.2; extra == 'test' | ||
| Description-Content-Type: text/markdown | ||
@@ -42,0 +43,0 @@ |
+14
-13
| [build-system] | ||
| build-backend = "hatchling.build" | ||
| requires = ["hatchling>=1.8", "hatch-vcs>=0.2"] | ||
| requires = ["hatchling>=1.11.1", "hatch-vcs>=0.2"] | ||
@@ -22,24 +22,25 @@ [project] | ||
| dependencies = [ | ||
| "pytest>=7.1.2", | ||
| "memray>=1.3", | ||
| "pytest>=7.2", | ||
| "memray>=1.4.1", | ||
| ] | ||
| optional-dependencies.docs = [ | ||
| "furo>=2022.6.21", | ||
| "sphinx>=5.1.1", | ||
| "sphinx-argparse>=0.3.1", | ||
| "furo>=2022.9.29", | ||
| "sphinx>=5.3", | ||
| "sphinx-argparse>=0.4", | ||
| "sphinx-inline-tabs>=2022.1.2b11", | ||
| "sphinxcontrib-programoutput>=0.17", | ||
| "towncrier>=21.9", | ||
| "towncrier>=22.8", | ||
| ] | ||
| optional-dependencies.lint = [ | ||
| "black==22.6", | ||
| "flake8==5.0.4", | ||
| "black==22.10", | ||
| "flake8==6", | ||
| "isort==5.10.1", | ||
| "mypy==0.971", | ||
| "mypy==0.991", | ||
| ] | ||
| optional-dependencies.test = [ | ||
| "covdefaults>=2.2", | ||
| "pytest>=7.1.2", | ||
| "coverage>=6.4.4", | ||
| "flaky>=3.7.0", | ||
| "pytest>=7.2", | ||
| "coverage>=6.5", | ||
| "flaky>=3.7", | ||
| "pytest-xdist>=3.0.2", | ||
| ] | ||
@@ -46,0 +47,0 @@ dynamic = ["version"] |
@@ -1,1 +0,1 @@ | ||
| __version__ = "1.3.1" | ||
| __version__ = "1.3.2" |
| from __future__ import annotations | ||
| from dataclasses import dataclass | ||
| from typing import Optional | ||
| from typing import Tuple | ||
@@ -51,3 +50,3 @@ | ||
| limit: str, *, _allocations: list[AllocationRecord] | ||
| ) -> Optional[_MemoryInfo]: | ||
| ) -> _MemoryInfo | None: | ||
| """Limit memory used by the test.""" | ||
@@ -54,0 +53,0 @@ max_memory = parse_memory_string(limit) |
@@ -8,2 +8,3 @@ from __future__ import annotations | ||
| import os | ||
| import pickle | ||
| import uuid | ||
@@ -80,2 +81,3 @@ from dataclasses import dataclass | ||
| class Result: | ||
| test_id: str | ||
| metadata: Metadata | ||
@@ -90,5 +92,18 @@ result_file: Path | ||
| path: Path | None = config.getvalue("memray_bin_path") | ||
| self._tmp_dir: None | TemporaryDirectory[str] = None | ||
| if path is None: | ||
| self._tmp_dir: None | TemporaryDirectory[str] = TemporaryDirectory() | ||
| self.result_path: Path = Path(self._tmp_dir.name) | ||
| # Check the MEMRAY_RESULT_PAtH environment variable. If this | ||
| # is set, it means that we are running in a worker and the main | ||
| # process has set it so we'll use it as the directory to store | ||
| # the results. | ||
| result_path = os.getenv("MEMRAY_RESULT_PATH") | ||
| if not result_path: | ||
| # We are not running in a worker, so we'll create a temporary | ||
| # directory to store the results. Other possible workers will | ||
| # use this directory by reading the MEMRAY_RESULT_PATH environment | ||
| # variable. | ||
| self._tmp_dir = TemporaryDirectory() | ||
| os.environ["MEMRAY_RESULT_PATH"] = self._tmp_dir.name | ||
| result_path = self._tmp_dir.name | ||
| self.result_path: Path = Path(result_path) | ||
| else: | ||
@@ -98,2 +113,4 @@ self._tmp_dir = None | ||
| self._bin_prefix = config.getvalue("memray_bin_prefix") or uuid.uuid4().hex | ||
| self.result_metadata_path = self.result_path / "metadata" | ||
| self.result_metadata_path.mkdir(exist_ok=True, parents=True) | ||
@@ -105,2 +122,4 @@ @hookimpl(hookwrapper=True) # type: ignore[misc] # Untyped decorator | ||
| self._tmp_dir.cleanup() | ||
| if os.environ.get("MEMRAY_RESULT_PATH"): | ||
| del os.environ["MEMRAY_RESULT_PATH"] | ||
@@ -133,3 +152,10 @@ @hookimpl(hookwrapper=True) # type: ignore[misc] # Untyped decorator | ||
| return None | ||
| self.results[pyfuncitem.nodeid] = Result(metadata, result_file) | ||
| result = Result(pyfuncitem.nodeid, metadata, result_file) | ||
| metadata_path = ( | ||
| self.result_metadata_path | ||
| / result_file.with_suffix(".metadata").name | ||
| ) | ||
| with open(metadata_path, "wb") as file_handler: | ||
| pickle.dump(result, file_handler) | ||
| self.results[pyfuncitem.nodeid] = result | ||
| finally: | ||
@@ -167,3 +193,3 @@ # Restore the original function. This is needed because some | ||
| func = reader.get_high_watermark_allocation_records | ||
| allocations = list((func(merge_threads=True))) | ||
| allocations = list(func(merge_threads=True)) | ||
| res = marker_fn(*marker.args, **marker.kwargs, _allocations=allocations) | ||
@@ -199,2 +225,11 @@ if res: | ||
| if not self.results: | ||
| # If there are not results is because we are likely running under | ||
| # pytest-xdist, and the master process is not running the tests. In | ||
| # this case, we can retrieve the results from the metadata directory | ||
| # instead, that is common for all workers. | ||
| for result_file in self.result_metadata_path.glob("*.metadata"): | ||
| result = pickle.loads(result_file.read_bytes()) | ||
| self.results[result.test_id] = result | ||
| total_sizes = collections.Counter( | ||
@@ -201,0 +236,0 @@ { |
@@ -343,2 +343,3 @@ from __future__ import annotations | ||
| "H-magic-test_a.py-test_b[1].bin", | ||
| "metadata", | ||
| } | ||
@@ -393,1 +394,80 @@ | ||
| assert result.ret == ExitCode.TESTS_FAILED | ||
| def test_memray_report_with_pytest_xdist(pytester: Pytester) -> None: | ||
| pytester.makepyfile( | ||
| """ | ||
| import pytest | ||
| from memray._test import MemoryAllocator | ||
| allocator = MemoryAllocator() | ||
| def allocating_func1(): | ||
| allocator.valloc(1024) | ||
| allocator.free() | ||
| def allocating_func2(): | ||
| allocator.valloc(1024*2) | ||
| allocator.free() | ||
| def test_foo(): | ||
| allocating_func1() | ||
| def test_bar(): | ||
| allocating_func2() | ||
| """ | ||
| ) | ||
| result = pytester.runpytest("--memray", "-n", "2") | ||
| assert result.ret == ExitCode.OK | ||
| output = result.stdout.str() | ||
| assert "MEMRAY REPORT" in output | ||
| # We don't check the exact number of memory allocated because pytest-xdist | ||
| # can spawn some threads using the `execnet` library which can allocate extra | ||
| # memory. | ||
| assert "Total memory allocated:" in output | ||
| assert "results for test_memray_report_with_pytest_xdist.py::test_foo" in output | ||
| assert "valloc:" in output | ||
| assert "-> 2.0KiB" in output | ||
| assert "results for test_memray_report_with_pytest_xdist.py::test_bar" in output | ||
| assert "valloc:" in output | ||
| assert "-> 1.0KiB" in output | ||
| @pytest.mark.parametrize( | ||
| "size, outcome", | ||
| [ | ||
| (1024 * 5, ExitCode.TESTS_FAILED), | ||
| (1024 * 2, ExitCode.TESTS_FAILED), | ||
| (1024 * 2 - 1, ExitCode.OK), | ||
| (1024 * 1, ExitCode.OK), | ||
| ], | ||
| ) | ||
| def test_limit_memory_marker_with_pytest_xdist( | ||
| pytester: Pytester, size: int, outcome: ExitCode | ||
| ) -> None: | ||
| pytester.makepyfile( | ||
| f""" | ||
| import pytest | ||
| from memray._test import MemoryAllocator | ||
| allocator = MemoryAllocator() | ||
| @pytest.mark.limit_memory("2KB") | ||
| def test_memory_alloc_fails(): | ||
| allocator.valloc({size}) | ||
| allocator.free() | ||
| @pytest.mark.limit_memory("2KB") | ||
| def test_memory_alloc_fails_2(): | ||
| allocator.valloc({size}) | ||
| allocator.free() | ||
| """ | ||
| ) | ||
| result = pytester.runpytest("--memray", "-n", "2") | ||
| assert result.ret == outcome |
+1
-1
@@ -63,3 +63,3 @@ [tox] | ||
| deps = | ||
| towncrier>=21.9 | ||
| towncrier>=22.8 | ||
| commands = | ||
@@ -66,0 +66,0 @@ make gen_news VERSION={posargs} |
Alert delta unavailable
Currently unable to show alert delta for PyPI packages.
331894
1.31%932
11.35%