Skip to content

Commit f4393bb

Browse files
holmboeclaude
andcommitted
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 <noreply@anthropic.com>
1 parent 3391d30 commit f4393bb

3 files changed

Lines changed: 50 additions & 91 deletions

File tree

tests/test_cli.py

Lines changed: 14 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
"""Tests for the CLI application."""
22

3-
import tempfile
4-
from pathlib import Path
5-
63
from typer.testing import CliRunner
74
from weav import __version__
85
from weav.cli import app
@@ -17,32 +14,22 @@ def test_version_command():
1714
assert __version__ in result.stdout
1815

1916

20-
def test_render_with_keyval():
17+
def test_render_with_keyval(tmp_path):
2118
"""Test the render command with keyval."""
22-
with tempfile.NamedTemporaryFile(mode="w", suffix=".j2", delete=False) as f:
23-
f.write("Hello {{ name }}!")
24-
f.flush()
25-
result = runner.invoke(app, [f.name, "--keyval", "name=World"])
26-
Path(f.name).unlink()
27-
19+
template = tmp_path / "test.j2"
20+
template.write_text("Hello {{ name }}!")
21+
result = runner.invoke(app, [str(template), "--keyval", "name=World"])
2822
assert result.exit_code == 0
2923
assert "Hello World!" in result.stdout
3024

3125

32-
def test_render_with_yaml_data():
26+
def test_render_with_yaml_data(tmp_path):
3327
"""Test the render command with YAML data file."""
34-
with tempfile.NamedTemporaryFile(mode="w", suffix=".j2", delete=False) as template_file:
35-
template_file.write("Items: {% for item in items %}{{ item }} {% endfor %}")
36-
template_file.flush()
37-
38-
with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as data_file:
39-
data_file.write("items:\n - apple\n - banana\n")
40-
data_file.flush()
41-
42-
result = runner.invoke(app, [template_file.name, "--data", data_file.name])
43-
Path(data_file.name).unlink()
44-
Path(template_file.name).unlink()
45-
28+
template = tmp_path / "test.j2"
29+
template.write_text("Items: {% for item in items %}{{ item }} {% endfor %}")
30+
data_file = tmp_path / "data.yaml"
31+
data_file.write_text("items:\n - apple\n - banana\n")
32+
result = runner.invoke(app, [str(template), "--data", str(data_file)])
4633
assert result.exit_code == 0
4734
assert "apple" in result.stdout
4835
assert "banana" in result.stdout
@@ -55,12 +42,9 @@ def test_render_template_not_found():
5542
assert "Error" in result.stdout or "Error" in result.stderr
5643

5744

58-
def test_render_data_file_not_found():
45+
def test_render_data_file_not_found(tmp_path):
5946
"""Test the render command with non-existent data file."""
60-
with tempfile.NamedTemporaryFile(mode="w", suffix=".j2", delete=False) as f:
61-
f.write("Hello {{ name }}!")
62-
f.flush()
63-
result = runner.invoke(app, [f.name, "--data", "nonexistent.yaml"])
64-
Path(f.name).unlink()
65-
47+
template = tmp_path / "test.j2"
48+
template.write_text("Hello {{ name }}!")
49+
result = runner.invoke(app, [str(template), "--data", "nonexistent.yaml"])
6650
assert result.exit_code == 1

tests/test_template.py

Lines changed: 27 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,15 @@
11
"""Tests for the template module."""
22

3-
import tempfile
4-
from pathlib import Path
5-
63
import pytest
74
from weav.template import TemplateError, compile_template, find_template
85

96

10-
def test_find_template_direct_path():
7+
def test_find_template_direct_path(tmp_path):
118
"""Test finding template by direct file path."""
12-
with tempfile.NamedTemporaryFile(mode="w", suffix=".j2", delete=False) as f:
13-
f.write("Hello {{ name }}")
14-
f.flush()
15-
_loader, name = find_template(f.name)
16-
Path(f.name).unlink()
17-
18-
assert name == Path(f.name).name
9+
template = tmp_path / "test.j2"
10+
template.write_text("Hello {{ name }}")
11+
_loader, name = find_template(str(template))
12+
assert name == template.name
1913

2014

2115
def test_find_template_not_found():
@@ -24,65 +18,41 @@ def test_find_template_not_found():
2418
find_template("nonexistent_template.j2")
2519

2620

