From 30a140ff7929a4751f57d87a64dcd1683d29e382 Mon Sep 17 00:00:00 2001 From: dpys Date: Sun, 8 Dec 2024 02:21:03 -0500 Subject: [PATCH 1/9] feat: cli flexibility --- MANIFEST.ini | 1 + asv.conf.json | 1 + nxbench/cli.py | 163 ++++++++++++++++++++-------------- nxbench/configs/asv.conf.json | 6 +- pyproject.toml | 7 +- 5 files changed, 107 insertions(+), 71 deletions(-) create mode 120000 asv.conf.json diff --git a/MANIFEST.ini b/MANIFEST.ini index c7ae914..f3261e9 100644 --- a/MANIFEST.ini +++ b/MANIFEST.ini @@ -1,6 +1,7 @@ include README.md include LICENSE include pyproject.toml +include asv.conf.json recursive-include nxbench/configs * recursive-include nxbench/data * diff --git a/asv.conf.json b/asv.conf.json new file mode 120000 index 0000000..2ca45c3 --- /dev/null +++ b/asv.conf.json @@ -0,0 +1 @@ +/Users/derekpisner/Applications/nxbench/nxbench/configs/asv.conf.json \ No newline at end of file diff --git a/nxbench/cli.py b/nxbench/cli.py index 7a3a407..b68739d 100644 --- a/nxbench/cli.py +++ b/nxbench/cli.py @@ -1,9 +1,12 @@ +import json import logging import os import shutil import subprocess import sys +import tempfile from collections.abc import Sequence +from importlib import resources from pathlib import Path import click @@ -19,23 +22,7 @@ def validate_executable(path: str | Path) -> Path: - """Validate an executable path. - - Parameters - ---------- - path : str or Path - Path to executable to validate - - Returns - ------- - Path - Validated executable path - - Raises - ------ - ValueError - If path is not a valid executable - """ + """Validate an executable path.""" executable = Path(path).resolve() if not executable.exists(): raise ValueError(f"Executable not found: {executable}") @@ -50,32 +37,31 @@ def safe_run( capture_output: bool = False, **kwargs, ) -> subprocess.CompletedProcess: - """Safely run a subprocess command with optional output capture. + """ + Safely run a subprocess command with optional output capture. Parameters ---------- - cmd : sequence of str or Path - Command and arguments to run. First item must be path to executable. + cmd : Sequence[str | Path] + The command and arguments to execute. check : bool, default=True - Whether to check return code + If True, raise an exception if the command fails. capture_output : bool, default=False - Whether to capture stdout and stderr + If True, capture stdout and stderr. **kwargs : dict - Additional arguments to subprocess.run + Additional keyword arguments to pass to subprocess.run. Returns ------- subprocess.CompletedProcess - Completed process info + The completed process. Raises ------ - ValueError - If command is empty TypeError - If command contains invalid argument types - subprocess.SubprocessError - If command fails and check=True + If a command argument is not of type str or Path. + ValueError + If a command argument contains potentially unsafe characters. """ if not cmd: raise ValueError("Empty command") @@ -86,6 +72,8 @@ def safe_run( for arg in cmd[1:]: if not isinstance(arg, (str, Path)): raise TypeError(f"Command argument must be str or Path, got {type(arg)}") + if ";" in str(arg) or "&&" in str(arg) or "|" in str(arg): + raise ValueError(f"Potentially unsafe argument: {arg}") safe_cmd.append(str(arg)) return subprocess.run( # noqa: S603 @@ -138,42 +126,95 @@ def get_git_hash() -> str: return "unknown" -def run_asv_command( - args: Sequence[str], check: bool = True -) -> subprocess.CompletedProcess: - """Run ASV command with security checks. +def find_project_root() -> Path: + """Find the project root directory (one containing .git).""" + current = Path(__file__).resolve() + for parent in current.parents: + if (parent / ".git").exists(): + return parent + return current.parents[1] - Parameters - ---------- - args : sequence of str - Command arguments - check : bool, default=True - Whether to check return code - Returns - ------- - subprocess.CompletedProcess - Completed process info +def ensure_asv_config_in_root(): + """Ensure asv.conf.json is present at the project root as a symlink.""" + project_root = find_project_root() + target = project_root / "asv.conf.json" + if not target.exists(): + with resources.path("nxbench.configs", "asv.conf.json") as config_path: + target.symlink_to(config_path) + return project_root - Raises - ------ - click.ClickException - If command fails - """ + +def run_asv_command( + args: Sequence[str], check: bool = True +) -> subprocess.CompletedProcess: + """Run ASV command with dynamic asv.conf.json based on DVCS presence.""" asv_path = get_asv_executable() if asv_path is None: raise click.ClickException("ASV executable not found") - safe_args = [] - for arg in args: - if not isinstance(arg, str): - raise click.ClickException(f"Invalid argument type: {type(arg)}") - safe_args.append(arg) + project_root = find_project_root() + has_git = (project_root / ".git").exists() + + logger.debug(f"Project root: {project_root}") + logger.debug(f"Has .git: {has_git}") try: - return safe_run([asv_path, *safe_args], check=check) - except (subprocess.SubprocessError, ValueError) as e: - raise click.ClickException(str(e)) + with resources.open_text("nxbench.configs", "asv.conf.json") as f: + config_data = json.load(f) + except FileNotFoundError: + raise click.ClickException("asv.conf.json not found in package resources.") + + if not has_git: + logger.debug( + "No .git directory found. Modifying asv.conf.json for remote repo and " + "virtualenv." + ) + config_data["repo"] = "https://github.com/dpys/nxbench.git" + config_data["environment_type"] = "virtualenv" + else: + logger.debug("Found .git directory. Using existing repository settings.") + + with tempfile.TemporaryDirectory() as tmpdir: + temp_config_path = Path(tmpdir) / "asv.conf.json" + try: + with temp_config_path.open("w") as f: + json.dump(config_data, f, indent=4) + logger.debug(f"Temporary asv.conf.json created at: {temp_config_path}") + except Exception as e: + raise click.ClickException(f"Failed to write temporary asv.conf.json: {e}") + + safe_args = [] + for arg in args: + if not isinstance(arg, str): + raise click.ClickException(f"Invalid argument type: {type(arg)}") + if ";" in arg or "&&" in arg or "|" in arg: + raise click.ClickException(f"Potentially unsafe argument: {arg}") + safe_args.append(arg) + + if "--config" not in safe_args: + safe_args = ["--config", str(temp_config_path), *safe_args] + logger.debug(f"Added --config {temp_config_path} to ASV arguments.") + + old_cwd = Path.cwd() + if has_git: + os.chdir(project_root) + logger.debug(f"Changed working directory to project root: {project_root}") + + try: + asv_command = [asv_path, *safe_args] + logger.debug(f"Executing ASV command: {' '.join(map(str, asv_command))}") + return safe_run(asv_command, check=check) + except subprocess.CalledProcessError: + logger.exception("ASV command failed") + raise click.ClickException("ASV command failed") + except (subprocess.SubprocessError, ValueError): + logger.exception("ASV subprocess error occurred") + raise click.ClickException("ASV subprocess error occurred") + finally: + if has_git: + os.chdir(old_cwd) + logger.debug(f"Restored working directory to: {old_cwd}") @click.group() @@ -329,15 +370,7 @@ def run_benchmark(ctx, backend: tuple[str], collection: str): ) @click.pass_context def export(ctx, result_file: Path, output_format: str): - """Export benchmark results. - - Parameters - ---------- - result_file : Path - Output file path for results - output_format : str - Format to export results in (json, csv, or sql) - """ + """Export benchmark results.""" config = ctx.obj.get("CONFIG") if config: logger.debug(f"Using config file for export: {config}") diff --git a/nxbench/configs/asv.conf.json b/nxbench/configs/asv.conf.json index 00c440f..ad09f15 100644 --- a/nxbench/configs/asv.conf.json +++ b/nxbench/configs/asv.conf.json @@ -3,14 +3,14 @@ "project": "nxbench", "timeout": 3000, "project_url": "https://github.com/dpys/nxbench", - "repo": ".", + "repo": "https://github.com/dpys/nxbench.git", "branches": [ "main" ], - "environment_type": "existing", + "environment_type": "virtualenv", "show_commit_url": "https://github.com/dpys/nxbench/commit/", "pythons": [ - "3.11" + "/usr/local/bin/python3.11" ], "req": [ "networkx==3.4.2", diff --git a/pyproject.toml b/pyproject.toml index 36b584d..16733ff 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -90,6 +90,10 @@ packages = [ 'nxbench.configs', ] +[tool.setuptools.package-data] +"nxbench.configs" = ["asv.conf.json", "example.yaml"] +"nxbench.data" = ["network_directory.csv"] + platforms = [ 'Linux', 'Mac OSX', @@ -100,9 +104,6 @@ platforms = [ [tool.setuptools.dynamic] version = { attr = "nxbench._version.__version__" } -[tool.setuptools.package-data] -"nxbench.data" = ["network_directory.csv"] - [tool.black] line-length = 88 target-version = ["py310", "py311", "py312"] From 52cf830bcf5464e713973eeff81e00166c4c2335 Mon Sep 17 00:00:00 2001 From: dpys Date: Sun, 8 Dec 2024 02:32:09 -0500 Subject: [PATCH 2/9] feat: cli flexibility --- nxbench/cli.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/nxbench/cli.py b/nxbench/cli.py index b68739d..47d58ab 100644 --- a/nxbench/cli.py +++ b/nxbench/cli.py @@ -145,6 +145,10 @@ def ensure_asv_config_in_root(): return project_root +def has_git(project_root): + return (project_root / ".git").exists() + + def run_asv_command( args: Sequence[str], check: bool = True ) -> subprocess.CompletedProcess: @@ -154,10 +158,9 @@ def run_asv_command( raise click.ClickException("ASV executable not found") project_root = find_project_root() - has_git = (project_root / ".git").exists() - + _has_git = has_git(project_root) logger.debug(f"Project root: {project_root}") - logger.debug(f"Has .git: {has_git}") + logger.debug(f"Has .git: {_has_git}") try: with resources.open_text("nxbench.configs", "asv.conf.json") as f: @@ -165,7 +168,7 @@ def run_asv_command( except FileNotFoundError: raise click.ClickException("asv.conf.json not found in package resources.") - if not has_git: + if not _has_git: logger.debug( "No .git directory found. Modifying asv.conf.json for remote repo and " "virtualenv." @@ -197,7 +200,7 @@ def run_asv_command( logger.debug(f"Added --config {temp_config_path} to ASV arguments.") old_cwd = Path.cwd() - if has_git: + if _has_git: os.chdir(project_root) logger.debug(f"Changed working directory to project root: {project_root}") From d72442de089c777b4a048366f7de8c7770bc0923 Mon Sep 17 00:00:00 2001 From: dpys Date: Sun, 8 Dec 2024 02:42:37 -0500 Subject: [PATCH 3/9] feat: cli flexibility --- nxbench/cli.py | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/nxbench/cli.py b/nxbench/cli.py index 47d58ab..475d41c 100644 --- a/nxbench/cli.py +++ b/nxbench/cli.py @@ -178,6 +178,8 @@ def run_asv_command( else: logger.debug("Found .git directory. Using existing repository settings.") + config_data["pythons"] = [str(get_python_executable())] + with tempfile.TemporaryDirectory() as tmpdir: temp_config_path = Path(tmpdir) / "asv.conf.json" try: @@ -207,15 +209,21 @@ def run_asv_command( try: asv_command = [asv_path, *safe_args] logger.debug(f"Executing ASV command: {' '.join(map(str, asv_command))}") - return safe_run(asv_command, check=check) + return subprocess.run( # noqa: S603 + asv_command, + capture_output=True, + text=True, + shell=False, + check=check, + ) except subprocess.CalledProcessError: - logger.exception("ASV command failed") - raise click.ClickException("ASV command failed") + logger.exception("ASV command failed.") + raise click.ClickException("ASV command failed.") except (subprocess.SubprocessError, ValueError): - logger.exception("ASV subprocess error occurred") - raise click.ClickException("ASV subprocess error occurred") + logger.exception("ASV subprocess error occurred.") + raise click.ClickException("ASV subprocess error occurred.") finally: - if has_git: + if _has_git: os.chdir(old_cwd) logger.debug(f"Restored working directory to: {old_cwd}") @@ -332,13 +340,17 @@ def run_benchmark(ctx, backend: tuple[str], collection: str): if config: logger.debug(f"Config file used for benchmark run: {config}") - try: - git_hash = get_git_hash() - except subprocess.CalledProcessError: - logger.exception("Failed to get git hash") - raise click.ClickException("Could not determine git commit hash") + cmd_args = ["run", "--quick"] - cmd_args = ["run", "--quick", f"--set-commit-hash={git_hash}"] + project_root = find_project_root() + _has_git = has_git(project_root) + if _has_git: + try: + git_hash = get_git_hash() + cmd_args.append(f"--set-commit-hash={git_hash}") + except subprocess.CalledProcessError: + logger.exception("Failed to get git hash") + raise click.ClickException("Could not determine git commit hash") if package_config.verbosity_level >= 1: cmd_args.append("--verbose") From c781bfb579339f81d6917b31f71ba963845d336e Mon Sep 17 00:00:00 2001 From: dpys Date: Sun, 8 Dec 2024 02:50:03 -0500 Subject: [PATCH 4/9] feat: cli flexibility --- nxbench/cli.py | 11 +++-------- nxbench/configs/asv.conf.json | 3 --- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/nxbench/cli.py b/nxbench/cli.py index 475d41c..edfb263 100644 --- a/nxbench/cli.py +++ b/nxbench/cli.py @@ -178,6 +178,7 @@ def run_asv_command( else: logger.debug("Found .git directory. Using existing repository settings.") + # **Set the 'pythons' field to the current Python executable** config_data["pythons"] = [str(get_python_executable())] with tempfile.TemporaryDirectory() as tmpdir: @@ -207,15 +208,9 @@ def run_asv_command( logger.debug(f"Changed working directory to project root: {project_root}") try: - asv_command = [asv_path, *safe_args] + asv_command = [str(asv_path), *safe_args] logger.debug(f"Executing ASV command: {' '.join(map(str, asv_command))}") - return subprocess.run( # noqa: S603 - asv_command, - capture_output=True, - text=True, - shell=False, - check=check, - ) + return safe_run(asv_command) except subprocess.CalledProcessError: logger.exception("ASV command failed.") raise click.ClickException("ASV command failed.") diff --git a/nxbench/configs/asv.conf.json b/nxbench/configs/asv.conf.json index ad09f15..9f20688 100644 --- a/nxbench/configs/asv.conf.json +++ b/nxbench/configs/asv.conf.json @@ -9,9 +9,6 @@ ], "environment_type": "virtualenv", "show_commit_url": "https://github.com/dpys/nxbench/commit/", - "pythons": [ - "/usr/local/bin/python3.11" - ], "req": [ "networkx==3.4.2", "nx_parallel==0.3", From 80fbe8521042596ba090316d3b1daf8e7910b638 Mon Sep 17 00:00:00 2001 From: dpys Date: Sun, 8 Dec 2024 03:02:50 -0500 Subject: [PATCH 5/9] feat: dynamically resolve benchmarks_dir location --- nxbench/cli.py | 17 ++++++++++++++++- nxbench/configs/asv.conf.json | 2 +- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/nxbench/cli.py b/nxbench/cli.py index edfb263..e8faf10 100644 --- a/nxbench/cli.py +++ b/nxbench/cli.py @@ -178,7 +178,22 @@ def run_asv_command( else: logger.debug("Found .git directory. Using existing repository settings.") - # **Set the 'pythons' field to the current Python executable** + try: + import nxbench + + nxbench_path = Path(nxbench.__file__).resolve().parent + benchmark_dir = nxbench_path / "benchmarks" + if not benchmark_dir.exists(): + logger.error( + FileNotFoundError(f"Benchmark directory not found: {benchmark_dir}") + ) + config_data["benchmark_dir"] = str(benchmark_dir) + logger.debug(f"Set benchmark_dir to: {benchmark_dir}") + except ImportError: + raise click.ClickException("Failed to import nxbench. Ensure it is installed.") + except FileNotFoundError as e: + raise click.ClickException(str(e)) + config_data["pythons"] = [str(get_python_executable())] with tempfile.TemporaryDirectory() as tmpdir: diff --git a/nxbench/configs/asv.conf.json b/nxbench/configs/asv.conf.json index 9f20688..02d1604 100644 --- a/nxbench/configs/asv.conf.json +++ b/nxbench/configs/asv.conf.json @@ -5,7 +5,7 @@ "project_url": "https://github.com/dpys/nxbench", "repo": "https://github.com/dpys/nxbench.git", "branches": [ - "main" + "cli-from-anywhere" ], "environment_type": "virtualenv", "show_commit_url": "https://github.com/dpys/nxbench/commit/", From 5edaec36ad9922d21751582a1cf8d957967253ea Mon Sep 17 00:00:00 2001 From: dpys Date: Sun, 8 Dec 2024 03:03:18 -0500 Subject: [PATCH 6/9] feat: dynamically resolve benchmarks_dir location --- nxbench/cli.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/nxbench/cli.py b/nxbench/cli.py index e8faf10..20b2429 100644 --- a/nxbench/cli.py +++ b/nxbench/cli.py @@ -184,9 +184,7 @@ def run_asv_command( nxbench_path = Path(nxbench.__file__).resolve().parent benchmark_dir = nxbench_path / "benchmarks" if not benchmark_dir.exists(): - logger.error( - FileNotFoundError(f"Benchmark directory not found: {benchmark_dir}") - ) + logger.error(f"Benchmark directory not found: {benchmark_dir}") config_data["benchmark_dir"] = str(benchmark_dir) logger.debug(f"Set benchmark_dir to: {benchmark_dir}") except ImportError: From c034ad8435cb3b06caf9f2957185089a11ee4b1c Mon Sep 17 00:00:00 2001 From: dpys Date: Sun, 8 Dec 2024 03:26:53 -0500 Subject: [PATCH 7/9] fix: asv from anywhere --- MANIFEST.ini | 1 - asv.conf.json | 1 - nxbench/cli.py | 65 +++++++++++++++++++++++++++-------- nxbench/configs/asv.conf.json | 9 ----- nxbench/configs/example.yaml | 9 +++++ 5 files changed, 59 insertions(+), 26 deletions(-) delete mode 120000 asv.conf.json diff --git a/MANIFEST.ini b/MANIFEST.ini index f3261e9..c7ae914 100644 --- a/MANIFEST.ini +++ b/MANIFEST.ini @@ -1,7 +1,6 @@ include README.md include LICENSE include pyproject.toml -include asv.conf.json recursive-include nxbench/configs * recursive-include nxbench/data * diff --git a/asv.conf.json b/asv.conf.json deleted file mode 120000 index 2ca45c3..0000000 --- a/asv.conf.json +++ /dev/null @@ -1 +0,0 @@ -/Users/derekpisner/Applications/nxbench/nxbench/configs/asv.conf.json \ No newline at end of file diff --git a/nxbench/cli.py b/nxbench/cli.py index 20b2429..99b9ff1 100644 --- a/nxbench/cli.py +++ b/nxbench/cli.py @@ -11,6 +11,7 @@ import click import pandas as pd +import yaml from nxbench.benchmarks.config import DatasetConfig from nxbench.data.loader import BenchmarkDataManager @@ -97,6 +98,19 @@ def get_git_executable() -> Path | None: return None +def get_git_hash(repo_path: Path) -> str: + """Get current git commit hash within the specified repository path.""" + git_path = get_git_executable() + if git_path is None: + return "unknown" + + try: + proc = safe_run([str(git_path), "rev-parse", "HEAD"]) + return proc.stdout.strip() + except (subprocess.SubprocessError, ValueError): + return "unknown" + + def get_asv_executable() -> Path | None: """Get full path to asv executable.""" asv_path = shutil.which("asv") @@ -113,26 +127,13 @@ def get_python_executable() -> Path: return validate_executable(sys.executable) -def get_git_hash() -> str: - """Get current git commit hash.""" - git_path = get_git_executable() - if git_path is None: - return "unknown" - - try: - proc = safe_run([git_path, "rev-parse", "HEAD"], capture_output=True) - return proc.stdout.strip() - except (subprocess.SubprocessError, ValueError): - return "unknown" - - def find_project_root() -> Path: """Find the project root directory (one containing .git).""" current = Path(__file__).resolve() for parent in current.parents: if (parent / ".git").exists(): return parent - return current.parents[1] + return current.parent def ensure_asv_config_in_root(): @@ -194,6 +195,40 @@ def run_asv_command( config_data["pythons"] = [str(get_python_executable())] + config_file_path = os.environ.get("NXBENCH_CONFIG_FILE") + if config_file_path: + try: + with Path(config_file_path).open("r") as yaml_file: + yaml_config = yaml.safe_load(yaml_file) + asv_yaml_config = yaml_config.get("asv_config", {}) + + repo = asv_yaml_config.get("repo") + if repo: + config_data["repo"] = repo + logger.debug(f"Set repo to: {repo}") + + branches = asv_yaml_config.get("branches") + if branches: + config_data["branches"] = branches + logger.debug(f"Set branches to: {branches}") + + req = asv_yaml_config.get("req") + if req: + config_data["req"] = req + logger.debug(f"Set req to: {req}") + + except FileNotFoundError: + raise click.ClickException( + f"Configuration file not found: {config_file_path}" + ) + except yaml.YAMLError as e: + raise click.ClickException(f"Error parsing YAML config: {e}") + else: + logger.warning( + "NXBENCH_CONFIG_FILE environment variable not set. Using default ASV " + "config." + ) + with tempfile.TemporaryDirectory() as tmpdir: temp_config_path = Path(tmpdir) / "asv.conf.json" try: @@ -354,7 +389,7 @@ def run_benchmark(ctx, backend: tuple[str], collection: str): _has_git = has_git(project_root) if _has_git: try: - git_hash = get_git_hash() + git_hash = get_git_hash(project_root) cmd_args.append(f"--set-commit-hash={git_hash}") except subprocess.CalledProcessError: logger.exception("Failed to get git hash") diff --git a/nxbench/configs/asv.conf.json b/nxbench/configs/asv.conf.json index 02d1604..55d215c 100644 --- a/nxbench/configs/asv.conf.json +++ b/nxbench/configs/asv.conf.json @@ -3,17 +3,8 @@ "project": "nxbench", "timeout": 3000, "project_url": "https://github.com/dpys/nxbench", - "repo": "https://github.com/dpys/nxbench.git", - "branches": [ - "cli-from-anywhere" - ], "environment_type": "virtualenv", "show_commit_url": "https://github.com/dpys/nxbench/commit/", - "req": [ - "networkx==3.4.2", - "nx_parallel==0.3", - "graphblas_algorithms==2023.10.0" - ], "matrix": {}, "benchmark_dir": "nxbench/benchmarks", "env_dir": "env", diff --git a/nxbench/configs/example.yaml b/nxbench/configs/example.yaml index fa01986..c3de8d0 100644 --- a/nxbench/configs/example.yaml +++ b/nxbench/configs/example.yaml @@ -219,3 +219,12 @@ matrix: - "1" - "4" - "8" + +asv_config: + repo: "https://github.com/dpys/nxbench.git" + branches: + - "main" + req: + - "networkx==3.4.2" + - "nx_parallel==0.3" + - "graphblas_algorithms==2023.10.0" From ae122ef0cfe32fa77474e59e9c5582ff3a9c01f4 Mon Sep 17 00:00:00 2001 From: dpys Date: Sun, 8 Dec 2024 03:28:20 -0500 Subject: [PATCH 8/9] fix: asv from anywhere --- nxbench/cli.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/nxbench/cli.py b/nxbench/cli.py index 99b9ff1..2052ea6 100644 --- a/nxbench/cli.py +++ b/nxbench/cli.py @@ -105,7 +105,13 @@ def get_git_hash(repo_path: Path) -> str: return "unknown" try: - proc = safe_run([str(git_path), "rev-parse", "HEAD"]) + proc = subprocess.run( # noqa: S603 + [str(git_path), "rev-parse", "HEAD"], + cwd=str(repo_path), + capture_output=True, + text=True, + check=True, + ) return proc.stdout.strip() except (subprocess.SubprocessError, ValueError): return "unknown" From 6ab1f2ef900a44f0414a7447895cc70f0a212591 Mon Sep 17 00:00:00 2001 From: dpys Date: Sun, 8 Dec 2024 03:40:06 -0500 Subject: [PATCH 9/9] fix: asv from anywhere --- nxbench/cli.py | 78 +++++++++++------------------------ nxbench/configs/asv.conf.json | 1 + 2 files changed, 24 insertions(+), 55 deletions(-) diff --git a/nxbench/cli.py b/nxbench/cli.py index 2052ea6..bffcd19 100644 --- a/nxbench/cli.py +++ b/nxbench/cli.py @@ -11,7 +11,6 @@ import click import pandas as pd -import yaml from nxbench.benchmarks.config import DatasetConfig from nxbench.data.loader import BenchmarkDataManager @@ -157,7 +156,7 @@ def has_git(project_root): def run_asv_command( - args: Sequence[str], check: bool = True + args: Sequence[str], check: bool = True, use_commit_hash: bool = True ) -> subprocess.CompletedProcess: """Run ASV command with dynamic asv.conf.json based on DVCS presence.""" asv_path = get_asv_executable() @@ -180,7 +179,7 @@ def run_asv_command( "No .git directory found. Modifying asv.conf.json for remote repo and " "virtualenv." ) - config_data["repo"] = "https://github.com/dpys/nxbench.git" + config_data["repo"] = str(project_root.resolve()) config_data["environment_type"] = "virtualenv" else: logger.debug("Found .git directory. Using existing repository settings.") @@ -201,48 +200,11 @@ def run_asv_command( config_data["pythons"] = [str(get_python_executable())] - config_file_path = os.environ.get("NXBENCH_CONFIG_FILE") - if config_file_path: - try: - with Path(config_file_path).open("r") as yaml_file: - yaml_config = yaml.safe_load(yaml_file) - asv_yaml_config = yaml_config.get("asv_config", {}) - - repo = asv_yaml_config.get("repo") - if repo: - config_data["repo"] = repo - logger.debug(f"Set repo to: {repo}") - - branches = asv_yaml_config.get("branches") - if branches: - config_data["branches"] = branches - logger.debug(f"Set branches to: {branches}") - - req = asv_yaml_config.get("req") - if req: - config_data["req"] = req - logger.debug(f"Set req to: {req}") - - except FileNotFoundError: - raise click.ClickException( - f"Configuration file not found: {config_file_path}" - ) - except yaml.YAMLError as e: - raise click.ClickException(f"Error parsing YAML config: {e}") - else: - logger.warning( - "NXBENCH_CONFIG_FILE environment variable not set. Using default ASV " - "config." - ) - with tempfile.TemporaryDirectory() as tmpdir: temp_config_path = Path(tmpdir) / "asv.conf.json" - try: - with temp_config_path.open("w") as f: - json.dump(config_data, f, indent=4) - logger.debug(f"Temporary asv.conf.json created at: {temp_config_path}") - except Exception as e: - raise click.ClickException(f"Failed to write temporary asv.conf.json: {e}") + with temp_config_path.open("w") as f: + json.dump(config_data, f, indent=4) + logger.debug(f"Temporary asv.conf.json created at: {temp_config_path}") safe_args = [] for arg in args: @@ -256,6 +218,17 @@ def run_asv_command( safe_args = ["--config", str(temp_config_path), *safe_args] logger.debug(f"Added --config {temp_config_path} to ASV arguments.") + if use_commit_hash and _has_git: + try: + git_hash = get_git_hash(project_root) + if git_hash != "unknown": + safe_args.append(f"--set-commit-hash={git_hash}") + logger.debug(f"Set commit hash to: {git_hash}") + except subprocess.CalledProcessError: + logger.warning( + "Could not determine git commit hash. Proceeding without it." + ) + old_cwd = Path.cwd() if _has_git: os.chdir(project_root) @@ -382,8 +355,13 @@ def benchmark(ctx): help="Backends to benchmark. Specify multiple values to run for multiple backends.", ) @click.option("--collection", type=str, default="all", help="Graph collection to use.") +@click.option( + "--use-commit-hash/--no-commit-hash", + default=False, + help="Whether to use git commit hash for benchmarking.", +) @click.pass_context -def run_benchmark(ctx, backend: tuple[str], collection: str): +def run_benchmark(ctx, backend: tuple[str], collection: str, use_commit_hash: bool): """Run benchmarks.""" config = ctx.obj.get("CONFIG") if config: @@ -391,16 +369,6 @@ def run_benchmark(ctx, backend: tuple[str], collection: str): cmd_args = ["run", "--quick"] - project_root = find_project_root() - _has_git = has_git(project_root) - if _has_git: - try: - git_hash = get_git_hash(project_root) - cmd_args.append(f"--set-commit-hash={git_hash}") - except subprocess.CalledProcessError: - logger.exception("Failed to get git hash") - raise click.ClickException("Could not determine git commit hash") - if package_config.verbosity_level >= 1: cmd_args.append("--verbose") @@ -418,7 +386,7 @@ def run_benchmark(ctx, backend: tuple[str], collection: str): cmd_args.append("--python=same") try: - run_asv_command(cmd_args) + run_asv_command(cmd_args, use_commit_hash=use_commit_hash) except subprocess.CalledProcessError: logger.exception("Benchmark run failed") raise click.ClickException("Benchmark run failed") diff --git a/nxbench/configs/asv.conf.json b/nxbench/configs/asv.conf.json index 55d215c..064384c 100644 --- a/nxbench/configs/asv.conf.json +++ b/nxbench/configs/asv.conf.json @@ -3,6 +3,7 @@ "project": "nxbench", "timeout": 3000, "project_url": "https://github.com/dpys/nxbench", + "repo": ".", "environment_type": "virtualenv", "show_commit_url": "https://github.com/dpys/nxbench/commit/", "matrix": {},