diff --git a/.gitignore b/.gitignore index acb3116f06..faf2763255 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,8 @@ coverage.json unit_results.json unit_results/ test_assets/ +.nrl_remote_map.json +.nrl_remote_state.json # Cache uv_cache/ diff --git a/docs/testing.md b/docs/testing.md index 5a24452813..c1d1bc570a 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -30,6 +30,42 @@ uv run --extra mcore --group test bash tests/run_unit.sh --mcore-only uv run --extra mcore --group test bash tests/run_unit.sh --mcore-only --hf-gated ``` +### Experimental: Faster local test iteration with pytest-testmon + +We support `pytest-testmon` to speed up local unit test runs by re-running only impacted tests. This works for both regular in-process code and out-of-process `@ray.remote` workers via a lightweight, test-only selection helper. + +Usage: +```sh +# Re-run only impacted unit tests +uv run --group test pytest --testmon tests/unit + +# You can also combine with markers/paths +uv run --group test pytest --hf-gated --testmon tests/unit/models/policy/test_dtensor_worker.py +``` + +What to expect: +- On the first run in a fresh workspace, testmon may run a broader set (or deselect everything if nothing was executed yet) to build its dependency cache. +- On subsequent runs, editing non-remote code narrows selection to only the tests that import/use those modules. +- Editing code inside `@ray.remote` actors also retriggers impacted tests. We maintain a static mapping from test modules to transitive `nemo_rl` modules they import and intersect that with changed files when `--testmon` is present. +- After a successful impacted run, a second `--testmon` invocation (with no further edits) will deselect all tests. +- Running `pytest` with `-k some_substring_in_test_name` will always run tests that match even if `--testmon` is passed. + +Limitations and tips: +- Selection is based on Python imports and file mtimes; non-Python assets (YAML/JSON/shell) are not tracked. When editing those, re-run target tests explicitly. +- The remote-aware selection uses a conservative static import map (no dynamic import resolution). If a test loads code dynamically that isn’t visible via imports, you may need to run it explicitly once to seed the map. +- The helper is test-only and does not alter library behavior. It activates automatically when you pass `--testmon`. + +Refreshing remote-selection artifacts +------------------------------------- +If you change test layout or significantly refactor imports, the remote-selection artifacts may become stale. +To rebuild them, delete the following files at the repo root and re-run with `--testmon` to seed again: + +```sh +# At the root of nemo-rl +rm .nrl_remote_map.json .nrl_remote_state.json +``` + + ### Run Unit Tests in a Hermetic Environment For environments lacking necessary dependencies (e.g., `gcc`, `nvcc`) diff --git a/nemo_rl/distributed/virtual_cluster.py b/nemo_rl/distributed/virtual_cluster.py index 5d5e0bd7d9..833376a017 100644 --- a/nemo_rl/distributed/virtual_cluster.py +++ b/nemo_rl/distributed/virtual_cluster.py @@ -78,6 +78,9 @@ def init_ray(log_dir: Optional[str] = None) -> None: Try to attach to an existing local cluster. If that cluster uses the same CUDA_VISIBLE_DEVICES or Slurm managed tag we will reuse it. Otherwise, we will detach and start a fresh local cluster. + + Args: + log_dir: Optional directory to store Ray logs and temp files. """ # Set up runtime environment env_vars = dict(os.environ) diff --git a/pyproject.toml b/pyproject.toml index b5ed8e4453..100c4ca250 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -134,6 +134,7 @@ test = [ "pytest-timeout", "pytest-cov", "pytest-asyncio", + "pytest-testmon", ] [tool.uv.sources] diff --git a/tests/unit/__init__.py b/tests/unit/__init__.py index 341a77c5bc..31c1220368 100644 --- a/tests/unit/__init__.py +++ b/tests/unit/__init__.py @@ -11,3 +11,15 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. + + +""" +Auto-loading remote_select plugin here: +- Ensures the plugin is discovered without extra CLI flags or global config. +- Loads early in pytest’s startup so ``pytest_load_initial_conftests`` can + rewrite args before other plugins (e.g., testmon) prune collection. +- Scopes behavior to unit tests only (does not affect functional tests). +- Avoids a top-level ``conftest.py`` that would apply repo-wide. +""" + +pytest_plugins = ["tests.unit._plugins.remote_select"] diff --git a/tests/unit/_plugins/remote_select.py b/tests/unit/_plugins/remote_select.py new file mode 100644 index 0000000000..a3f21c136a --- /dev/null +++ b/tests/unit/_plugins/remote_select.py @@ -0,0 +1,284 @@ +# Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +Remote-aware test selection helper for pytest-testmon (Python 3.12). + +Purpose +------- +When running unit tests with ``--testmon``, pytest-testmon tracks in-process +Python execution and reruns only affected tests. Code executed inside +``@ray.remote`` actors runs out-of-process, so testmon alone cannot see those +dependencies. This lightweight test-only plugin augments selection so that +edits inside remote actors can still retrigger the relevant tests. + +How it works +------------ +- Builds a static mapping from each unit test (nodeid) to the transitive set + of ``nemo_rl`` Python files that the test module imports. +- Stores the mapping in ``.nrl_remote_map.json`` and tracks mtimes in + ``.nrl_remote_state.json`` at repo root. +- When ``--testmon`` is present: + - On first run, seeds the state file and does not change selection. + - On subsequent runs, compares mtimes; if tracked files changed, it replaces + the pytest positional args with the affected nodeids so those tests run. +- Honors ``-k``. If a ``-k`` filter is provided, the plugin does not alter + selection and lets user intent win. + +Limitations +----------- +- Static import analysis only; dynamic imports/loading are not discovered. +- Only Python files are considered (YAML/JSON/shell edits are not tracked). +- The mapping is conservative; if a test exercises code not visible via + imports, run it once explicitly to seed the map. + +Activation +---------- +This plugin auto-loads via ``tests/unit/__init__.py`` and only engages when +``--testmon`` is present. + +Artifacts +--------- +Two JSON files are written to the repository root: + +1) ``.nrl_remote_map.json`` + - Maps test nodeids to the transitive set of project files (under ``nemo_rl/``) + imported by that test module. + - Example (paths abbreviated for readability): + { + "tests/unit/distributed/test_worker_groups.py::test_configure_worker_interaction": [ + "/workspaces/nemo-rl/nemo_rl/distributed/worker_groups.py", + "/workspaces/nemo-rl/nemo_rl/distributed/virtual_cluster.py" + ], + "tests/unit/models/policy/test_dtensor_worker.py::test_lm_policy_init[True]": [ + "/workspaces/nemo-rl/nemo_rl/models/policy/dtensor_policy_worker.py" + ] + } + +2) ``.nrl_remote_state.json`` + - Stores the last-seen modification time (mtime) per tracked file to detect changes. + - Example: + { + "/workspaces/nemo-rl/nemo_rl/distributed/worker_groups.py": 1725369123.456, + "/workspaces/nemo-rl/nemo_rl/models/policy/dtensor_policy_worker.py": 1725369187.012 + } +""" + +import ast +import json +import os +import sys +from pathlib import Path +from typing import Iterable + +REPO_ROOT: Path = Path(__file__).resolve().parents[3] +MAP_PATH: Path = REPO_ROOT / ".nrl_remote_map.json" +STATE_PATH: Path = REPO_ROOT / ".nrl_remote_state.json" +PROJECT_PREFIXES: tuple[str, ...] = ("nemo_rl",) + + +def _read_text(path: Path) -> str: + try: + return path.read_text() + except Exception: + return "" + + +def _parse_imported_modules(py_path: Path) -> set[str]: + src = _read_text(py_path) + try: + tree = ast.parse(src) + except Exception: + return set() + modules: set[str] = set() + for node in ast.walk(tree): + if isinstance(node, ast.Import): + for alias in node.names: + modules.add(alias.name) + elif isinstance(node, ast.ImportFrom): + if node.module: + modules.add(node.module) + return {m for m in modules if m.startswith(PROJECT_PREFIXES)} + + +def _module_to_file(module_name: str) -> Path | None: + mod_path = Path(module_name.replace(".", "/") + ".py") + abs_path = (REPO_ROOT / mod_path).resolve() + return abs_path if abs_path.exists() else None + + +def _discover_test_nodeids_and_files() -> dict[str, set[str]]: + mapping: dict[str, set[str]] = {} + tests_root = REPO_ROOT / "tests" / "unit" + for test_path in tests_root.rglob("test_*.py"): + rel = test_path.relative_to(REPO_ROOT) + mod_node_prefix = str(rel) + modules = _parse_imported_modules(test_path) + files: set[str] = set() + for m in modules: + f = _module_to_file(m) + if f: + files.add(str(f)) + if not files: + continue + src = _read_text(test_path) + try: + tree = ast.parse(src) + except Exception: + continue + for node in tree.body: + if isinstance(node, ast.FunctionDef) and node.name.startswith("test_"): + nodeid = f"{mod_node_prefix}::{node.name}" + mapping[nodeid] = set(files) + elif isinstance(node, ast.ClassDef) and node.name.startswith("Test"): + for sub in node.body: + if isinstance(sub, ast.FunctionDef) and sub.name.startswith( + "test_" + ): + nodeid = f"{mod_node_prefix}::{node.name}::{sub.name}" + mapping[nodeid] = set(files) + return mapping + + +def _load_mapping() -> dict[str, set[str]]: + if not MAP_PATH.exists(): + return {} + try: + data = json.loads(MAP_PATH.read_text()) + return {k: set(v) for k, v in data.items()} + except Exception: + return {} + + +def _save_mapping(mapping: dict[str, set[str]]) -> None: + MAP_PATH.write_text( + json.dumps({k: sorted(v) for k, v in mapping.items()}, indent=2) + ) + + +def _detect_changed(files: Iterable[str]) -> set[str]: + prev: dict[str, float] = {} + if STATE_PATH.exists(): + try: + prev = json.loads(STATE_PATH.read_text()) + except Exception: + prev = {} + changed: set[str] = set() + state: dict[str, float] = {} + for f in files: + try: + mtime = os.path.getmtime(f) + state[f] = mtime + if prev.get(f, 0) < mtime: + changed.add(f) + except FileNotFoundError: + changed.add(f) + if files: + STATE_PATH.write_text(json.dumps(state, indent=2)) + return changed + + +def _has_k_filter(args: list[str]) -> bool: + """Return True if -k/--keyword filter is present in CLI args.""" + if "-k" in args: + return True + for i, a in enumerate(args): + if a.startswith("-k") or a.startswith("--keyword"): + return True + if a in {"-k", "--keyword"} and i + 1 < len(args): + return True + return False + + +def pytest_load_initial_conftests(args, early_config, parser): + # Only augment when user asked for --testmon and no -k filter is provided + if "--testmon" not in args or _has_k_filter(args): + return + + affected = _select_affected(None) + # None = first run (seed only), empty set = no changes; leave args unchanged + if affected is None or affected == set(): + return + + # Remove --testmon and narrow args to affected nodeids (execute only those tests) + while "--testmon" in args: + args.remove("--testmon") + if not any(not a.startswith("-") for a in args): + args[:] = sorted(affected) + else: + args.extend(sorted(affected)) + + +def _effective_mapping() -> dict[str, set[str]]: + mapping = _load_mapping() + if not mapping: + mapping = _discover_test_nodeids_and_files() + if mapping: + _save_mapping(mapping) + return mapping + + +def _select_affected(config) -> set[str] | None: + mapping = _effective_mapping() + if not mapping: + return None + file_set: set[str] = set() + for files in mapping.values(): + file_set.update(files) + if not file_set: + return None + if not STATE_PATH.exists(): + _ = _detect_changed(file_set) + return None + changed = _detect_changed(file_set) + if not changed: + return set() + affected: set[str] = set() + for nodeid, files in mapping.items(): + if any(f in changed for f in files): + affected.add(nodeid) + return affected + + +def pytest_configure(config) -> None: + # Late-stage fallback in case initial hook didn't capture + tm_on = config.pluginmanager.hasplugin("testmon") or "--testmon" in sys.argv + if not tm_on: + return + # Honor -k/--keyword filters + if _has_k_filter(sys.argv): + return + affected = _select_affected(config) + if affected is None or affected == set(): + return + try: + config.args[:] = sorted(affected) + except Exception: + pass + + +def pytest_collection_modifyitems(config, items): + tm_on = config.pluginmanager.hasplugin("testmon") or "--testmon" in sys.argv + if not tm_on: + return + # Honor -k/--keyword filters + if _has_k_filter(sys.argv): + return + affected = _select_affected(config) + if affected is None: + return + if affected == set(): + # No changes → deselect all for speed + items[:] = [] + return + items[:] = [it for it in items if it.nodeid in affected] diff --git a/uv.lock b/uv.lock index 91580d7d37..e3dc17ad59 100644 --- a/uv.lock +++ b/uv.lock @@ -302,8 +302,8 @@ name = "bitsandbytes" version = "0.45.5" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "numpy", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, - { name = "torch", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, + { name = "numpy", marker = "(platform_machine != 'aarch64' and sys_platform != 'darwin') or sys_platform == 'win32'" }, + { name = "torch", marker = "(platform_machine != 'aarch64' and sys_platform != 'darwin') or sys_platform == 'win32'" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/07/b7/cb5ce4d1a382cf53c19ef06c5fc29e85f5e129b4da6527dd207d90a5b8ad/bitsandbytes-0.45.5-py3-none-manylinux_2_24_x86_64.whl", hash = "sha256:a5453f30cc6aab6ccaac364e6bf51a7808d3da5f71763dffeb6d9694c59136e4", size = 76059261, upload-time = "2025-04-07T13:32:52.573Z" }, @@ -567,7 +567,7 @@ name = "coloredlogs" version = "15.0.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "humanfriendly" }, + { name = "humanfriendly", marker = "sys_platform != 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/cc/c7/eed8f27100517e8c0e6b923d5f0845d0cb99763da6fdee00478f91db7325/coloredlogs-15.0.1.tar.gz", hash = "sha256:7c991aa71a4577af2f82600d8f8f3a89f936baeaf9b50a9c197da014e5bf16b0", size = 278520, upload-time = "2021-06-11T10:22:45.202Z" } wheels = [ @@ -735,9 +735,9 @@ name = "cppimport" version = "22.8.2" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "filelock" }, - { name = "mako" }, - { name = "pybind11" }, + { name = "filelock", marker = "sys_platform != 'darwin'" }, + { name = "mako", marker = "sys_platform != 'darwin'" }, + { name = "pybind11", marker = "sys_platform != 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/54/27/01d9078a77b9e31b79b9716e66ca4db74f4744c5232bcb3e8769395c4280/cppimport-22.8.2.tar.gz", hash = "sha256:bbb4957102db41bc99ad72c233bce92f9d1fd91be352fc07878c4361033a401f", size = 26635, upload-time = "2022-08-02T16:50:36.872Z" } @@ -824,8 +824,8 @@ name = "cupy-cuda12x" version = "13.6.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "fastrlock" }, - { name = "numpy" }, + { name = "fastrlock", marker = "sys_platform != 'darwin'" }, + { name = "numpy", marker = "sys_platform != 'darwin'" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/12/c5/7e7fc4816d0de0154e5d9053242c3a08a0ca8b43ee656a6f7b3b95055a7b/cupy_cuda12x-13.6.0-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:a6970ceefe40f9acbede41d7fe17416bd277b1bd2093adcde457b23b578c5a59", size = 127334633, upload-time = "2025-08-18T08:24:43.065Z" }, @@ -1965,8 +1965,8 @@ name = "liger-kernel" version = "0.5.8" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "torch", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, - { name = "triton", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, + { name = "torch", marker = "(platform_machine != 'aarch64' and sys_platform != 'darwin') or sys_platform == 'win32'" }, + { name = "triton", marker = "(platform_machine != 'aarch64' and sys_platform != 'darwin') or sys_platform == 'win32'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/a2/55/3a703f337110e2a121a04e503abfeec2c191529cbee18bb1fb630d65642a/liger_kernel-0.5.8.tar.gz", hash = "sha256:3246d7dced89e0f982a52de259d4f78fd10eb9171246b28ae52b63ad09fc0732", size = 3593097, upload-time = "2025-04-12T16:44:32.252Z" } wheels = [ @@ -2517,12 +2517,12 @@ name = "mlx-lm" version = "0.26.3" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "jinja2", marker = "sys_platform != 'linux'" }, - { name = "mlx", marker = "sys_platform != 'linux'" }, - { name = "numpy", marker = "sys_platform != 'linux'" }, - { name = "protobuf", marker = "sys_platform != 'linux'" }, - { name = "pyyaml", marker = "sys_platform != 'linux'" }, - { name = "transformers", marker = "sys_platform != 'linux'" }, + { name = "jinja2", marker = "sys_platform == 'darwin'" }, + { name = "mlx", marker = "sys_platform == 'darwin'" }, + { name = "numpy", marker = "sys_platform == 'darwin'" }, + { name = "protobuf", marker = "sys_platform == 'darwin'" }, + { name = "pyyaml", marker = "sys_platform == 'darwin'" }, + { name = "transformers", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/af/4b/ed8ec01f182203b0897415a9d20f0cd8a141def77ad43deea18ffaba4c9c/mlx_lm-0.26.3.tar.gz", hash = "sha256:06cd74ee3eea920335c528e68feb854eede45fe4e5f149b464ac100c1dbeaded", size = 172096, upload-time = "2025-08-06T21:48:22.762Z" } wheels = [ @@ -2900,6 +2900,7 @@ test = [ { name = "pytest" }, { name = "pytest-asyncio" }, { name = "pytest-cov" }, + { name = "pytest-testmon" }, { name = "pytest-timeout" }, ] @@ -2983,6 +2984,7 @@ test = [ { name = "pytest", specifier = ">=7.0.0" }, { name = "pytest-asyncio" }, { name = "pytest-cov" }, + { name = "pytest-testmon" }, { name = "pytest-timeout" }, ] @@ -3238,22 +3240,22 @@ name = "nvidia-modelopt" version = "0.33.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "ninja" }, - { name = "numpy" }, - { name = "nvidia-ml-py" }, - { name = "nvidia-modelopt-core" }, - { name = "packaging" }, - { name = "pulp" }, - { name = "pydantic" }, - { name = "regex" }, - { name = "rich" }, - { name = "safetensors" }, - { name = "scipy" }, - { name = "torch" }, - { name = "torchprofile" }, + { name = "ninja", marker = "sys_platform != 'darwin'" }, + { name = "numpy", marker = "sys_platform != 'darwin'" }, + { name = "nvidia-ml-py", marker = "sys_platform != 'darwin'" }, + { name = "nvidia-modelopt-core", marker = "sys_platform != 'darwin'" }, + { name = "packaging", marker = "sys_platform != 'darwin'" }, + { name = "pulp", marker = "sys_platform != 'darwin'" }, + { name = "pydantic", marker = "sys_platform != 'darwin'" }, + { name = "regex", marker = "sys_platform != 'darwin'" }, + { name = "rich", marker = "sys_platform != 'darwin'" }, + { name = "safetensors", marker = "sys_platform != 'darwin'" }, + { name = "scipy", marker = "sys_platform != 'darwin'" }, + { name = "torch", marker = "sys_platform != 'darwin'" }, + { name = "torchprofile", marker = "sys_platform != 'darwin'" }, { name = "torchvision", version = "0.22.1", source = { registry = "https://download.pytorch.org/whl/cu128" }, marker = "platform_machine == 'aarch64' and sys_platform == 'linux'" }, - { name = "torchvision", version = "0.22.1+cu128", source = { registry = "https://download.pytorch.org/whl/cu128" }, marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, - { name = "tqdm" }, + { name = "torchvision", version = "0.22.1+cu128", source = { registry = "https://download.pytorch.org/whl/cu128" }, marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, + { name = "tqdm", marker = "sys_platform != 'darwin'" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/ca/cb/4af39357792a96f334c7877ea0380c9337aec210ff4794a7dd95beb7c349/nvidia_modelopt-0.33.1-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:6c51091683a117cd40fdb96a0ec28579f2276f6b627db7ccddc370df544e1dd7", size = 751683, upload-time = "2025-08-12T18:37:48.832Z" }, @@ -3262,18 +3264,18 @@ wheels = [ [package.optional-dependencies] onnx = [ - { name = "cppimport" }, + { name = "cppimport", marker = "sys_platform != 'darwin'" }, { name = "cupy-cuda12x", marker = "platform_machine != 'aarch64' and sys_platform != 'darwin'" }, - { name = "ml-dtypes", version = "0.4.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13'" }, - { name = "ml-dtypes", version = "0.5.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13'" }, - { name = "onnx" }, - { name = "onnx-graphsurgeon" }, - { name = "onnxconverter-common" }, - { name = "onnxruntime", marker = "platform_machine == 'aarch64' or sys_platform == 'darwin'" }, + { name = "ml-dtypes", version = "0.4.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13' and sys_platform != 'darwin'" }, + { name = "ml-dtypes", version = "0.5.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13' and sys_platform != 'darwin'" }, + { name = "onnx", marker = "sys_platform != 'darwin'" }, + { name = "onnx-graphsurgeon", marker = "sys_platform != 'darwin'" }, + { name = "onnxconverter-common", marker = "sys_platform != 'darwin'" }, + { name = "onnxruntime", marker = "platform_machine == 'aarch64' and sys_platform != 'darwin'" }, { name = "onnxruntime-gpu", version = "1.20.0", source = { registry = "https://pypi.org/simple" }, marker = "sys_platform == 'win32'" }, { name = "onnxruntime-gpu", version = "1.22.0", source = { registry = "https://pypi.org/simple" }, marker = "platform_machine != 'aarch64' and sys_platform != 'darwin' and sys_platform != 'win32'" }, - { name = "onnxscript" }, - { name = "polygraphy" }, + { name = "onnxscript", marker = "sys_platform != 'darwin'" }, + { name = "polygraphy", marker = "sys_platform != 'darwin'" }, ] [[package]] @@ -3314,13 +3316,13 @@ name = "nvidia-resiliency-ext" version = "0.4.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "defusedxml" }, - { name = "nvidia-ml-py" }, - { name = "packaging" }, - { name = "psutil" }, - { name = "pynvml" }, - { name = "pyyaml" }, - { name = "torch" }, + { name = "defusedxml", marker = "sys_platform != 'darwin'" }, + { name = "nvidia-ml-py", marker = "sys_platform != 'darwin'" }, + { name = "packaging", marker = "sys_platform != 'darwin'" }, + { name = "psutil", marker = "sys_platform != 'darwin'" }, + { name = "pynvml", marker = "sys_platform != 'darwin'" }, + { name = "pyyaml", marker = "sys_platform != 'darwin'" }, + { name = "torch", marker = "sys_platform != 'darwin'" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/70/05/38d491962273c7905708762279f440520eb79f3c00b67a023497215ad023/nvidia_resiliency_ext-0.4.1-cp312-cp312-manylinux_2_31_aarch64.whl", hash = "sha256:b3bd5f01535574b16d0f38bca6e39afe3806c4a2896eee1b321cd944e00025a7", size = 444570, upload-time = "2025-07-17T03:50:58.877Z" }, @@ -3371,9 +3373,9 @@ name = "onnx" version = "1.18.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "numpy" }, - { name = "protobuf" }, - { name = "typing-extensions" }, + { name = "numpy", marker = "sys_platform != 'darwin'" }, + { name = "protobuf", marker = "sys_platform != 'darwin'" }, + { name = "typing-extensions", marker = "sys_platform != 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/3d/60/e56e8ec44ed34006e6d4a73c92a04d9eea6163cc12440e35045aec069175/onnx-1.18.0.tar.gz", hash = "sha256:3d8dbf9e996629131ba3aa1afd1d8239b660d1f830c6688dd7e03157cccd6b9c", size = 12563009, upload-time = "2025-05-12T22:03:09.626Z" } wheels = [ @@ -3395,8 +3397,8 @@ name = "onnx-graphsurgeon" version = "0.5.8" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "numpy" }, - { name = "onnx" }, + { name = "numpy", marker = "sys_platform != 'darwin'" }, + { name = "onnx", marker = "sys_platform != 'darwin'" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/71/53/98334c4f64a9e289a8cb48f5e7966b8ff015414d0bf26587cf46d764f1d8/onnx_graphsurgeon-0.5.8-py2.py3-none-any.whl", hash = "sha256:6f611ea29a8e4740fbab1aae52bf4c40b8b9918f8459058d20b99acc79fce121", size = 57923, upload-time = "2025-04-10T18:49:24.483Z" }, @@ -3407,11 +3409,11 @@ name = "onnx-ir" version = "0.1.7" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "ml-dtypes", version = "0.4.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13'" }, - { name = "ml-dtypes", version = "0.5.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13'" }, - { name = "numpy" }, - { name = "onnx" }, - { name = "typing-extensions" }, + { name = "ml-dtypes", version = "0.4.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13' and sys_platform != 'darwin'" }, + { name = "ml-dtypes", version = "0.5.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13' and sys_platform != 'darwin'" }, + { name = "numpy", marker = "sys_platform != 'darwin'" }, + { name = "onnx", marker = "sys_platform != 'darwin'" }, + { name = "typing-extensions", marker = "sys_platform != 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/6a/14/4a003926218f8edee6da19546f69a1831b74cdd993eaf5ff50a2fb168e70/onnx_ir-0.1.7.tar.gz", hash = "sha256:4734b7587807ca657158b042c138879c3f454756fae74e949f6c99f0107d8df6", size = 107944, upload-time = "2025-08-22T15:01:16.383Z" } wheels = [ @@ -3423,10 +3425,10 @@ name = "onnxconverter-common" version = "1.15.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "numpy" }, - { name = "onnx" }, - { name = "packaging" }, - { name = "protobuf" }, + { name = "numpy", marker = "sys_platform != 'darwin'" }, + { name = "onnx", marker = "sys_platform != 'darwin'" }, + { name = "packaging", marker = "sys_platform != 'darwin'" }, + { name = "protobuf", marker = "sys_platform != 'darwin'" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/76/ac/c3ff41cc2d36c8caab51bffa9185ea64019f161850b9641eb0409b243ae1/onnxconverter_common-1.15.0-py2.py3-none-any.whl", hash = "sha256:24579ed1bb3c10beca39a4517d196c17341911be5bd09bd0e6050a7379a2a7d9", size = 89640, upload-time = "2025-07-01T16:42:56.968Z" }, @@ -3437,12 +3439,12 @@ name = "onnxruntime" version = "1.22.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "coloredlogs", marker = "platform_machine == 'aarch64' or sys_platform == 'darwin' or sys_platform == 'win32'" }, - { name = "flatbuffers", marker = "platform_machine == 'aarch64' or sys_platform == 'darwin' or sys_platform == 'win32'" }, - { name = "numpy", marker = "platform_machine == 'aarch64' or sys_platform == 'darwin' or sys_platform == 'win32'" }, - { name = "packaging", marker = "platform_machine == 'aarch64' or sys_platform == 'darwin' or sys_platform == 'win32'" }, - { name = "protobuf", marker = "platform_machine == 'aarch64' or sys_platform == 'darwin' or sys_platform == 'win32'" }, - { name = "sympy", marker = "platform_machine == 'aarch64' or sys_platform == 'darwin' or sys_platform == 'win32'" }, + { name = "coloredlogs", marker = "(platform_machine == 'aarch64' and sys_platform != 'darwin') or sys_platform == 'win32'" }, + { name = "flatbuffers", marker = "(platform_machine == 'aarch64' and sys_platform != 'darwin') or sys_platform == 'win32'" }, + { name = "numpy", marker = "(platform_machine == 'aarch64' and sys_platform != 'darwin') or sys_platform == 'win32'" }, + { name = "packaging", marker = "(platform_machine == 'aarch64' and sys_platform != 'darwin') or sys_platform == 'win32'" }, + { name = "protobuf", marker = "(platform_machine == 'aarch64' and sys_platform != 'darwin') or sys_platform == 'win32'" }, + { name = "sympy", marker = "(platform_machine == 'aarch64' and sys_platform != 'darwin') or sys_platform == 'win32'" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/29/e5/00b099b4d4f6223b610421080d0eed9327ef9986785c9141819bbba0d396/onnxruntime-1.22.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:984cea2a02fcc5dfea44ade9aca9fe0f7a8a2cd6f77c258fc4388238618f3928", size = 14473861, upload-time = "2025-07-10T19:15:42.911Z" }, @@ -3500,13 +3502,13 @@ name = "onnxscript" version = "0.4.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "ml-dtypes", version = "0.4.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13'" }, - { name = "ml-dtypes", version = "0.5.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13'" }, - { name = "numpy" }, - { name = "onnx" }, - { name = "onnx-ir" }, - { name = "packaging" }, - { name = "typing-extensions" }, + { name = "ml-dtypes", version = "0.4.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13' and sys_platform != 'darwin'" }, + { name = "ml-dtypes", version = "0.5.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13' and sys_platform != 'darwin'" }, + { name = "numpy", marker = "sys_platform != 'darwin'" }, + { name = "onnx", marker = "sys_platform != 'darwin'" }, + { name = "onnx-ir", marker = "sys_platform != 'darwin'" }, + { name = "packaging", marker = "sys_platform != 'darwin'" }, + { name = "typing-extensions", marker = "sys_platform != 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/a8/9f/45aed9951d3fa50a97b910487186ef9c15ad08d3c9cb3605aabd99f65f92/onnxscript-0.4.0.tar.gz", hash = "sha256:de618eeb6e0c57f5a70f85909ab1f829cbb2053ad55f8f2fcc2701fa29b7adfc", size = 567393, upload-time = "2025-08-22T21:05:46.416Z" } wheels = [ @@ -4322,7 +4324,7 @@ name = "pynvml" version = "12.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-ml-py" }, + { name = "nvidia-ml-py", marker = "sys_platform != 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/26/6f/6b5880ed0239e85b9a39aed103b65b2ef81425beef9f45e5c035bf008330/pynvml-12.0.0.tar.gz", hash = "sha256:299ce2451a6a17e6822d6faee750103e25b415f06f59abb8db65d30f794166f5", size = 33636, upload-time = "2024-12-02T15:04:36.631Z" } wheels = [ @@ -4429,6 +4431,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a4/7f/92c8dbe185aa38270fec1e73e0ed70d8e5de31963aa057ba621055f8b008/pytest_random_order-1.2.0-py3-none-any.whl", hash = "sha256:78d1d6f346222cdf26a7302c502d2f1cab19454529af960b8b9e1427a99ab277", size = 10889, upload-time = "2025-06-22T14:44:42.438Z" }, ] +[[package]] +name = "pytest-testmon" +version = "2.1.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "coverage" }, + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/54/24/b17712bc8b9d9814a30346e5bd76a6c4539f5187455f4e0d99d95f033da6/pytest_testmon-2.1.3.tar.gz", hash = "sha256:dad41aa7d501d74571750da1abd3f6673b63fd9dbf3023bd1623814999018c97", size = 22608, upload-time = "2024-12-22T12:43:28.822Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/73/08/278800711d937e76ce59105fea1bb739ae5ff5c13583fd064fe3b4e64fa1/pytest_testmon-2.1.3-py3-none-any.whl", hash = "sha256:53ba06d8a90ce24c3a191b196aac72ca4b788beff5eb1c1bffee04dc50ec7105", size = 24994, upload-time = "2024-12-22T12:43:10.173Z" }, +] + [[package]] name = "pytest-timeout" version = "2.4.0" @@ -5647,10 +5662,10 @@ name = "torchprofile" version = "0.0.4" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "numpy" }, - { name = "torch" }, + { name = "numpy", marker = "sys_platform != 'darwin'" }, + { name = "torch", marker = "sys_platform != 'darwin'" }, { name = "torchvision", version = "0.22.1", source = { registry = "https://download.pytorch.org/whl/cu128" }, marker = "platform_machine == 'aarch64' and sys_platform == 'linux'" }, - { name = "torchvision", version = "0.22.1+cu128", source = { registry = "https://download.pytorch.org/whl/cu128" }, marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, + { name = "torchvision", version = "0.22.1+cu128", source = { registry = "https://download.pytorch.org/whl/cu128" }, marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/6f/36/574c0c46e818533b78b3c09505211162918188325ab4165ef11a3f295755/torchprofile-0.0.4.tar.gz", hash = "sha256:96b6da17d752a06b02977e078aea95614893b31d4117dd5dcd081f30ce65611b", size = 4557, upload-time = "2021-06-22T04:58:03.592Z" } wheels = [