From fa1db96ffca0d333107b32d8d354a1dcb726337d Mon Sep 17 00:00:00 2001 From: Henrik Holmboe Date: Mon, 30 Mar 2026 12:25:59 +0200 Subject: [PATCH] Fix Windows CI failures from unclosed file handles Tests used NamedTemporaryFile and called unlink() while the file was still open, which fails on Windows. Switch to pytest tmp_path fixture. Also close data file handles in compile_template() after YAML loading, as parse_data_args() opens files that were never closed. Co-Authored-By: Claude Opus 4.6 --- tests/test_cli.py | 44 +++++++--------------- tests/test_template.py | 84 ++++++++++++++---------------------------- weav/template.py | 13 +++++-- 3 files changed, 50 insertions(+), 91 deletions(-) diff --git a/tests/test_cli.py b/tests/test_cli.py index 9dd8d37..0d887f1 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -1,8 +1,5 @@ """Tests for the CLI application.""" -import tempfile -from pathlib import Path - from typer.testing import CliRunner from weav import __version__ from weav.cli import app @@ -17,32 +14,22 @@ def test_version_command(): assert __version__ in result.stdout -def test_render_with_keyval(): +def test_render_with_keyval(tmp_path): """Test the render command with keyval.""" - with tempfile.NamedTemporaryFile(mode="w", suffix=".j2", delete=False) as f: - f.write("Hello {{ name }}!") - f.flush() - result = runner.invoke(app, [f.name, "--keyval", "name=World"]) - Path(f.name).unlink() - + template = tmp_path / "test.j2" + template.write_text("Hello {{ name }}!") + result = runner.invoke(app, [str(template), "--keyval", "name=World"]) assert result.exit_code == 0 assert "Hello World!" in result.stdout -def test_render_with_yaml_data(): +def test_render_with_yaml_data(tmp_path): """Test the render command with YAML data file.""" - with tempfile.NamedTemporaryFile(mode="w", suffix=".j2", delete=False) as template_file: - template_file.write("Items: {% for item in items %}{{ item }} {% endfor %}") - template_file.flush() - - with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as data_file: - data_file.write("items:\n - apple\n - banana\n") - data_file.flush() - - result = runner.invoke(app, [template_file.name, "--data", data_file.name]) - Path(data_file.name).unlink() - Path(template_file.name).unlink() - + template = tmp_path / "test.j2" + template.write_text("Items: {% for item in items %}{{ item }} {% endfor %}") + data_file = tmp_path / "data.yaml" + data_file.write_text("items:\n - apple\n - banana\n") + result = runner.invoke(app, [str(template), "--data", str(data_file)]) assert result.exit_code == 0 assert "apple" in result.stdout assert "banana" in result.stdout @@ -55,12 +42,9 @@ def test_render_template_not_found(): assert "Error" in result.stdout or "Error" in result.stderr -def test_render_data_file_not_found(): +def test_render_data_file_not_found(tmp_path): """Test the render command with non-existent data file.""" - with tempfile.NamedTemporaryFile(mode="w", suffix=".j2", delete=False) as f: - f.write("Hello {{ name }}!") - f.flush() - result = runner.invoke(app, [f.name, "--data", "nonexistent.yaml"]) - Path(f.name).unlink() - + template = tmp_path / "test.j2" + template.write_text("Hello {{ name }}!") + result = runner.invoke(app, [str(template), "--data", "nonexistent.yaml"]) assert result.exit_code == 1 diff --git a/tests/test_template.py b/tests/test_template.py index f7dda6f..1a1dd4b 100644 --- a/tests/test_template.py +++ b/tests/test_template.py @@ -1,21 +1,15 @@ """Tests for the template module.""" -import tempfile -from pathlib import Path - import pytest from weav.template import TemplateError, compile_template, find_template -def test_find_template_direct_path(): +def test_find_template_direct_path(tmp_path): """Test finding template by direct file path.""" - with tempfile.NamedTemporaryFile(mode="w", suffix=".j2", delete=False) as f: - f.write("Hello {{ name }}") - f.flush() - _loader, name = find_template(f.name) - Path(f.name).unlink() - - assert name == Path(f.name).name + template = tmp_path / "test.j2" + template.write_text("Hello {{ name }}") + _loader, name = find_template(str(template)) + assert name == template.name def test_find_template_not_found(): @@ -24,65 +18,41 @@ def test_find_template_not_found(): find_template("nonexistent_template.j2") -def test_compile_template_basic(): +def test_compile_template_basic(tmp_path): """Test basic template compilation.""" - with tempfile.NamedTemporaryFile(mode="w", suffix=".j2", delete=False) as f: - f.write("Hello {{ name }}!") - f.flush() - result = compile_template(f.name, [], ["name=World"]) - Path(f.name).unlink() - + template = tmp_path / "test.j2" + template.write_text("Hello {{ name }}!") + result = compile_template(str(template), [], ["name=World"]) assert result == "Hello World!" -def test_compile_template_with_yaml(): +def test_compile_template_with_yaml(tmp_path): """Test template compilation with YAML data.""" - with tempfile.NamedTemporaryFile(mode="w", suffix=".j2", delete=False) as template_file: - template_file.write("{{ greeting }}, {{ name }}!") - template_file.flush() - - with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as data_file: - data_file.write("greeting: Hello\nname: World\n") - data_file.flush() - - result = compile_template(template_file.name, [data_file.name], []) - Path(data_file.name).unlink() - Path(template_file.name).unlink() - + template = tmp_path / "test.j2" + template.write_text("{{ greeting }}, {{ name }}!") + data_file = tmp_path / "data.yaml" + data_file.write_text("greeting: Hello\nname: World\n") + result = compile_template(str(template), [str(data_file)], []) assert result == "Hello, World!" -def test_compile_template_keyval_overrides_yaml(): +def test_compile_template_keyval_overrides_yaml(tmp_path): """Test that keyval overrides YAML data.""" - with tempfile.NamedTemporaryFile(mode="w", suffix=".j2", delete=False) as template_file: - template_file.write("{{ name }}") - template_file.flush() - - with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as data_file: - data_file.write("name: FromYAML\n") - data_file.flush() - - result = compile_template(template_file.name, [data_file.name], ["name=FromKeyval"]) - Path(data_file.name).unlink() - Path(template_file.name).unlink() - + template = tmp_path / "test.j2" + template.write_text("{{ name }}") + data_file = tmp_path / "data.yaml" + data_file.write_text("name: FromYAML\n") + result = compile_template(str(template), [str(data_file)], ["name=FromKeyval"]) assert result == "FromKeyval" -def test_compile_template_wrapped_list(): +def test_compile_template_wrapped_list(tmp_path): """Test template compilation with wrapped list data.""" - with tempfile.NamedTemporaryFile(mode="w", suffix=".j2", delete=False) as template_file: - template_file.write("{% for item in items %}{{ item }} {% endfor %}") - template_file.flush() - - with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as data_file: - data_file.write("- apple\n- banana\n- cherry\n") - data_file.flush() - - result = compile_template(template_file.name, [f"items={data_file.name}"], []) - Path(data_file.name).unlink() - Path(template_file.name).unlink() - + template = tmp_path / "test.j2" + template.write_text("{% for item in items %}{{ item }} {% endfor %}") + data_file = tmp_path / "data.yaml" + data_file.write_text("- apple\n- banana\n- cherry\n") + result = compile_template(str(template), [f"items={data_file}"], []) assert "apple" in result assert "banana" in result assert "cherry" in result diff --git a/weav/template.py b/weav/template.py index c1f9d41..68d93a5 100644 --- a/weav/template.py +++ b/weav/template.py @@ -147,10 +147,15 @@ def compile_template( data_specs = parse_data_args(data_files) for file_obj, wrapper_key in data_specs: - loaded_file = load_and_wrap(yaml.load, file_obj, wrapper_key) - merged_data = deep_merge(merged_data, loaded_file) - if verbose: - print(f"Loaded {file_obj.name} with keys: {list(loaded_file.keys())}", file=sys.stderr) + try: + loaded_file = load_and_wrap(yaml.load, file_obj, wrapper_key) + merged_data = deep_merge(merged_data, loaded_file) + if verbose: + keys = list(loaded_file.keys()) + print(f"Loaded {file_obj.name} with keys: {keys}", file=sys.stderr) + finally: + if file_obj is not sys.stdin: + file_obj.close() # Merge keyval parameters into merged_data (keyval takes precedence) merged_data.update(parameters)