Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
10 changes: 10 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,16 @@
- `fzd` docstring corrected: `analysis_dir` defaults to `"analysis"` (the CLI uses
`results_fzd`).

### fzd CLI execution fix

- The `fzd` command and the `fz design` subcommand were unusable: both passed
`results_dir=` and the algorithm options as `**kwargs` to core `fzd()`, which accepts
`analysis_dir` and `algorithm_options` instead — so every invocation failed immediately
with `TypeError: fzd() got an unexpected keyword argument 'results_dir'`. Both call
sites now map to the correct parameters. Added regression tests that exercise the `fzd`
CLI and the `fz design` subcommand (only the Python API was covered before, which is why
this shipped).

### CLI hardening for scripting and AI agents

- Log messages (`FZ_LOG_LEVEL`) and progress output now go to **stderr** instead of
Expand Down
8 changes: 4 additions & 4 deletions fz/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -672,9 +672,9 @@ def fzd_main():
model,
args.output_expression,
args.algorithm,
results_dir=args.results_dir,
calculators=calculators,
**(algo_options if isinstance(algo_options, dict) else {})
algorithm_options=(algo_options if isinstance(algo_options, dict) else {}),
analysis_dir=args.results_dir,
)

# Print summary
Expand Down Expand Up @@ -853,9 +853,9 @@ def main():
model,
args.output_expression,
args.algorithm,
results_dir=args.results_dir,
calculators=calculators,
**algo_options
algorithm_options=algo_options,
analysis_dir=args.results_dir,
Comment on lines 856 to +858
)

# Print summary
Expand Down
56 changes: 56 additions & 0 deletions tests/test_fzd.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
Tests for fzd (iterative design of experiments with algorithms)
"""

import json
import os
import subprocess
import sys
import tempfile
import shutil
Expand Down Expand Up @@ -148,6 +150,60 @@ def test_fzd_randomsampling(self, simple_model):
assert "result" in result["XY"].columns # output_expression as column name
assert algo_path in result["algorithm"] # algorithm field contains the path

def test_fzd_cli_runs(self, simple_model, monkeypatch):
"""Regression: the `fzd` CLI must actually run a design.

fzd_main() called core fzd() with results_dir=/**options, but fzd()
accepts analysis_dir/algorithm_options — so every invocation raised
TypeError ('unexpected keyword argument results_dir'). No fzd CLI
execution test existed, so the break shipped. This covers entry point 1
(the standalone `fzd` command, fz.cli:fzd_main).
Comment on lines +156 to +160
"""
if shutil.which("bc") is None:
pytest.skip("bc command not available")

input_dir, model = simple_model
algo_path = str(Path(__file__).parent.parent / "examples" / "algorithms" / "randomsampling.py")
analysis_dir = Path(input_dir).parent / "fzd_cli_out"

from fz import cli
monkeypatch.setattr(sys, "argv", [
"fzd",
"-i", str(input_dir),
"-m", json.dumps(model),
"-v", json.dumps({"x": "[0;1]", "y": "[0;1]"}),
"-e", "result",
"-a", algo_path,
"-o", json.dumps({"nvalues": 3, "seed": 42}), # exercises --options
"-r", str(analysis_dir), # exercises --results_dir
])
rc = cli.fzd_main()
assert rc == 0

def test_fz_design_cli_runs(self, simple_model):
"""Regression: the `fz design` subcommand must run too (entry point 2).

Same bug as test_fzd_cli_runs, separate call site in fz.cli:main.
"""
if shutil.which("bc") is None:
pytest.skip("bc command not available")

input_dir, model = simple_model
algo_path = str(Path(__file__).parent.parent / "examples" / "algorithms" / "randomsampling.py")
analysis_dir = Path(input_dir).parent / "design_cli_out"

result = subprocess.run([
sys.executable, "-m", "fz.cli", "design",
"-i", str(input_dir),
"-m", json.dumps(model),
"-v", json.dumps({"x": "[0;1]", "y": "[0;1]"}),
"-e", "result",
"-a", algo_path,
"-o", json.dumps({"nvalues": 3, "seed": 42}),
"-r", str(analysis_dir),
], capture_output=True, text=True)
assert result.returncode == 0, f"stdout:\n{result.stdout}\nstderr:\n{result.stderr}"

# Removed test_fzd_requires_pandas - pandas is now a required dependency

def test_fzd_returns_dataframe(self, simple_model):
Expand Down
Loading