27-
def test_compile_template_basic():
21+
def test_compile_template_basic(tmp_path):
2822
"""Test basic template compilation."""
29-
with tempfile.NamedTemporaryFile(mode="w", suffix=".j2", delete=False) as f:
30-
f.write("Hello {{ name }}!")
31-
f.flush()
32-
result = compile_template(f.name, [], ["name=World"])
33-
Path(f.name).unlink()
34-
23+
template = tmp_path / "test.j2"
24+
template.write_text("Hello {{ name }}!")
25+
result = compile_template(str(template), [], ["name=World"])
3526
assert result == "Hello World!"
3627

3728

38-
def test_compile_template_with_yaml():
29+
def test_compile_template_with_yaml(tmp_path):
3930
"""Test template compilation with YAML data."""
40-
with tempfile.NamedTemporaryFile(mode="w", suffix=".j2", delete=False) as template_file:
41-
template_file.write("{{ greeting }}, {{ name }}!")
42-
template_file.flush()
43-
44-
with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as data_file:
45-
data_file.write("greeting: Hello\nname: World\n")
46-
data_file.flush()
47-
48-
result = compile_template(template_file.name, [data_file.name], [])
49-
Path(data_file.name).unlink()
50-
Path(template_file.name).unlink()
51-
31+
template = tmp_path / "test.j2"
32+
template.write_text("{{ greeting }}, {{ name }}!")
33+
data_file = tmp_path / "data.yaml"
34+
data_file.write_text("greeting: Hello\nname: World\n")
35+
result = compile_template(str(template), [str(data_file)], [])
5236
assert result == "Hello, World!"
5337

5438

55-
def test_compile_template_keyval_overrides_yaml():
39+
def test_compile_template_keyval_overrides_yaml(tmp_path):
5640
"""Test that keyval overrides YAML data."""
57-
with tempfile.NamedTemporaryFile(mode="w", suffix=".j2", delete=False) as template_file:
58-
template_file.write("{{ name }}")
59-
template_file.flush()
60-
61-
with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as data_file:
62-
data_file.write("name: FromYAML\n")
63-
data_file.flush()
64-
65-
result = compile_template(template_file.name, [data_file.name], ["name=FromKeyval"])
66-
Path(data_file.name).unlink()
67-
Path(template_file.name).unlink()
68-
41+
template = tmp_path / "test.j2"
42+
template.write_text("{{ name }}")
43+
data_file = tmp_path / "data.yaml"
44+
data_file.write_text("name: FromYAML\n")
45+
result = compile_template(str(template), [str(data_file)], ["name=FromKeyval"])
6946
assert result == "FromKeyval"
7047

7148

72-
def test_compile_template_wrapped_list():
49+
def test_compile_template_wrapped_list(tmp_path):
7350
"""Test template compilation with wrapped list data."""
74-
with tempfile.NamedTemporaryFile(mode="w", suffix=".j2", delete=False) as template_file:
75-
template_file.write("{% for item in items %}{{ item }} {% endfor %}")
76-
template_file.flush()
77-
78-
with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as data_file:
79-
data_file.write("- apple\n- banana\n- cherry\n")
80-
data_file.flush()
81-
82-
result = compile_template(template_file.name, [f"items={data_file.name}"], [])
83-
Path(data_file.name).unlink()
84-
Path(template_file.name).unlink()
85-
51+
template = tmp_path / "test.j2"
52+
template.write_text("{% for item in items %}{{ item }} {% endfor %}")
53+
data_file = tmp_path / "data.yaml"
54+
data_file.write_text("- apple\n- banana\n- cherry\n")
55+
result = compile_template(str(template), [f"items={data_file}"], [])
8656
assert "apple" in result
8757
assert "banana" in result
8858
assert "cherry" in result

weav/template.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -147,10 +147,15 @@ def compile_template(
147147
data_specs = parse_data_args(data_files)
148148

149149
for file_obj, wrapper_key in data_specs:
150-
loaded_file = load_and_wrap(yaml.load, file_obj, wrapper_key)
151-
merged_data = deep_merge(merged_data, loaded_file)
152-
if verbose:
153-
print(f"Loaded {file_obj.name} with keys: {list(loaded_file.keys())}", file=sys.stderr)
150+
try:
151+
loaded_file = load_and_wrap(yaml.load, file_obj, wrapper_key)
152+
merged_data = deep_merge(merged_data, loaded_file)
153+
if verbose:
154+
keys = list(loaded_file.keys())
155+
print(f"Loaded {file_obj.name} with keys: {keys}", file=sys.stderr)
156+
finally:
157+
if file_obj is not sys.stdin:
158+
file_obj.close()
154159

155160
# Merge keyval parameters into merged_data (keyval takes precedence)
156161
merged_data.update(parameters)

0 commit comments

Comments
 (0)